【问题标题】:Mongoose unique index on subdocument子文档上的猫鼬唯一索引
【发布时间】:2014-11-12 22:19:30
【问题描述】:

假设我有一个简单的架构:

var testSchema = new mongoose.Schema({
    map: { type: [ mongoose.Schema.Types.Mixed ], default: [] },
    ...possibly something else
});

现在让我们确保对(_idmap._id)是唯一的。

testSchema.index({ _id: 1, 'map._id': 1 }, { unique: true });

使用db.test.getIndexes() 快速检查表明它已创建。

{
    "v" : 1,
    "unique" : true,
    "key" : {
        "_id" : 1,
        "map._id" : 1
    },
    "name" : "_id_1_map._id_1",
    "ns" : "test.test",
    "background" : true,
    "safe" : null
}

问题是,这个索引被忽略了,我可以很容易地创建多个具有相同map._id 的子文档。我可以轻松地多次执行以下查询:

db.maps.update({ _id: ObjectId("some valid id") }, { $push: { map: { '_id': 'asd' } } });

最终得到以下结果:

{
    "_id": ObjectId("some valid id"),
    "map": [
        {
            "_id": "asd" 
        },
        {
            "_id": "asd" 
        },
        {
            "_id": "asd" 
        }
    ]
}

这里发生了什么?为什么我可以推送冲突的子文档?

【问题讨论】:

标签: mongodb indexing mongoose


【解决方案1】:

mongodb中第一个objectId长度必须是24,然后可以关闭_id,将_id重命名为id或者其他,试试$addToSet。祝你好运。

CoffeeScript 示例:

FromSchema = new Schema(
  source: { type: String, trim: true }
  version: String
  { _id: false }//to trun off _id
)

VisitorSchema = new Schema(
  id: { type: String, unique: true, trim: true }
  uids: [ { type: Number, unique: true} ]
  from: [ FromSchema ]
)

//to update
Visitor.findOneAndUpdate(
  { id: idfa }
  { $addToSet: { uids: uid, from: { source: source, version: version } } }
  { upsert: true }
  (err, visitor) ->
    //do stuff

【讨论】:

  • 子文档的_id 在声明为Mixed 的数组时不是ObjectId。它也没有回答问题。
【解决方案2】:

长话短说:Mongo 不支持子文档的唯一索引,尽管它允许创建它们...

【讨论】:

  • ...这意味着无法按照问题中解释的方式对子文档进行唯一索引,这就是我所写的。
【解决方案3】:

这出现在 google,所以我想添加一个替代方法来使用索引来实现子文档中的唯一键约束,例如功能,希望没关系。

我对 Mongoose 不是很熟悉,所以它只是一个 mongo 控制台更新:

var foo = { _id: 'some value' }; //Your new subdoc here

db.yourCollection.update(
{ '_id': 'your query here', 'myArray._id': { '$ne': foo._id } },
{ '$push': { myArray: { foo } })

文档看起来像:

{
  _id: '...',
  myArray: [{_id:'your schema here'}, {...}, ...]
}

如果您的子文档键已存在,则确保 update 不会返回要更新的文档(即查找部分)的关键。

【讨论】:

  • 您可以使用$addToSet 运算符将值添加到数组中,除非该值已经存在,在这种情况下$addToSet 不会对该数组执行任何操作。
  • 但这不会覆盖模型的整个文档集吗?因为恰好在 id 匹配后,如果这个 myArray.id 已经存在,查询会尝试查找 id 和 myArray.id 匹配的文档。看起来很浪费
  • RE 集合扫描,使用索引。 $addToSet 在撰写本文时不了解对象相等性,因此无论如何都会添加。根据您的用例,这种方法现在可能有点过头了。
猜你喜欢
  • 2017-01-26
  • 2011-07-28
  • 1970-01-01
  • 1970-01-01
  • 2014-07-29
  • 2015-07-28
  • 2021-05-10
  • 1970-01-01
相关资源
最近更新 更多