Find Documents Where a Field Compares with Another in an Array

后端 未结 1 1134
野趣味
野趣味 2021-01-13 18:54

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

{
    \"_id\" : ObjectId(\"5afa6df3a24cdb1652632ef5\"),
    \"createdBy\" : {
        \"_id\         


        
相关标签:
1条回答
  • 2021-01-13 19:30

    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"
        }
      }}
    ])
    

    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
         })
      }
    })
    

    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)
      }
    })
    

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

    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"] }
        ]
      }
    })
    

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


    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())
    

    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.

    0 讨论(0)
提交回复
热议问题