Question

I have a 'search' function where I want to pass in an arbitrary 'filter' condition and have matches returned

The following matches any name/email where the filter string is a match:

@people = Person.all
@people = @people.or(
    {'name.first_name' => /#{filter}/i}, 
    {'name.last_name' => /#{filter}/i}, 
    {'email' => /#{filter}/i }
)

The following correctly does the same on the 'tags' array on the Person record:

@people = Person.all
@people = @people.any_in('tags' => [/#{filter}/i])

Can anyone tell me how to combine the two queries, so that a Person is matched if the filter text is found in the name, email or any of the tags?

Was it helpful?

Solution

It turns out there is a method I was missing here ... found indirectly via https://github.com/mongoid/mongoid/issues/2845

Given these two queryables:

a=Person.where({'name.first_name'=> /a/i})
b=Person.where({'name.first_name'=> /j/i})

You can combine them using .selector

Person.or(a.selector, b.selector).to_a
=> selector={"$or"=>[{"name.first_name"=>/a/i}, {"name.first_name"=>/j/i}]}

or

Person.and(a.selector, b.selector).to_a
=> selector={"$and"=>[{"name.first_name"=>/a/i}, {"name.first_name"=>/j/i}]}

OTHER TIPS

You don't need to use any_in at all. If say:

:array_field => regex

then MongoDB will automatically check each element of array_field against regex for you, you don't have care about the arrayness at all. That means that you can toss the :tags check in with the other conditions:

regex   = /#{filter}/i
@people = Person.where(
    :$or => [
        { 'name.first_name' => regex },
        { 'name.last_name'  => regex },
        { 'email'           => regex },
        { 'tags'            => regex }
    ]
)

I also pull the regex out of the query into a variable to make it clear that you're using the same one for each check and I switched to where as that's more common (at least in my experience).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top