Search code examples
rubymongodbhashmongoidmongoid4

Mongoid 'or' and 'in' - querying multiple arrays


I have a document in Mongo with two array fields:

field   :app_usernames,     type:  Array 
field   :email_addresses,   type:  Array

I'd like to create a function which take an array of usernames and an array of email addresses to search collection. The kicker is that I want it to return documents which have any of the values passed in the arrays:

def find_people(usernames_to_search, emails_to_search)...

So given a document with field values:

app_usernames = ['test1','test2','test3']
email_addresses = ['test1@test.com','test2@test.com']

I want the function to find it when any of those values are searched, via the array parameters. It should return this document in the following cases:

find_people nil,['test1@test.com']
find_people ['test3'],['test1@test.com']
find_people ['oldusername'],['test1@test.com']

The last one seems to be causing me trouble.

Thus far I've tried

.or(:app_usernames.in usernames_to_search, :email_addresses.in emails_to_search)

But to no avail.


Solution

  • The or method is meant to be called with a list of individual conditions so that it can turn this:

    x.or(condition1, condition2)
    

    into a MongoDB query like:

    $or: [ condition1, condition2 ]
    

    When you say:

    .or(:app_usernames.in => usernames_to_search, :email_addresses.in => emails_to_search)
    

    How many arguments are you passing to or? The answer is one. You're actually saying this:

    .or({ :app_usernames.in => usernames_to_search, :email_addresses.in => emails_to_search })
    

    You need to add the braces yourself so Ruby doesn't collapse the arguments into one Hash:

    .or(
      { :app_usernames.in   => usernames_to_search },
      { :email_addresses.in => emails_to_search    }
    )
    

    Or something like this:

    args = [ ]
    args.push(:app_usernames.in => usernames_to_search) if(usernames_to_search.present?)
    args.push(:email_addresses.in => emails_to_search)  if(emails_to_search.present?)
    query = query.or(*args) if(args.present?)