6

I have a collection with next data:

db.MyCollection.insert({ 
        id: 1, 
        Location: [ 1, 1 ],
        Properties: [ { Type: 1, Value: "a" }, { Type: 2, Value: "b" }, { Type: 3, Value: "c" } ]
    });

db.MyCollection.insert({ 
        id: 2, 
        Location: [ 1, 2 ],
        Properties: [ { Type: 1, Value: "a" }, { Type: 2, Value: "a" }, { Type: 3, Value: "c" } ]
    });

db.MyCollection.insert({ 
        id: 3, 
        Location: [ 2, 1 ],
        Properties: [ { Type: 1, Value: "a" }, { Type: 3, Value: "b" }, { Type: 3, Value: "a" } ]
    });

db.MyCollection.insert({ 
        id: 4, 
        Location: [ 2, 2 ],
        Properties: [ { Type: 2, Value: "b" }, { Type: 2, Value: "a" }, { Type: 3, Value: "c" } ]
    });

db.MyCollection.ensureIndex({ Location: "2d"});
db.MyCollection.ensureIndex({ "Properties.Type": 1, "Properties.Value": 1});
db.MyCollection.ensureIndex({ Location: "2d", "Properties.Type": 1, "Properties.Value": 1});

What I want is to find all items (using any of above indexes) that:

  1. match the Location
  2. contain properties with (Type=1 and Value="a") and (Type=2 and Value="b")

Here is my query (it doesn't work, but looks close to the right one):

db.MyCollection.find(
{ 
    Location: { "$within": { "$center": [ [1, 1], 5 ] } },
    Properties: {
        $elemMatch: {
            $and: [
                { Type: 1, Value: "a" },
                { Type: 2, Value: "b" }
            ]
        }
    }
})

Update:

The $all query works better as there is a problem with the $and one (see my comment in the JohnnyHK answer). Thanks for help.

1 Answer 1

15

In a case like this where you want the docs that include a specific set of array elements, you can use the $all operator:

db.MyCollection.find(
{ 
    Location: { "$within": { "$center": [ [1, 1], 5 ] } },
    Properties: {
        $all: [
            {$elemMatch: { Type: 1, Value: "a" }},
            {$elemMatch: { Type: 2, Value: "b" }}
        ]
    }
})

To do it without the $all operator you could use:

db.MyCollection.find(
{ 
    Location: { "$within": { "$center": [ [1, 1], 5 ] } },
    $and: [
        { Properties: {
            $elemMatch: { Type: 1, Value: "a" }
        }},
        { Properties: {
            $elemMatch: { Type: 2, Value: "b" }
        }}
    ]
})
Sign up to request clarification or add additional context in comments.

15 Comments

Are there alternatives to $all operator (jira.mongodb.org/browse/SERVER-1748)? There is an open bug for this operator causes it performance penalty and looks like it will not be fixed in next 2 years
@Kamarey You could also do it with $and but I don't know if it would be any faster. See updated answer.
that's not right. You want $all and $elemMatch. See the indexing second for examples: chemeo.com/doc/technology
@AsyaKamsky Both of these did work when I tested them. The $all elements behave as if you've specified $elemMatch for each one.
That's because this is a poor test data set to find that bug.
|

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.