Je me demandais s'il y avait moyen de forcer une entrée de collection unique mais seulement si l'entrée n'est pas nulle. e Exemple de schéma:
var UsersSchema = new Schema({
name : {type: String, trim: true, index: true, required: true},
email : {type: String, trim: true, index: true, unique: true}
});
'email' dans ce cas n'est pas requis mais si 'email' est enregistré, je veux m'assurer que cette entrée est unique (au niveau de la base de données).
Les entrées vides semblent obtenir la valeur "null", donc chaque entrée sans e-mail se bloque avec l'option "unique" (s'il existe un utilisateur différent sans e-mail).
En ce moment, je le résous au niveau de l'application, mais j'aimerais enregistrer cette requête db.
tHX
Depuis MongoDB v1.8 +, vous pouvez obtenir le comportement souhaité pour garantir des valeurs uniques mais autoriser plusieurs documents sans le champ en définissant l'option sparse
sur true lors de la définition de l'index. Un péché:
email : {type: String, trim: true, index: true, unique: true, sparse: true}
Ou dans le Shell:
db.users.ensureIndex({email: 1}, {unique: true, sparse: true});
Notez qu'un index unique et clairsemé n'autorise toujours pas plusieurs documents avec un champ email
avec une valeur de null
, seulement plusieurs documents sans un champ email
.
Oui, il est possible d'avoir plusieurs documents avec un champ défini sur null
ou non défini, tout en appliquant des valeurs "réelles" uniques.
exigences :
string
ou object
quand ce n'est pas null
).Si les détails ne vous intéressent pas, n'hésitez pas à passer à la section implementation
.
Pour compléter la réponse de @ Nolan, à partir de MongoDB v3.2, vous pouvez utiliser un index unique partiel avec une expression de filtre.
L'expression de filtre partiel a des limites. Il ne peut comprendre que les éléments suivants:
- expressions d'égalité (c.-à-d. champ: valeur ou en utilisant le
$eq
opérateur),$exists: true
expression,$gt
,$gte
,$lt
,$lte
expressions,$type
expressions,$and
opérateur au niveau supérieur uniquement
Cela signifie que l'expression triviale {"yourField"{$ne: null}}
Ne peut pas être utilisé.
Cependant, en supposant que votre champ utilise toujours le même type , vous pouvez utiliser un $type
expression .
{ field: { $type: <BSON type number> | <String alias> } }
MongoDB v3.6 a ajouté la prise en charge de la spécification de plusieurs types possibles, qui peuvent être passés sous forme de tableau:
{ field: { $type: [ <BSON type1> , <BSON type2>, ... ] } }
ce qui signifie qu'il permet à la valeur d'être de n'importe quel nombre de plusieurs types lorsqu'elle n'est pas null
.
Par conséquent, si nous voulons autoriser le champ email
dans l'exemple ci-dessous à accepter string
ou, disons, binary data
valeurs, un $type
l'expression serait:
{email: {$type: ["string", "binData"]}}
Vous pouvez le spécifier dans un schéma mangouste:
const UsersSchema = new Schema({
name: {type: String, trim: true, index: true, required: true},
email: {
type: String, trim: true, index: {
unique: true,
partialFilterExpression: {email: {$type: "string"}}
}
}
});
ou l'ajouter directement à la collection (qui utilise le pilote node.js natif):
User.collection.createIndex("email", {
unique: true,
partialFilterExpression: {
"email": {
$type: "string"
}
}
});
en utilisant collection.createIndex
db.collection('users').createIndex({
"email": 1
}, {
unique: true,
partialFilterExpression: {
"email": {
$type: "string"
}
}
},
function (err, results) {
// ...
}
);
en utilisant db.collection.createIndex
:
db.users.createIndex({
"email": 1
}, {
unique: true,
partialFilterExpression: {
"email": {$type: "string"}
}
})
Cela permettra d'insérer plusieurs enregistrements avec un e-mail null
, ou sans aucun champ e-mail, mais pas avec la même chaîne d'e-mail.
Juste une mise à jour rapide pour ceux qui recherchent ce sujet.
La réponse sélectionnée fonctionnera, mais vous voudrez peut-être envisager d'utiliser des index partiels à la place.
Modifié dans la version 3.2: à partir de MongoDB 3.2, MongoDB offre la possibilité de créer des index partiels. Les index partiels offrent un sur-ensemble des fonctionnalités des index clairsemés. Si vous utilisez MongoDB 3.2 ou version ultérieure, les index partiels doivent être préférés aux index clairsemés.
Plus de doco sur les index partiels: https://docs.mongodb.com/manual/core/index-partial/
En fait, seul le premier document où "e-mail" en tant que champ n'existe pas sera enregistré avec succès. Les enregistrements ultérieurs dans lesquels "e-mail" n'est pas présent échoueront lors de l'envoi d'une erreur (voir l'extrait de code ci-dessous). Pour la raison, consultez la documentation officielle de MongoDB concernant les index uniques et les clés manquantes ici à http://www.mongodb.org/display/DOCS/Indexes#Indexes-UniqueIndexes .
// NOTE: Code to executed in mongo console.
db.things.ensureIndex({firstname: 1}, {unique: true});
db.things.save({lastname: "Smith"});
// Next operation will fail because of the unique index on firstname.
db.things.save({lastname: "Jones"});
Par définition, un index unique ne peut autoriser qu'une seule valeur à être stockée une seule fois. Si vous considérez null comme une de ces valeurs, il ne peut être inséré qu'une seule fois! Vous avez raison dans votre approche en la garantissant et en la validant au niveau de l'application. Voilà comment cela peut se faire.
Vous pouvez également aimer lire ceci http://www.mongodb.org/display/DOCS/Querying+and+nulls