查找某个字段与数组中另一个字段进行比较的文档 [英] Find Documents Where a Field Compares with Another in an Array

查看:84
本文介绍了查找某个字段与数组中另一个字段进行比较的文档的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

比方说,我有一些看起来像这样的文件:

Let's say I have a collection of documents that look like this:

{
    "_id" : ObjectId("5afa6df3a24cdb1652632ef5"),
    "createdBy" : {
        "_id" : "59232a1a41aa651ddff0939f"
    },
    "owner" : {
        "_id" : "5abc4dc0f47f732c96d84aac"
    },
    "acl" : [
        {
            "profile" : {
                "_id" : "59232a1a41aa651ddff0939f"
            }
        },
        {
            "profile" : {
                "_id" : "5abc4dc0f47f732c96d84aac"
            }
        }
    ]
}

我想找到所有文档,其中createdBy._id != owner._idcreatedBy._id出现在acl数组之一中.最终,我将要更新所有此类文档,以将owner._id字段设置为等于createdBy._id字段.现在,我只是想弄清楚如何查询要更新的文档子集.

I want to find all documents where createdBy._id != owner._id, AND where the createdBy._id appears in one of the entries in the acl array. Eventually, I will want to update all such documents to set the owner._id field to equal the createdBy._id field. For now, I'm just trying to figure out how to query the subset of documents I want to update.

到目前为止,我已经提出了这个建议:

So far, I have come up with this:

db.boards.find({
  $where: "this.createdBy._id != this.owner._id", 
  $where: function() {
    return this.acl.some(
      function(e) => {
        e.profile._id === this.createdBy._id
      }, this);
  }
)

(为了防止ES6出现问题,我使用了ES5语法)

(I have used ES5 syntax just in case ES6 isn't ok)

但是当我运行此查询时,出现以下错误:

But when I run this query, I get the following error:

错误:错误:{"ok":0,"errmsg":"TypeError:e.profile是 未定义:\ n_funcs2/< @:2:36 \ n_funcs2 @:2:12 \ n,"代码:139}

Error: error: { "ok" : 0, "errmsg" : "TypeError: e.profile is undefined :\n_funcs2/<@:2:36\n_funcs2@:2:12\n", "code" : 139 }

如何执行此查询/这是怎么回事?我希望根据文档进行查询我读过.在上面,e应该是acl数组的元素,因此我希望它具有一个字段profile,但事实并非如此.

How do I perform this query / what is going on here? I would have expected my query to work, based on the docs I've read. Above, e should be an element of the acl array, so I expect it to have a field profile, but that doesn't seem to be the case.

请注意,我使用的是Mongo 3.2,因此无法使用 $ expr ,我已经看到一些资源表明这是有可能的.

Note, I'm using Mongo 3.2, so I can't use $expr, which I've seen some resources suggest is a possibility.

解决方案

事实证明,我对该集合的架构做出了不正确的假设.我遇到上述错误的原因是,某些文档具有acl数组,且该数组的元素没有profile字段.下面的查询检查这种情况.它也只有一个$where,因为我最初编写它的方式(有两个)似乎最终给了我条件的或"而不是与"的条件.

It turns out that I had made an incorrect assumption about the schema of this collection. The reason I was running into the above error is because some documents have an acl array with an element that doesn't have a profile field. The below query checks for this case. It also has a single $where, because the way I had written it originally (with two) seemed to end up giving me an OR of the conditions instead of an AND.

db.boards.find({
  $where: function() {
    return this.acl.some(
      function(e) => {
        e.profile !== undefined && e.profile._id === this.createdBy._id && this.createdBy._id != this.owner._id
      }, this);
  }
)

推荐答案

您仍然可以在此处将aggregate()与MongoDB 3.2一起使用,但仅需使用

You can still use aggregate() here with MongoDB 3.2, but just using $redact instead:

db.boards.aggregate([
  { "$redact": {
    "$cond": {
      "if": {
        "$and": [
          { "$ne": [ "$createdBy._id", "$owner._id" ] },
          { "$setIsSubset": [["$createdBy._id"], "$acl.profile._id"] }
        ]
      },
      "then": "$$KEEP",
      "else": "$$PRUNE"
    }
  }}
])

或者对于MongoDB 3.2 shell,使用 $where ,您只需要保留this的作用域副本,并且您的语法有些不对:

Or with $where for the MongoDB 3.2 shell, you just need to keep a scoped copy of this, and your syntax was a bit off:

db.boards.find({
  "$where": function() {
    var self = this;
    return (this.createdBy._id != this.owner._id)
      && this.acl.some(function(e) {
        return e.profile._id === self.createdBy._id
     })
  }
})

或者在与ES6兼容的环境中:

Or in an ES6 compatible environment then:

db.boards.find({
  "$where": function() {
    return (this.createdBy._id != this.owner._id)
      && this.acl.some(e => e.profile._id === this.createdBy._id)
  }
})

聚合是两者中性能最高的选项,应该总是比使用JavaScript评估更可取

The aggregate is the most performant option of the two and should always be preferable to using JavaScript evalulation

及其价值,使用 $expr 为:

And for what it's worth, the newer syntax with $expr would be:

db.boards.find({
  "$expr": {
    "$and": [
      { "$ne": [ "$createdBy._id", "$owner._id" ] },
      { "$in": [ "$createdBy._id", "$acl.profile._id"] }
    ]
  }
})

使用 $in 优先于 $setIsSubset ,其中语法略短.

Using $in in preference to $setIsSubset where the syntax is a little shorter.

注意在这里进行JavaScript比较的唯一原因是因为您在这些字段中错误地将ObjectId值存储为字符串".就像在_id字段中一样,存在真实" ObjectId的地方,比较需要从valueOf()中获取字符串"以进行比较:

NOTE The only reason the JavaScript comparison here works is because you have mistakenly stored ObjectId values as "strings" in those fields. Where there is a "real" ObjectId just like in the _id field, the comparison needs to take the "string" from valueOf() in order to compare:

    return (this.createdBy._id.valueOf() != this.owner._id.valueOf())
      && this.acl.some(e => e.profile._id.valueOf() === this.createdBy._id.valueOf())

如果没有,它实际上是JavaScript的对象比较",而{ a: 1 } === { a: 1 }实际上是false.因此,避免这种复杂性是使用本机运算符代替的另一个原因.

Without that it's actually an "Object Comparison" with JavaScript and { a: 1 } === { a: 1 } is actually false. So avoiding that complexity is another reason there are native operators for this instead.

这篇关于查找某个字段与数组中另一个字段进行比较的文档的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆