MongoDB: Unique index on array element's property












20















I have a structure similar to this:



class Cat {
int id;
List<Kitten> kittens;
}

class Kitten {
int id;
}


I'd like to prevent users from creating a cat with more than one kitten with the same id. I've tried creating an index as follows:



db.Cats.ensureIndex({'id': 1, 'kittens.id': 1}, {unique:true})


But when I attempt to insert a badly-formatted cat, Mongo accepts it.



Am I missing something? can this even be done?










share|improve this question


















  • 1





    See stackoverflow.com/questions/4435637/…

    – pingw33n
    Jul 19 '11 at 8:19
















20















I have a structure similar to this:



class Cat {
int id;
List<Kitten> kittens;
}

class Kitten {
int id;
}


I'd like to prevent users from creating a cat with more than one kitten with the same id. I've tried creating an index as follows:



db.Cats.ensureIndex({'id': 1, 'kittens.id': 1}, {unique:true})


But when I attempt to insert a badly-formatted cat, Mongo accepts it.



Am I missing something? can this even be done?










share|improve this question


















  • 1





    See stackoverflow.com/questions/4435637/…

    – pingw33n
    Jul 19 '11 at 8:19














20












20








20


3






I have a structure similar to this:



class Cat {
int id;
List<Kitten> kittens;
}

class Kitten {
int id;
}


I'd like to prevent users from creating a cat with more than one kitten with the same id. I've tried creating an index as follows:



db.Cats.ensureIndex({'id': 1, 'kittens.id': 1}, {unique:true})


But when I attempt to insert a badly-formatted cat, Mongo accepts it.



Am I missing something? can this even be done?










share|improve this question














I have a structure similar to this:



class Cat {
int id;
List<Kitten> kittens;
}

class Kitten {
int id;
}


I'd like to prevent users from creating a cat with more than one kitten with the same id. I've tried creating an index as follows:



db.Cats.ensureIndex({'id': 1, 'kittens.id': 1}, {unique:true})


But when I attempt to insert a badly-formatted cat, Mongo accepts it.



Am I missing something? can this even be done?







mongodb






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Jul 19 '11 at 7:48









Electric MonkElectric Monk

1,33511633




1,33511633








  • 1





    See stackoverflow.com/questions/4435637/…

    – pingw33n
    Jul 19 '11 at 8:19














  • 1





    See stackoverflow.com/questions/4435637/…

    – pingw33n
    Jul 19 '11 at 8:19








1




1





See stackoverflow.com/questions/4435637/…

– pingw33n
Jul 19 '11 at 8:19





See stackoverflow.com/questions/4435637/…

– pingw33n
Jul 19 '11 at 8:19












4 Answers
4






active

oldest

votes


















28














As far as I know, unique indexes only enforce uniqueness across different documents, so this would throw a duplicate key error:



db.cats.insert( { id: 123, kittens: [ { id: 456 } ] } )
db.cats.insert( { id: 123, kittens: [ { id: 456 } ] } )


But this is allowed:



db.cats.insert( { id: 123, kittens: [ { id: 456 }, { id: 456 } ] } )


I'm not sure if there's any way enforce the constraint you need at the Mongo level, maybe it's something you could check in the application logic when you insert of update?






share|improve this answer



















  • 2





    Unfortunately it seems like you're right - I'll have to enforce this in code. Oh well.

    – Electric Monk
    Jul 19 '11 at 22:36






  • 7





    So sad! :(. There should be an option to ensure uniqueness in document arrays.

    – Juzer Ali
    Aug 3 '12 at 19:06











  • What you can do is write validation hooks when saving in the array to ensure that it is unique

    – Ouwen Huang
    Nov 23 '14 at 7:53



















25














Ensuring uniqueness of the individual values in an array field



In addition to the example above, there is a function in MongoDB to ensure that when you are adding a new object/value to an array field, that it will only perform the update if the value/object doesn't already exist.



So if you have a document that looks like this:



{ _id: 123, kittens: [456] }


This would be allowed:



db.cats.update({_id:123}, {$push: {kittens:456}})


resulting in



{ _id: 123, kittens: [456, 456] }


however using the $addToSet function (as opposed to $push) would check if the value already exists before adding it.
So, starting with:



{ _id: 123, kittens: [456] }


then executing:



db.cats.update({_id:123}, {$addToSet: {kittens:456}})


Would not have any effect.



So, long story short, unique constraints don't validate uniqueness within the value items of an array field, just that two documents can't have identical values in the indexed fields.






share|improve this answer































    3














    There is an equivalent of insert with uniquness in array attribute. The following command essentially does insert while ensuring the uniqueness of kittens (upsert creates it for you if the object with 123 doesn't already exist).



    db.cats.update(
    { id: 123 },
    { $addToSet: {kittens: { $each: [ 456, 456] }}, $set: {'otherfields': 'extraval', "field2": "value2"}},
    { upsert: true}
    )


    The resulting value of the object will be



    {
    "id": 123,
    "kittens": [456],
    "otherfields": "extraval",
    "field2": "value2"
    }





    share|improve this answer
























    • The important properties are: $addToSet and upsert. The $set property is unrelated to the OP.

      – Michael Cole
      May 23 '17 at 17:23





















    0














    You can write a custom Mongoose validation method in this case. You can hook into post validation. Mongoose has validation and you can implement a hook before (pre) or after(post) validation. In this case, you can use post validation to see if the array is valid. Then just make sure the array has no duplications. There may be efficiency improvements you can make based upon your details. If you only have '_id' for example you could just use the JS includes function.



    catSchema.post('validate',function(next) {
    return new Promise((resolve,reject) => {
    for(var i = 0; i < this.kittens.length; i++) {
    let kitten = this.kittens[i];
    for(var p = 0; p < this.kittens.length; p++) {
    if (p == i) {
    continue;
    }
    if (kitten._id == this.kittens[p]._id) {
    return reject('Duplicate Kitten Ids not allowed');
    }
    }
    }
    return resolve();
    });
    });


    I like to use promises in validation because it's easier to specify errors.






    share|improve this answer























      Your Answer






      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "1"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f6743849%2fmongodb-unique-index-on-array-elements-property%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      4 Answers
      4






      active

      oldest

      votes








      4 Answers
      4






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      28














      As far as I know, unique indexes only enforce uniqueness across different documents, so this would throw a duplicate key error:



      db.cats.insert( { id: 123, kittens: [ { id: 456 } ] } )
      db.cats.insert( { id: 123, kittens: [ { id: 456 } ] } )


      But this is allowed:



      db.cats.insert( { id: 123, kittens: [ { id: 456 }, { id: 456 } ] } )


      I'm not sure if there's any way enforce the constraint you need at the Mongo level, maybe it's something you could check in the application logic when you insert of update?






      share|improve this answer



















      • 2





        Unfortunately it seems like you're right - I'll have to enforce this in code. Oh well.

        – Electric Monk
        Jul 19 '11 at 22:36






      • 7





        So sad! :(. There should be an option to ensure uniqueness in document arrays.

        – Juzer Ali
        Aug 3 '12 at 19:06











      • What you can do is write validation hooks when saving in the array to ensure that it is unique

        – Ouwen Huang
        Nov 23 '14 at 7:53
















      28














      As far as I know, unique indexes only enforce uniqueness across different documents, so this would throw a duplicate key error:



      db.cats.insert( { id: 123, kittens: [ { id: 456 } ] } )
      db.cats.insert( { id: 123, kittens: [ { id: 456 } ] } )


      But this is allowed:



      db.cats.insert( { id: 123, kittens: [ { id: 456 }, { id: 456 } ] } )


      I'm not sure if there's any way enforce the constraint you need at the Mongo level, maybe it's something you could check in the application logic when you insert of update?






      share|improve this answer



















      • 2





        Unfortunately it seems like you're right - I'll have to enforce this in code. Oh well.

        – Electric Monk
        Jul 19 '11 at 22:36






      • 7





        So sad! :(. There should be an option to ensure uniqueness in document arrays.

        – Juzer Ali
        Aug 3 '12 at 19:06











      • What you can do is write validation hooks when saving in the array to ensure that it is unique

        – Ouwen Huang
        Nov 23 '14 at 7:53














      28












      28








      28







      As far as I know, unique indexes only enforce uniqueness across different documents, so this would throw a duplicate key error:



      db.cats.insert( { id: 123, kittens: [ { id: 456 } ] } )
      db.cats.insert( { id: 123, kittens: [ { id: 456 } ] } )


      But this is allowed:



      db.cats.insert( { id: 123, kittens: [ { id: 456 }, { id: 456 } ] } )


      I'm not sure if there's any way enforce the constraint you need at the Mongo level, maybe it's something you could check in the application logic when you insert of update?






      share|improve this answer













      As far as I know, unique indexes only enforce uniqueness across different documents, so this would throw a duplicate key error:



      db.cats.insert( { id: 123, kittens: [ { id: 456 } ] } )
      db.cats.insert( { id: 123, kittens: [ { id: 456 } ] } )


      But this is allowed:



      db.cats.insert( { id: 123, kittens: [ { id: 456 }, { id: 456 } ] } )


      I'm not sure if there's any way enforce the constraint you need at the Mongo level, maybe it's something you could check in the application logic when you insert of update?







      share|improve this answer












      share|improve this answer



      share|improve this answer










      answered Jul 19 '11 at 8:18









      Chris FulstowChris Fulstow

      31.1k775100




      31.1k775100








      • 2





        Unfortunately it seems like you're right - I'll have to enforce this in code. Oh well.

        – Electric Monk
        Jul 19 '11 at 22:36






      • 7





        So sad! :(. There should be an option to ensure uniqueness in document arrays.

        – Juzer Ali
        Aug 3 '12 at 19:06











      • What you can do is write validation hooks when saving in the array to ensure that it is unique

        – Ouwen Huang
        Nov 23 '14 at 7:53














      • 2





        Unfortunately it seems like you're right - I'll have to enforce this in code. Oh well.

        – Electric Monk
        Jul 19 '11 at 22:36






      • 7





        So sad! :(. There should be an option to ensure uniqueness in document arrays.

        – Juzer Ali
        Aug 3 '12 at 19:06











      • What you can do is write validation hooks when saving in the array to ensure that it is unique

        – Ouwen Huang
        Nov 23 '14 at 7:53








      2




      2





      Unfortunately it seems like you're right - I'll have to enforce this in code. Oh well.

      – Electric Monk
      Jul 19 '11 at 22:36





      Unfortunately it seems like you're right - I'll have to enforce this in code. Oh well.

      – Electric Monk
      Jul 19 '11 at 22:36




      7




      7





      So sad! :(. There should be an option to ensure uniqueness in document arrays.

      – Juzer Ali
      Aug 3 '12 at 19:06





      So sad! :(. There should be an option to ensure uniqueness in document arrays.

      – Juzer Ali
      Aug 3 '12 at 19:06













      What you can do is write validation hooks when saving in the array to ensure that it is unique

      – Ouwen Huang
      Nov 23 '14 at 7:53





      What you can do is write validation hooks when saving in the array to ensure that it is unique

      – Ouwen Huang
      Nov 23 '14 at 7:53













      25














      Ensuring uniqueness of the individual values in an array field



      In addition to the example above, there is a function in MongoDB to ensure that when you are adding a new object/value to an array field, that it will only perform the update if the value/object doesn't already exist.



      So if you have a document that looks like this:



      { _id: 123, kittens: [456] }


      This would be allowed:



      db.cats.update({_id:123}, {$push: {kittens:456}})


      resulting in



      { _id: 123, kittens: [456, 456] }


      however using the $addToSet function (as opposed to $push) would check if the value already exists before adding it.
      So, starting with:



      { _id: 123, kittens: [456] }


      then executing:



      db.cats.update({_id:123}, {$addToSet: {kittens:456}})


      Would not have any effect.



      So, long story short, unique constraints don't validate uniqueness within the value items of an array field, just that two documents can't have identical values in the indexed fields.






      share|improve this answer




























        25














        Ensuring uniqueness of the individual values in an array field



        In addition to the example above, there is a function in MongoDB to ensure that when you are adding a new object/value to an array field, that it will only perform the update if the value/object doesn't already exist.



        So if you have a document that looks like this:



        { _id: 123, kittens: [456] }


        This would be allowed:



        db.cats.update({_id:123}, {$push: {kittens:456}})


        resulting in



        { _id: 123, kittens: [456, 456] }


        however using the $addToSet function (as opposed to $push) would check if the value already exists before adding it.
        So, starting with:



        { _id: 123, kittens: [456] }


        then executing:



        db.cats.update({_id:123}, {$addToSet: {kittens:456}})


        Would not have any effect.



        So, long story short, unique constraints don't validate uniqueness within the value items of an array field, just that two documents can't have identical values in the indexed fields.






        share|improve this answer


























          25












          25








          25







          Ensuring uniqueness of the individual values in an array field



          In addition to the example above, there is a function in MongoDB to ensure that when you are adding a new object/value to an array field, that it will only perform the update if the value/object doesn't already exist.



          So if you have a document that looks like this:



          { _id: 123, kittens: [456] }


          This would be allowed:



          db.cats.update({_id:123}, {$push: {kittens:456}})


          resulting in



          { _id: 123, kittens: [456, 456] }


          however using the $addToSet function (as opposed to $push) would check if the value already exists before adding it.
          So, starting with:



          { _id: 123, kittens: [456] }


          then executing:



          db.cats.update({_id:123}, {$addToSet: {kittens:456}})


          Would not have any effect.



          So, long story short, unique constraints don't validate uniqueness within the value items of an array field, just that two documents can't have identical values in the indexed fields.






          share|improve this answer













          Ensuring uniqueness of the individual values in an array field



          In addition to the example above, there is a function in MongoDB to ensure that when you are adding a new object/value to an array field, that it will only perform the update if the value/object doesn't already exist.



          So if you have a document that looks like this:



          { _id: 123, kittens: [456] }


          This would be allowed:



          db.cats.update({_id:123}, {$push: {kittens:456}})


          resulting in



          { _id: 123, kittens: [456, 456] }


          however using the $addToSet function (as opposed to $push) would check if the value already exists before adding it.
          So, starting with:



          { _id: 123, kittens: [456] }


          then executing:



          db.cats.update({_id:123}, {$addToSet: {kittens:456}})


          Would not have any effect.



          So, long story short, unique constraints don't validate uniqueness within the value items of an array field, just that two documents can't have identical values in the indexed fields.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Nov 17 '13 at 5:14









          doublehelixdoublehelix

          1,4141412




          1,4141412























              3














              There is an equivalent of insert with uniquness in array attribute. The following command essentially does insert while ensuring the uniqueness of kittens (upsert creates it for you if the object with 123 doesn't already exist).



              db.cats.update(
              { id: 123 },
              { $addToSet: {kittens: { $each: [ 456, 456] }}, $set: {'otherfields': 'extraval', "field2": "value2"}},
              { upsert: true}
              )


              The resulting value of the object will be



              {
              "id": 123,
              "kittens": [456],
              "otherfields": "extraval",
              "field2": "value2"
              }





              share|improve this answer
























              • The important properties are: $addToSet and upsert. The $set property is unrelated to the OP.

                – Michael Cole
                May 23 '17 at 17:23


















              3














              There is an equivalent of insert with uniquness in array attribute. The following command essentially does insert while ensuring the uniqueness of kittens (upsert creates it for you if the object with 123 doesn't already exist).



              db.cats.update(
              { id: 123 },
              { $addToSet: {kittens: { $each: [ 456, 456] }}, $set: {'otherfields': 'extraval', "field2": "value2"}},
              { upsert: true}
              )


              The resulting value of the object will be



              {
              "id": 123,
              "kittens": [456],
              "otherfields": "extraval",
              "field2": "value2"
              }





              share|improve this answer
























              • The important properties are: $addToSet and upsert. The $set property is unrelated to the OP.

                – Michael Cole
                May 23 '17 at 17:23
















              3












              3








              3







              There is an equivalent of insert with uniquness in array attribute. The following command essentially does insert while ensuring the uniqueness of kittens (upsert creates it for you if the object with 123 doesn't already exist).



              db.cats.update(
              { id: 123 },
              { $addToSet: {kittens: { $each: [ 456, 456] }}, $set: {'otherfields': 'extraval', "field2": "value2"}},
              { upsert: true}
              )


              The resulting value of the object will be



              {
              "id": 123,
              "kittens": [456],
              "otherfields": "extraval",
              "field2": "value2"
              }





              share|improve this answer













              There is an equivalent of insert with uniquness in array attribute. The following command essentially does insert while ensuring the uniqueness of kittens (upsert creates it for you if the object with 123 doesn't already exist).



              db.cats.update(
              { id: 123 },
              { $addToSet: {kittens: { $each: [ 456, 456] }}, $set: {'otherfields': 'extraval', "field2": "value2"}},
              { upsert: true}
              )


              The resulting value of the object will be



              {
              "id": 123,
              "kittens": [456],
              "otherfields": "extraval",
              "field2": "value2"
              }






              share|improve this answer












              share|improve this answer



              share|improve this answer










              answered Nov 17 '16 at 6:18









              timchunghttimchunght

              18413




              18413













              • The important properties are: $addToSet and upsert. The $set property is unrelated to the OP.

                – Michael Cole
                May 23 '17 at 17:23





















              • The important properties are: $addToSet and upsert. The $set property is unrelated to the OP.

                – Michael Cole
                May 23 '17 at 17:23



















              The important properties are: $addToSet and upsert. The $set property is unrelated to the OP.

              – Michael Cole
              May 23 '17 at 17:23







              The important properties are: $addToSet and upsert. The $set property is unrelated to the OP.

              – Michael Cole
              May 23 '17 at 17:23













              0














              You can write a custom Mongoose validation method in this case. You can hook into post validation. Mongoose has validation and you can implement a hook before (pre) or after(post) validation. In this case, you can use post validation to see if the array is valid. Then just make sure the array has no duplications. There may be efficiency improvements you can make based upon your details. If you only have '_id' for example you could just use the JS includes function.



              catSchema.post('validate',function(next) {
              return new Promise((resolve,reject) => {
              for(var i = 0; i < this.kittens.length; i++) {
              let kitten = this.kittens[i];
              for(var p = 0; p < this.kittens.length; p++) {
              if (p == i) {
              continue;
              }
              if (kitten._id == this.kittens[p]._id) {
              return reject('Duplicate Kitten Ids not allowed');
              }
              }
              }
              return resolve();
              });
              });


              I like to use promises in validation because it's easier to specify errors.






              share|improve this answer




























                0














                You can write a custom Mongoose validation method in this case. You can hook into post validation. Mongoose has validation and you can implement a hook before (pre) or after(post) validation. In this case, you can use post validation to see if the array is valid. Then just make sure the array has no duplications. There may be efficiency improvements you can make based upon your details. If you only have '_id' for example you could just use the JS includes function.



                catSchema.post('validate',function(next) {
                return new Promise((resolve,reject) => {
                for(var i = 0; i < this.kittens.length; i++) {
                let kitten = this.kittens[i];
                for(var p = 0; p < this.kittens.length; p++) {
                if (p == i) {
                continue;
                }
                if (kitten._id == this.kittens[p]._id) {
                return reject('Duplicate Kitten Ids not allowed');
                }
                }
                }
                return resolve();
                });
                });


                I like to use promises in validation because it's easier to specify errors.






                share|improve this answer


























                  0












                  0








                  0







                  You can write a custom Mongoose validation method in this case. You can hook into post validation. Mongoose has validation and you can implement a hook before (pre) or after(post) validation. In this case, you can use post validation to see if the array is valid. Then just make sure the array has no duplications. There may be efficiency improvements you can make based upon your details. If you only have '_id' for example you could just use the JS includes function.



                  catSchema.post('validate',function(next) {
                  return new Promise((resolve,reject) => {
                  for(var i = 0; i < this.kittens.length; i++) {
                  let kitten = this.kittens[i];
                  for(var p = 0; p < this.kittens.length; p++) {
                  if (p == i) {
                  continue;
                  }
                  if (kitten._id == this.kittens[p]._id) {
                  return reject('Duplicate Kitten Ids not allowed');
                  }
                  }
                  }
                  return resolve();
                  });
                  });


                  I like to use promises in validation because it's easier to specify errors.






                  share|improve this answer













                  You can write a custom Mongoose validation method in this case. You can hook into post validation. Mongoose has validation and you can implement a hook before (pre) or after(post) validation. In this case, you can use post validation to see if the array is valid. Then just make sure the array has no duplications. There may be efficiency improvements you can make based upon your details. If you only have '_id' for example you could just use the JS includes function.



                  catSchema.post('validate',function(next) {
                  return new Promise((resolve,reject) => {
                  for(var i = 0; i < this.kittens.length; i++) {
                  let kitten = this.kittens[i];
                  for(var p = 0; p < this.kittens.length; p++) {
                  if (p == i) {
                  continue;
                  }
                  if (kitten._id == this.kittens[p]._id) {
                  return reject('Duplicate Kitten Ids not allowed');
                  }
                  }
                  }
                  return resolve();
                  });
                  });


                  I like to use promises in validation because it's easier to specify errors.







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Dec 28 '18 at 13:35









                  David JDavid J

                  56638




                  56638






























                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f6743849%2fmongodb-unique-index-on-array-elements-property%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      這個網誌中的熱門文章

                      Xamarin.form Move up view when keyboard appear

                      Post-Redirect-Get with Spring WebFlux and Thymeleaf

                      Anylogic : not able to use stopDelay()