145 votes

mongoDB/mongoose : unique si non nul

Je me demandais s'il y avait un 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}
});

Dans ce cas, l'adresse électronique n'est pas nécessaire, mais si elle est enregistrée, je veux m'assurer que cette entrée est unique (au niveau de la base de données).

Les entrées vides semblent recevoir la valeur 'null', ce qui fait que toutes les entrées sans email se plantent avec l'option 'unique' (s'il y a un utilisateur différent sans email).

Pour l'instant, je résous le problème au niveau de l'application, mais j'aimerais pouvoir sauvegarder cette requête de base de données.

thx

214voto

JohnnyHK Points 61191

À partir de MongoDB v1.8+, vous pouvez obtenir le comportement souhaité, à savoir garantir des valeurs uniques tout en autorisant plusieurs documents sans le champ, en définissant l'attribut sparse à true lors de la définition de l'index. Comme dans :

email : {type: String, trim: true, index: true, unique: true, sparse: true}

Ou dans la coquille :

db.users.ensureIndex({email: 1}, {unique: true, sparse: true});

Il convient de noter qu'un index unique et clairsemé ne permet toujours pas l'utilisation de plusieurs documents ayant un numéro d'identification unique. email avec un valeur de null , seulement plusieurs docs sans un email champ.

Ver http://docs.mongodb.org/manual/core/index-sparse/

77voto

MasterAM Points 2574

En résumé

Oui, il est possible d'avoir plusieurs documents dont le champ est défini comme suit null ou non définies, tout en imposant des valeurs "réelles" uniques.

exigences :

  • MongoDB v3.2+.
  • Connaître à l'avance le(s) type(s) de valeur concrète (par exemple, toujours une string o object quand ce n'est pas null ).

Si vous n'êtes pas intéressé par les détails, n'hésitez pas à passer à l'étape suivante. implementation section.

version plus longue

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 "filtre partiel" a des limites. Elle ne peut inclure que les éléments suivants :

  • des expressions d'égalité (c'est-à-dire champ : valeur ou utilisation de la fonction $eq opérateur),
  • $exists: true expression,
  • $gt , $gte , $lt , $lte expressions,
  • $type expressions,
  • $and uniquement au niveau supérieur

Cela signifie que l'expression triviale {"yourField"{$ne: null}} ne peut être utilisé.

Cependant, en supposant que votre domaine 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 possibilité de spécifier plusieurs types possibles, qui peuvent être transmis sous forme de tableau :

{ field: { $type: [ <BSON type1> , <BSON type2>, ... ] } }

ce qui signifie qu'il permet à la valeur d'être de n'importe quel type parmi un certain nombre de types multiples lorsqu'elle n'est pas null .

Par conséquent, si nous voulons permettre à la email dans l'exemple ci-dessous pour accepter soit string ou, disons, binary data valeurs, une $type l'expression serait :

{email: {$type: ["string", "binData"]}}

mise en œuvre

mangouste

Vous pouvez le spécifier dans un schéma mongoose :

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"
    }
  }
});

pilote natif mongodb

en utilisant collection.createIndex

db.collection('users').createIndex({
    "email": 1
  }, {
    unique: true,
    partialFilterExpression: {
      "email": {
        $type: "string"
      }
    }
  },
  function (err, results) {
    // ...
  }
);

shell mongodb

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 null email, ou sans champ email du tout, mais pas avec la même chaîne email.

8voto

Nolan Garrido Points 61

Juste une petite mise à jour pour ceux qui font des recherches sur ce sujet.

La réponse sélectionnée fonctionnera, mais vous pourriez 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. option de créer des index partiels. Les index partiels offrent un sur-ensemble de la fonctionnalité des index épars. Si vous utilisez MongoDB 3.2 ou ou une version ultérieure, les index partiels doivent être préférés aux index épars.

Plus de doco sur les index partiels : https://docs.mongodb.com/manual/core/index-partial/

2voto

Samyak Bhuta Points 929

En fait, seul le premier document où le champ "email" n'existe pas sera enregistré avec succès. Les sauvegardes ultérieures où "email" n'est pas présent échoueront en donnant une erreur (voir l'extrait de code ci-dessous). Pour en savoir plus, consultez la documentation officielle de MongoDB concernant les index uniques et les clés manquantes à l'adresse suivante 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, l'index unique ne permet de stocker qu'une seule fois une valeur. Si vous considérez que null est une de ces valeurs, elle ne peut être insérée qu'une seule fois ! Vous avez raison dans votre approche en assurant et en validant au niveau de l'application. C'est ainsi que cela peut être fait.

Vous aimerez peut-être aussi lire ceci http://www.mongodb.org/display/DOCS/Querying+et+nulls

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X