Mongoose: Puis-je créer un index unique pour le sous-document ?

Créé le 4 août 2014  ·  16Commentaires  ·  Source: Automattic/mongoose

Est-il possible de créer un index unique pour le sous-document ? Je veux dire, il peut se dupliquer dans un autre sous-document, mais pas le même parent,

Commentaire le plus utile

J'ai trouvé autre chose qui semble fonctionner, même dans les mises à jour simultanées.

Supposons que subdocs.name est la valeur que nous voulons unique :

Model.update({
  _id: '576328595b2880f831413b92',
  'subdocs.name': {
    $ne: 'Unique Name'
  }
}, {
  $push: {
    subdocs: {
      name: 'Unique Name'
    }
  }
}).then((raw) => {
  // check raw.nModified value
}).catch(next);

Tous les 16 commentaires

Une combinaison d'un index sur le sous-document et de l'opérateur $addToSet devrait vous donner ce que vous recherchez. Utilisez simplement $addToSet chaque fois que vous ajoutez au tableau et vous devriez éviter les doublons dans le document parent.

$addToSet fonctionne, mais il détecte également d'autres sous-documents dans un parent différent, corrigez-moi si je me trompe, car je viens de le tester

Je suppose que je ne comprends pas ce que vous entendez par sous-document dans un parent différent. Pouvez-vous fournir un exemple de code qui montre ce que vous essayez de faire ?

Désolé pour la bosse mais en cherchant une solution, j'ai pensé que cela pourrait aider à l'avenir:

const schema = new Schema({
  value: String,
  another: Number,
  subdocs: [{
    prop: String, /* Should be unique */
    value: Number
  }]
});

/**
 * Check for duplicated sub document properties.
 */
schema.pre('validate', function validate(next) {
  var unique = [];

  for (var i = 0, l = this.subdocs.length; i < l; i++) {
    let prop = this.subdocs[i].prop;

    if (unique.indexOf(prop) > -1) {
      return next(new Error('Duplicated sub document!'));
    }

    unique.push(prop);
  }

  next();
});

Explication:

Supposons que nous ayons un schéma avec une propriété subdocs qui contient un ensemble de subdoc avec une propriété appelée prop qui doit être unique. L'idée est de garder une trace des propriétés dupliquées dans un tableau appelé unique et de vérifier si la suivante est déjà dans le tableau unique .

Je sais que ce n'est pas super efficace mais ça marche.

Bien sûr, cela fonctionne la plupart du temps, mais gardez à l'esprit que lorsque vous effectuez un .push() sur un tableau, puis .save() , cela devient un $push vers mongodb, vous pouvez donc ont 2 documents pensent qu'ils enregistrent un sous-doc unique parce qu'ils ne savent pas qu'ils sont .push() -ing le même.

@vkarpov15 Le push est effectué vers le tableau unique qui ne contient que les valeurs que vous souhaitez rendre uniques afin que vous puissiez vérifier si de nouvelles valeurs sont également uniques. Il est destiné à être utilisé après avoir créé un push ou un addToSet mais avant d'enregistrer le document. Le tableau unique est temporaire et ne fait pas partie du document mongo :)

Nan c'est hors sujet. Supposons que vous essayez d'enregistrer 2 documents à peu près en même temps à partir de différents programmes JS - les deux font un .find() pour le document, poussent sur un sous-document avec prop = 'test' , et essaient d'enregistrer . Les deux penseront que c'est correct d'insérer prop = 'test' parce que leurs copies locales du document n'ont pas encore ce sous-doc, donc les deux le feront avec succès $push dans mongodb. Le problème est que faire .push() sur un tableau de mongoose se traduit par un $push à mongodb , donc il n'y a pas de bon moyen pour nous d'appliquer l'unicité dans un tableau doc ​​pour toutes les opérations de tableau ...

@ vkarpov15 bon point,

Eh bien, votre approche fonctionne tant que vous appelez markModified sur le tableau props _avant_ d'appeler push, alors mongoose écrasera toujours tout le tableau lors de l'enregistrement dans mongodb, vous n'avez donc pas les mêmes conditions de course

J'ai trouvé autre chose qui semble fonctionner, même dans les mises à jour simultanées.

Supposons que subdocs.name est la valeur que nous voulons unique :

Model.update({
  _id: '576328595b2880f831413b92',
  'subdocs.name': {
    $ne: 'Unique Name'
  }
}, {
  $push: {
    subdocs: {
      name: 'Unique Name'
    }
  }
}).then((raw) => {
  // check raw.nModified value
}).catch(next);

Super idée ça devrait bien marcher

??

@stgogm, vous venez de terminer une recherche de 5 heures, et pour cela, je vous en suis reconnaissant.

@vkarpov15 pouvez-vous me donner un exemple de la façon dont $addToSet peut assurer l'unicité du champ de sous-document

j'ai essayé ça

await Course.updateOne({ _id: id }, { $addToSet: { 'units.level': { name: body.name, level: body.level} } }) unitSchema.index({ level: 1 })
Mais unit.level accepte toujours les doublons

@ksuhiyp unitSchema.index({ level: 1 }, { unique: true }) et assurez-vous également de définir _id sur false dans unitSchema , comme :

const unitSchema = new Schema({ name: String, level: Number }, { _id: false })

$addToSet fonctionne avec les sous-documents, mais il recherche une égalité profonde exacte entre les documents, donc si un sous-doc a un _id alors $addToSet n'attrapera jamais de doublon. Voir:

Merci à @vkarpov15 Vous m'avez sauvé.

Cette page vous a été utile?
0 / 5 - 0 notes