Aller au contenu

Mongoose : CRUD et requêtes

Exemples avec les modèles Client et Commande (voir Schémas et modèles). Mongoose utilise des promesses : toutes les opérations s’écrivent avec await (les fonctions de rappel ne sont plus prises en charge).

const alice = await Client.create({
nom: 'Alice',
email: 'alice@exemple.com',
age: 30,
});
console.log(alice._id, alice.createdAt);
const lot = await Client.insertMany([
{ nom: 'Bob', email: 'bob@exemple.com' },
{ nom: 'Chloé', email: 'chloe@exemple.com' },
]);
const dan = new Client({ nom: 'Dan', email: 'dan@exemple.com' });
await dan.save();

create et save appliquent la validation et les hooks ; insertMany valide aussi, mais n’exécute pas les hooks save.

const tous = await Client.find();
const actifs = await Client.find({ actif: true });
const unique = await Client.findOne({ email: 'alice@exemple.com' });
const parId = await Client.findById('65f1c0a1b2c3d4e5f6a7b8c9');
const existe = await Client.exists({ email: 'alice@exemple.com' }); // { _id } ou null
const nombre = await Client.countDocuments({ actif: true });
const estimation = await Client.estimatedDocumentCount(); // rapide, sans filtre

findOne et findById renvoient null si rien ne correspond : toujours tester le résultat.

await Client.find({ age: { $gte: 18, $lt: 65 } });
await Client.find({ nom: { $in: ['Alice', 'Bob'] } });
await Client.find({ nom: /^a/i });
await Client.find({ $or: [{ actif: false }, { age: { $lt: 18 } }] });
await Client.find({ tags: 'promo' }); // tableau contenant « promo »
await Client.find({ 'adresse.ville': 'Lyon' });
const page = await Client.find({ actif: true })
.select('nom email') // ou .select({ nom: 1, email: 1 }) ; .select('-tags') pour exclure
.sort({ nom: 1 }) // 1 croissant, -1 décroissant
.skip(20)
.limit(10);

Pour une grande collection, paginer par curseur de clé plutôt que skip (qui relit les documents ignorés) :

const suite = await Client.find({ _id: { $gt: dernierId } })
.sort({ _id: 1 })
.limit(20);
const liste = await Client.find().lean();

lean() renvoie des objets JavaScript simples (sans méthodes, virtuels ni suivi de modifications) : bien plus rapide et léger pour des lectures destinées à être envoyées en JSON.

Document Mongoose lean()
Méthodes, virtuels, save() Oui Non
Mémoire et vitesse Plus lourd Léger
Usage Modifier puis sauvegarder Lecture seule

Charger puis enregistrer (validation et hooks complets)

Section intitulée « Charger puis enregistrer (validation et hooks complets) »
const client = await Client.findById(id);
if (!client) throw new Error('client introuvable');
client.nom = 'Alice Martin';
client.tags.push('fidèle');
await client.save();
await Client.updateOne(
{ email: 'alice@exemple.com' },
{ $set: { actif: false } },
);
await Client.updateMany({ actif: false }, { $set: { tags: [] } });
const maj = await Client.findByIdAndUpdate(
id,
{ $set: { nom: 'Alice M.' } },
{ returnDocument: 'after', runValidators: true },
);
Option Effet
returnDocument: 'after' (ou new: true) Renvoie le document modifié (sinon l’ancien)
runValidators: true Valide la mise à jour : non fait par défaut
upsert: true Crée le document s’il n’existe pas

Opérateurs de mise à jour courants :

Opérateur Effet
$set, $unset Fixer, supprimer un champ
$inc Incrémenter
$push, $addToSet Ajouter à un tableau ($addToSet sans doublon)
$pull Retirer d’un tableau
$currentDate Date courante
await Commande.updateOne(
{ _id },
{
$inc: { totalCentimes: 500 },
$push: { lignes: { article: 'A12', quantite: 1, prixCentimes: 500 } },
},
);
await Client.deleteOne({ email: 'dan@exemple.com' });
await Client.deleteMany({ actif: false });
const supprime = await Client.findByIdAndDelete(id); // renvoie le document supprimé
// Dangereux : req.body.email peut être { "$ne": null }
const utilisateur = await Client.findOne({ email: req.body.email });
// Sûr : forcer une chaîne
const utilisateur = await Client.findOne({ email: String(req.body.email) });

Un filtre reçu du client ({"email":{"$ne":null}}) peut contourner une vérification. Contrôler les types, valider avec une bibliothèque de schémas et activer sanitizeFilter :

mongoose.set('sanitizeFilter', true);

Les transactions exigent un ensemble de réplicas (replica set), pas un serveur MongoDB autonome :

const session = await mongoose.startSession();
try {
await session.withTransaction(async () => {
await Commande.create([{ numero: 'C-1', totalCentimes: 1000 }], {
session,
});
await Client.updateOne({ _id }, { $inc: { achats: 1 } }, { session });
});
} finally {
await session.endSession();
}
for await (const client of Client.find({ actif: true }).cursor()) {
await traiter(client);
}

Le curseur lit les documents un par un, sans tout charger en mémoire.

Symptôme Piste
findOne renvoie null Filtre incorrect, mauvaise base : mongosh pour vérifier
Une mise à jour ne valide rien runValidators: true
CastError: Cast to ObjectId failed for value … Identifiant invalide : tester avec mongoose.isValidObjectId(id)
E11000 duplicate key error Violation d’un index unique : traiter l’erreur (err.code === 11000)
Les méthodes du modèle sont absentes Résultat de lean()
La mise à jour d’un tableau ne persiste pas Modification d’un champ Mixed sans markModified, ou document non sauvegardé
Transaction numbers are only allowed on a replica set member or mongos Pas de replica set : transactions indisponibles

Ubuntu 24.04 et 26.04 : différences à connaître

Section intitulée « Ubuntu 24.04 et 26.04 : différences à connaître »

Aucune propre à Ubuntu.