【问题标题】:Unique array value in MongoMongo中的唯一数组值
【发布时间】:2017-01-31 08:44:15
【问题描述】:

我很难找到一种方法来使集合索引按我需要的方式工作。该集合有一个包含两个元素的数组,没有其他数组可以包含这两个元素(以任何顺序):

db.collection.insert(users : [1,2] // should be valid
db.collection.insert(users : [2,3] // should be valid
db.collection.insert(users : [1,3] // should be valid
db.collection.insert(users : [3,2] // should be invalid, since there's another array with that same value.

但是,如果我使用db.collection.createIndex({users:1}, {unique: true}),它将不允许我拥有两个具有共同元素的数组:

db.collection.insert(users : [1,2] // valid
db.collection.insert(users : [2,3] // invalid, since 2 is already on another document

我尝试的解决方案之一是使数组更深一层。创建完全相同的索引,但添加文档略有不同,这几乎是我需要的方式,但它仍然允许两个数组以相反的顺序具有相同的值:

db.chat.insert({ users : { people :  [1,2] }}) // valid
db.chat.insert({ users : { people :  [2,3] }}) // valid
db.chat.insert({ users : { people :  [2,1] }}) // valid, but it should be invalid, since there's another document with [1,2] array value.
db.chat.insert({ users : { people :  [1,2] }}) // invalid

有没有办法在索引级别上实现这一点?

【问题讨论】:

    标签: mongodb indexing unique


    【解决方案1】:

    mongodb 不会在整个数组上创建索引。但是……

    我们想要一个原子操作insertupdate,并保证数组内容的唯一性?然后,我们需要计算一个特征,该特征对于数组项的所有排列都是相同的,并为其创建一个唯一索引。

    一种方法是对数组项进行排序(解决排列问题)并将它们连接起来(创建一个特征)。例子,在javascript:

    function index(arr) {
      return arr.sort().join();
    }
    
    users1 = [1, 2], usersIndex1 = index(users1); // "1,2"
    users2 = [2, 1], usersIndex2 = index(users2); // "1,2"
    
    // Create the index
    db.collection.ensureIndex({usersIndex: 1}, {unique: true});
    // 
    db.collection.insert({users: users1, usersIndex: usersIndex1}); // Ok
    db.collection.insert({users: users2, usersIndex: usersIndex2}); // Error
    

    如果数组很长,您可以对字符串应用哈希函数,从而最小化集合的大小。不过,它也有可能发生冲突的代价。

    【讨论】:

      【解决方案2】:

      您必须在pre save hook 中编写自定义验证:

      咖啡版

      预保存挂钩

      chatSchema.pre('save', (next) ->
         data = @
         @.constructor.findOne {}, (err, doc) ->
            return next err if err?
            return next "Duplicate" if customValidation(data, doc) == false
            return next()
      )
      

      自定义验证

      customValidation = (oldDoc, newDoc)->
        #whatever you need
        e.g. return !lodash.equal(oldDoc, newDoc)
      

      Js版

      var customValidation;
      
      chatSchema.pre('save', function(next) {
        var data;
        data = this;
        return this.constructor.findOne({}, function(err, doc) {
          if (err != null) {
            return next(err);
          }
          if (customValidation(data, doc) === false) {
            return next("Duplicate");
          }
          
          return next();
          
        });
      });
      
      customValidation = function(oldDoc, newDoc) {
        return !lodash.equal(oldDoc, newDoc);
      };
      

      【讨论】:

        【解决方案3】:

        您应该首先找到记录,如果没有可用记录,则插入该记录

        db.chat.findOne({users: {$all: [3,2]}})
              .then(function(doc){
                    if(doc){
                      return res.json('already exists');            
                    } else {
                       db.chat.insert({users: [3,2]})
                    }
             })
             .catch(next);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-03-13
          • 2012-03-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多