1

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

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

11 Comments

No, sorry, that's not like my data.
@Houseman and what does that mean? How do you expect to get an answer you want if you do not show exactly what you mean?
It means the way that my data is arranged is different from the way your example data is arranged, and thus, your solution does not fit my problem. I showed an example of how my data is arranged in my question.
@Houseman Show your real data if you want a real answer. I actually suspect the answer is basically the same though, you just need to apply it differently.
I actually do not want to find entire documents where certain things match. As I said, I want to find "all the elements of A,B and C where matchVar is inside D", not the document where these are true.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.