user94628
user94628

Reputation: 3721

MongoDB search for each dict in list in collection

I have a collection containing a list of dicts and I want to search if any dict contains two specific key:values.

So for example I want to find_one where a dict contains a specific first and last names. This is my collection:

{
"names": [
    {
        "firstName": "bob",
        "lastName": "jones",
        "age": "34",
        "gender": "m"
    },
    {
        "firstName": "alice",
        "lastName": "smith",
        "age": "56",
        "gender": "f"
    },
    {
        "firstName": "bob",
        "lastName": "smith",
        "age": "19",
        "gender": "m"
    },          
  ]
}

I want to see if there is a record with bob smith as first and last names, I am searching this as:

first = 'bob'
last = 'smith'

nameExists = db.user.find_one({'$and':[{'names.firstName':first,'names.lastName':last}]})

Would this query retrieve the one record for bob smith?

Upvotes: 4

Views: 6860

Answers (3)

Neil Lunn
Neil Lunn

Reputation: 151072

While it is mentioned that indeed the $and operator is not required, in either form this is not the query that you want. Consider the following:

db.user.find_one({ 'names.firstName': 'alice','names.lastName': 'jones' })

This in fact does match the given record as there are both elements with "firstName" equal to "alice" and "lastName" values equal to "jones". But of course the problem here is simple in that there is no actual element in the array that has a sub-document for both of those values.

In order to match where an array element contains "both" the criteria given, you need to use the $elemMatch operator. This applies the query condition to the "elements" of the array.

db.user.find_one({ 
    'names': { '$elemMatch': { 'firstName': 'alice','lastName': 'smith' }
})

And of course if you tried "alice" and "jones" then that would not match as no element actually contains that operation.

Upvotes: 6

You don´t even need to add the $and parameter. In mongoDB, comma separated fields inside a query are joined by an implicit AND operator, so using simply {'names.firstName':first,'names.lastName':last} inside the find_one will work.

Anyway, that´s only a "clean code" fix; Your code will work properly as you are doing an "and" operation with just one element (note that the list used for the parameter $and contains only one dictionary).

Upvotes: 0

Malcolm Murdoch
Malcolm Murdoch

Reputation: 1085

Almost!

 nameExists = db.user.find_one({'$and':[{'names.firstName':first},{'names.lastName':last}]})

You need to separate the asks into separate {} brackets.

Upvotes: 0

Related Questions