MongoDB: Select element from array based on anothe

2019-08-02 14:42发布

问题:

I have a MongoDB collection with documents of the following structure (non-interesting bits left out):

{
  displayFieldId: "abcd",
  fields: [
    {
      fieldId: "efgh",
      value: "cake"
    },
    {
      fieldId: "abcd",
      value: "cheese"
    },
    ....
  ],
  ....
}

I would like to run a query on this collection to fetch only the element in the fields array which fieldId matches the document's displayFieldId. The result of the query on the document above should thus be:

{
  fields: [
    {
      fieldId: "abcd",
      value: "cheese"
    }
  ],
  ....
}

I constructed the following query. It does what I want, with the exception that the displayFieldValue is hard coded

db.containers.find({}, {
  fields: {
    $elemMatch: {
      fieldId: "abcd"
    }
  }
});

Is there a way to make it look at the document's displayFieldId and use that value instead of the hard coded "abcd"?


The server is running MongoDB 3.2.6

If possible, I would like to do this without aggregation, but if that can't be done, then aggregation will have to do

回答1:

With aggregation framework:

db.containers.aggregate([
    {        
        "$redact": { 
            "$cond": [
                { 
                    "$anyElementTrue": [ 
                        {
                            "$map": {
                                "input": "$fields",
                                "as": "el",
                                "in": { 
                                    "$eq": ["$$el.fieldId", "$displayFieldId"]
                                }
                            }
                        }
                    ] 
                },
                "$$KEEP",
                "$$PRUNE"
            ]
        }
    },
    {
        "$project": {
            "displayFieldId": 1,
            "fields": {
                "$filter": {
                    "input": "$fields",
                    "as": "el",
                    "cond": {
                        "$eq": ["$$el.fieldId", "$displayFieldId"]
                    }
                }
            },
            "otherfields": 1,
            ....
        }
    }
])

MongoDB 3.4:

db.containers.aggregate([
    {        
        "$redact": { 
            "$cond": [
                { 
                    "$anyElementTrue": [ 
                        {
                            "$map": {
                                "input": "$fields",
                                "as": "el",
                                "in": { 
                                    "$eq": ["$$el.fieldId", "$displayFieldId"]
                                }
                            }
                        }
                    ] 
                },
                "$$KEEP",
                "$$PRUNE"
            ]
        }
    },
    {
        "$addFields": {
            "fields": {
                "$filter": {
                    "input": "$fields",
                    "as": "el",
                    "cond": {
                        "$eq": ["$$el.fieldId", "$displayFieldId"]
                    }
                }
            }
        }
    }
])

Without aggregation framework - using $where (the slow query):

db.containers.find({
    "$where": function() {
        var self = this;
        return this.fields.filter(function(f){
            return self.displayFieldId === f.fieldId;
        }).length > 0;
    }
}).map(function(doc){
    var obj = doc;
    obj.fields = obj.fields.filter(function(f){
        return obj.displayFieldId === f.fieldId;
    });
    return obj;
})