【问题标题】:Mongoose match element or empty array with $in statement带有 $in 语句的 Mongoose 匹配元素或空数组
【发布时间】:2018-05-31 05:14:05
【问题描述】:

我正在尝试选择隐私设置与提供的文档相匹配的任何文档以及任何没有任何隐私设置(即公开)的文档。

当前的行为是,如果我有一个包含一组对象 ID 的架构,该数组引用了另一个集合:

privacy: [{
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Category',
    index: true,
    required: true,
    default: []
}],

我想过滤我的类别和公共类别的所有内容,在我们的例子中是没有隐私设置的内容。即一个空数组 []

我们目前使用 or 查询

{"$or":[
    {"privacy": {"$size": 0}},
    {"privacy": {"$in":
        ["5745bdd4b896d4f4367558b4","5745bd9bb896d4f4367558b2"]}
    }
]}

我很想通过仅提供一个空数组 [] 作为 $in 语句中的比较选项之一来查询它。这在 mongodb 中是可能的:

db.emptyarray.insert({a:1})
db.emptyarray.insert({a:2, b:null})
db.emptyarray.insert({a:2, b:[]})
db.emptyarray.insert({a:3, b:["perm1"]})
db.emptyarray.insert({a:3, b:["perm1", "perm2"]})
db.emptyarray.insert({a:3, b:["perm1", "perm2", []]})
> db.emptyarray.find({b:[]})
{ "_id" : ObjectId("5a305f3dd89e8a887e629ce0"), "a" : 2, "b" : [ ] }
{ "_id" : ObjectId("5a305f3dd89e8a887e629ce3"), "a" : 3, "b" : [ "perm1", "perm2", [ ] ] }
> db.emptyarray.find({b:{$in:[]}})
> db.emptyarray.find({b:{$in:[[], "perm1"]}})
{ "_id" : ObjectId("5a305f3dd89e8a887e629ce0"), "a" : 2, "b" : [ ] }
{ "_id" : ObjectId("5a305f3dd89e8a887e629ce1"), "a" : 3, "b" : [ "perm1" ] }
{ "_id" : ObjectId("5a305f3dd89e8a887e629ce2"), "a" : 3, "b" : [ "perm1", "perm2" ] }
{ "_id" : ObjectId("5a305f3dd89e8a887e629ce3"), "a" : 3, "b" : [ "perm1", "perm2", [ ] ] }
> db.emptyarray.find({b:{$in:[[], "perm1", null]}})
{ "_id" : ObjectId("5a305f3dd89e8a887e629cde"), "a" : 1 }
{ "_id" : ObjectId("5a305f3dd89e8a887e629cdf"), "a" : 2, "b" : null }
{ "_id" : ObjectId("5a305f3dd89e8a887e629ce0"), "a" : 2, "b" : [ ] }
{ "_id" : ObjectId("5a305f3dd89e8a887e629ce1"), "a" : 3, "b" : [ "perm1" ] }
{ "_id" : ObjectId("5a305f3dd89e8a887e629ce2"), "a" : 3, "b" : [ "perm1", "perm2" ] }
{ "_id" : ObjectId("5a305f3dd89e8a887e629ce3"), "a" : 3, "b" : [ "perm1", "perm2", [ ] ] }
> db.emptyarray.find({b:{$in:[[]]}})
{ "_id" : ObjectId("5a305f3dd89e8a887e629ce0"), "a" : 2, "b" : [ ] }
{ "_id" : ObjectId("5a305f3dd89e8a887e629ce3"), "a" : 3, "b" : [ "perm1", "perm2", [ ] ] }

可能是这样的:

"privacy_locations":{
     "$in": ["5745bdd4b896d4f4367558b4","5745bd9bb896d4f4367558b2",[]]
}

但是这个查询可以在控制台 (CLI) 中工作,但不能在引发转换错误的代码中工作:

{
    "message":"Error in retrieving records from db.", 
    "error":
        {
        "message":"Cast to ObjectId failed for value \"[]\" at ...
        }
}

现在我完全理解演员阵容正在发生,因为 Schema 被定义为 ObjectId。

但我仍然发现这种方法缺少两种可能的情况。

我相信可以在 $in 语句中查询(在 MongoDB 中)空选项或空数组。

array: {$in:[null, [], [option-1, option-2]}

这对吗?

我一直在想,解决我的问题(无法在选项中选择或为空)的最佳解决方案可能是让空数组成为具有 ALL 修复选项的数组。隐私设置意味着全部,而不是现在的状态,如果未设置,则视为全部。

但我不想对现有代码进行重大重构,我只需要看看我是否可以做出更好的查询或更高性能的查询。

今天我们的查询使用了一个索引有问题的 $OR 语句。即使它很快,我也想引起人们对这个问题的关注,即使它不被认为是一个错误。

我将不胜感激任何 cmets 或指导。

【问题讨论】:

    标签: mongodb mongoose mongodb-query mongoose-schema


    【解决方案1】:

    半简短的回答是,架构混合了 privacy 属性(ObjectIdArray)的类型,同时声明它在架构中严格属于 ObjectId 类型。

    由于 MongoDB 是无模式的,它允许每个文档的任何文档形状,并且不需要验证查询文档以匹配模式。另一方面,Mongoose 旨在应用模式强制,因此它将在尝试查询数据库之前根据模式验证查询文档。 { privacy: { $in: [[]] } } 的查询文档将无法通过验证,因为如错误所示,空数组不是有效的 ObjectId

    架构需要将类型声明为Mixed(不支持ref)才能继续使用空数组作为可接受的类型以及ObjectId

    // Current
    const FooSchema = new mongoose.Schema({
      privacy: [{
        type: mongoose.Schema.Types.ObjectId,
        ref: 'Category',
        index: true,
        required: true,
        default: []
      }]
    });
    
    const Foo = connection.model('Foo', FooSchema);
    
    const foo1 = new Foo();
    const foo2 = new Foo({privacy: [mongoose.Types.ObjectId()]});
    
    Promise.all([
      foo1.save(),
      foo2.save()
    ]).then((results) => {
      console.log('Saved', results);
      /*
      [
        { __v: 0, _id: 5a36e36a01e1b77cba8bd12f, privacy: [] },
        { __v: 0, _id: 5a36e36a01e1b77cba8bd131, privacy: [ 5a36e36a01e1b77cba8bd130 ] }
      ]
      */
    
      return Foo.find({privacy: { $in: [[]] }}).exec();
    }).then((results) => {
      // Never gets here
      console.log('Found', results);
    }).catch((err) => {
      console.log(err);
      // { [CastError: Cast to ObjectId failed for value "[]" at path "privacy" for model "Foo"] }
    });
    

    还有工作版本。 还要注意正确应用所需标志、索引标志和默认值的调整。

    // Updated
    const FooSchema = new mongoose.Schema({
      privacy: {
        type: [{
          type: mongoose.Schema.Types.Mixed
        }],
        index: true,
        required: true,
        default: [[]]
      }
    });
    
    const Foo = connection.model('Foo', FooSchema);
    
    const foo1 = new Foo();
    const foo2 = new Foo({
      privacy: [mongoose.Types.ObjectId()]
    });
    
    Promise.all([
      foo1.save(),
      foo2.save()
    ]).then((results) => {
      console.log(results);
      /*
        [
          { __v: 0, _id: 5a36f01733704f7e58c0bf9a, privacy: [ [] ] },
          { __v: 0, _id: 5a36f01733704f7e58c0bf9c, privacy: [ 5a36f01733704f7e58c0bf9b ] }
        ]
      */
    
      return Foo.find().where({
        privacy: { $in: [[]] }
      }).exec();
    }).then((results) => {
      console.log(results);
      // [ { _id: 5a36f01733704f7e58c0bf9a, __v: 0, privacy: [ [] ] } ]
    });
    

    【讨论】:

    • “{privacy: { $in: [[]] } } 的查询文档将无法通过验证,因为如错误所示,空数组不是有效的 ObjectId。”我不认为 [] 应该表示 ObjectID。这是数组的内容。包含 0 个 ObjectId 的数组是一个 EMPTY ARRAY []。而且我认为 $in 语句 si 会通过给定的选项,如果它包含一个 objectid 或者在这种情况下是一个字符串,因为 string 也不是一个 ObjectId 对吗?因此,通过选项转换为 ObjectId,并且应该能够评估 NULL 或空数组的选项。如果隐私为 null 则不是 OID
    • 来自 mongo 的 $in 文档:“如果该字段包含一个数组,则 $in 运算符选择其字段包含一个数组的文档,该数组包含至少一个与指定数组中的值(例如 等)”。这意味着 mongo 将遍历数组,试图将数组中的元素与查询的文档属性的任何数组值进行相等匹配。由于架构声明该属性是一个 ObjectIds 数组,Mongoose 尝试将查询文档中的所有值转换为 ObjectIds,因此会出错。
    • 如果隐私是空的 [] 像 {privacy:[]}, $in[[], ObjectId('xxxx')] 匹配 [] 因为它是隐私的值。你 where 子句和结果不是我需要的。我添加了 CLI 示例。如果 Foo.find().where({privacy: { $in: [[]] }}) 应该只返回一个文档,因为只有一个文档有一个空数组,另一个有一个带有 ObjectId 实例的数组.请允许我以不同的方式写它。如果我说隐私是 ObjectId 类型,它可以是 objectid 或 null。如果是 Array of OID,则可以为 Null、[] 或保持值。它不需要混合类型来保存这 3 个值。
    猜你喜欢
    • 1970-01-01
    • 2017-05-28
    • 2014-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-05
    • 2019-04-28
    相关资源
    最近更新 更多