misterculture
misterculture

Reputation: 23

MongoDB search with CakePHP and multiple $all

Let's say I have a simple database and I'm trying to find all users who speak both English and Spanish and/or English and French (but not Spanish and French). I have a users table with a handful of records set up like this:

{
    "username": "jdoe",
    "languages": ["english", "spanish", "french"]
}

I get the expected results when I search like this:

db.find({ "languages" : { $all:['english', 'spanish'], $all:['english', 'french'] } })

But I'm not sure how to do it with Cake. I can match a single language pair like so:

$this->User->find('all', array('conditions' => 'User.languages' => array('$all' => array('english', 'spanish'))));

But I'm stuck on how to search for multiple pairs; an array of arrays doesn't seem to do it.

This is a project someone else started, so I'm not particularly familiar with the nuances of CakePHP or MongoDB; I have a feeling I'm overlooking something obvious. Any ideas?

Upvotes: 1

Views: 1872

Answers (1)

jmikola
jmikola

Reputation: 6922

Your shell query example may have returned the expected results for your test data, but it's technically incorrect since you're clobbering the $all property in the query object. Using the top-level $or operator, the correct query would be:

db.users.find({"$or": [
    {"languages": {"$all": ["english", "spanish"]}},
    {"languages": {"$all": ["english", "french"]}}
]});

This translates to CakePHP as:

$results = $this->User->find('all', array('conditions' => array(
    '$or' => array(
        array('User.languages' => array('$all' => array('english', 'spanish'))),
        array('User.languages' => array('$all' => array('english', 'french'))),
    ),
)));

I tested this using the following fixture data (sans _id's) in a CakePHP app:

{ "username" : "john", "languages" : [ "english", "spanish", "french" ] }
{ "username" : "mike", "languages" : [ "spanish", "french" ] }
{ "username" : "bill", "languages" : [ "english" ] }
{ "username" : "fred", "languages" : [ "english", "french" ] }
{ "username" : "luke", "languages" : [ "english", "spanish" ] }

John, Fred and Luke are returned.

Upvotes: 4

Related Questions