5

I am trying to match documents with Mongoid/Mongodb where array fields are used in the query. I've been struggling with $elemMatch but can't seem to get it.

Context

  • A Project can have admin, member, reader users
  • These users are referenced by a Project (HABTM)
  • I want to be able to find projects where:
    • User A is admin
    • User B is admin or member
    • .. etc ..

Example

Given a Project document from Rails console:

[#<Project _id: 4f44355a9f5b7f385a000003, 
  _type: nil, name: "Project ABC", 
  desc: "some description", 
  admin_ids: 
    [BSON::ObjectId('123')], 
  member_ids: 
    [BSON::ObjectId('456'),
    BSON::ObjectId('789')], 
  reader_ids: []
>]

I had the following code:

@projects = Project.any_of({:admin_ids => [current_user.id]}, 
                           {:member_ids => [current_user.id]}).entries

Which matches current_user.id across either admin_ids and member_ids as long as there was only a single value in either of the arrays. As per the code above:

  • Trying to match user '123' gives correct result
  • Trying to match user '456' gives no result (incorrect)

$elemMatch

Based on researching, I think I should be using $elemMatch but am missing something.

As per the Project document code above:

// test case: this works with array of one
Project.all(conditions: {:admin_ids => "123"}).entries

// failure case: empty result   
Project.all(conditions: {:member_ids => {'$elemMatch' => {:id => '456' } }}).entries

// failure case: empty result
Project.all(conditions: {:member_ids => {'$elemMatch' => {:id => BSON::ObjectId('4f44a4019f5b7f3d5200000d') } }}).entries
1
  • I think you should just be able to use $in rather than $elemMatch - as the mongodocs say: "You only need to use this when more than one field must be matched in the array element." Commented Feb 22, 2012 at 11:16

2 Answers 2

11

You need to get rid of the array while querying.

@projects = Project.any_of({:admin_ids => current_user.id}, 
                       {:member_ids => current_user.id}).entries

That should work.

Sign up to request clarification or add additional context in comments.

1 Comment

for some reason I had to explicitly cast the current_user.id to a string with to_s for this to work in a scope.
11

I think you should just be able to use $in rather than $elemMatch - as the mongodocs say:

"You only need to use [$elemMatch] when more than one field must be matched in the array element."

Have you tried something like the following?

Project.any_in(:member_ids =>  [ member_id_one, member_id_two ])

2 Comments

Thanks for the clarification around $elemMatch. Yeah, I needed to have something more like @rubish's code.
Alternatively you can do: Project.where(:member_ids.in => [ member_id_one, member_id_two ])

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.