简体   繁体   中英

Mongoose query to find matching elements in multiple arrays

I have data like this:

{
  name:'string'
  A: [
    {
      D: []
    }
 ],
  B:[
    {
      D:[]
    }
  ],
  C:[
    {
      D:[]
    }
   ]
}

I want to search through all the D arrays in every array of A , B and C for a match.

I've made this query but it always returns an empty array, so I don't think I did it right.

model.find({'name':nameVar, $elemMatch:[
    {'A':{'A.D':{$in:[matchVar]}}},
    {'B':{'B.D':{$in:[matchVar]}}},
    {'C':{'C.D':{$in:[matchVar]}}}]},function(err,data){...

How can I return all the elements of A , B and C where matchVar is inside D ?


EDIT:

It looks like this isn't even possible? https://groups.google.com/forum/#!topic/mongodb-user/rUHK43Xtp88

But I'm still holding out for an answer

Well if I understand what you are actually asking in your question, then with this data:

{ 
    "name" : "string", 
    "A" : [ { "D" : [ "B" ] } ], 
    "B" : [ { "D" : [ "A" ] } ], 
    "C" : [ { "D" : [ "B" ] } ] 
},
{ 
    "name" : "string", 
    "A" : [ { "D" : [ "C" ] } ],
    "B" : [ { "D" : [ "C" ] } ], 
    "C" : [ { "D" : [ "C" ] } ]
},
{ 
    "name" : "string", 
    "A" : [ { "D" : [ "C" ] } ], 
    "B" : [ { "D" : [ "B" ] } ], 
    "C" : [ { "D" : [ "C" ] } ] 
}

You find the first document like so:

db.collection.find({
    "$or": [
         { "A.D": "A" },
         { "B.D": "A" },
         { "C.D": "A" },
     ]
})

Or perhaps you meant that the other way around, so to match the second document only:

db.collection.find({
    "$and": [
         { "A.D": "C" },
         { "B.D": "C" },
         { "C.D": "C" },
     ]
})

The link that you reference actually refers to some different behavior with the $all operator which has been changed in the 2.6 release to do what it really should be doing. But that doesn't really apply to this question how you have presented it.

So if you want any of the arrays to match then use $or around each query statement. If on the other hand you want only to match where all of the arrays contain your match, then use the $and operator instead.

It's only logical ;)


EDIT

Keep in mind that .find() is all about matching documents. When you actually want to match elements within arrays that extend beyond one match only then you use aggregate in order to filter the content:

db.collection.aggregate([
    // Matching documents still makes sense to reduce the pipeline
    { "$match": {
        "$or": [
             { "A.D": "B" },
             { "B.D": "B" },
             { "C.D": "B" },
        ]
    }},

    // Unwind all of the arrays
    { "$unwind": "$A" },
    { "$unwind": "$B" },
    { "$unwind": "$C" },
    { "$unwind": "$A.D" },
    { "$unwind": "$B.D" },
    { "$unwind": "$C.D" },

    // Set up for the next stage by adding "type"
    { "$project": { 
         "A": 1,
         "B": 1,
         "C": 1,
         "type": { "$cond": [1, ["A","B","C"], 0] }
    }},

    // Unwind that array so every document copied for each type
    { "$unwind": "$type" },

    // Do some conditional re-shaping to key/values
    { "$project": {
        "key": { "$cond": [
            { "$eq": [ "$type", "A" ] },
            "A",
            { "$cond": [
                { "$eq": [ "$type", "B" ] },
                "B",
                { "$cond": [
                { "$eq": [ "$type", "C" ] },
                "C",
                false
                ]}
            ]}
        ]},            
        "value": { "$cond": [
            { "$eq": [ "$type", "A" ] },
            "$A",
            { "$cond": [
                { "$eq": [ "$type", "B" ] },
                "$B",
                { "$cond": [
                { "$eq": [ "$type", "C" ] },
                "$C",
                false
                ]}
            ]}
        ]}
    }},

    // Filter for only the matching documents
    { "$match": { "value.D": "B" } },

    // Just group that by id
    { "$group": {
        "_id": "$_id",
        "doc": { "$push": { 
            "key": "$key", 
            "value": "$value" 
        }}
    }}
])

And that will return results:

{
    "_id" : ObjectId("53473e6ecb495e216c98292b"),
    "doc" : [
            {
                    "key" : "B",
                    "value" : {
                            "D" : "B"
                    }
            }
    ]
}
{
    "_id" : ObjectId("53473d87cb495e216c982929"),
    "doc" : [
            {
                    "key" : "A",
                    "value" : {
                            "D" : "B"
                    }
            },
            {
                    "key" : "C",
                    "value" : {
                            "D" : "B"
                    }
            }
    ]
}

So I got the two documents with only the rows from the arrays that matched the condition. Not in the same format as it went in, but then end result is the same.

You get some additional features with 2.6 that could be used ( I think, I haven't tried ) to re-shape again back into the form, but generally speaking, then yes the result is possible. It's a lot of work, but as you can see from the re-shaped form it also gives an indication of the format the data "should" probably be in if you want to query like this.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM