4

I have a Mongoid document called Equipment which can embed multiple Question documents. Here are the document schemas:

class Equipment
  include Mongoid::Document
  include Mongoid::Timestamps

  field :description
  field :modelNumber
  field :title
  field :categoryId
  field :subCategoryId
  field :stateId
  field :city
  field :askingPrice

  embeds_many :questions

end

class Question
  include Mongoid::Document
  field :content

  attr_accessible :content

  embedded_in :equipment, :inverse_of => :questions
  embeds_one :answers
end

My issue is that I can retrieve a Question document based on the Question Id. However, my current query returns the parent Equipment document. I would have expected the query to return the embedded Question document. I can eventually get the embedded Question document but I have to loop through all the Question documents of the parent Equipment document.

Here is my current query:

@question = Equipment.where('questions._id' => Moped::BSON::ObjectId(params[:id])).first

Is there a way to directly obtain the embedded Question document?

Rich Blumer
  • 960
  • 1
  • 15
  • 26

2 Answers2

5

Because you are using embedded documents, grabbing a single question may not make sense. It does, however, make sense in the context of the parent document. If you really just want a specific question, you can just use normal ARish syntax:

question = Question.find(params[:id])

I would strongly suggest you make sure you have an index on the question._id field. This will help ensure this query is a fast read operation. Otherwise, Mongo will need to run through all of the Equipment documents and check each of the embedded Question objects.

If the above doesn't work, you can try a nested relation:

Equipment.find(params[:equipment_id]).questions.find(params[:id])
Aaron K
  • 6,901
  • 3
  • 34
  • 29
  • Thanks for the response! The two suggestions you offered did not work for me. :-( However, this does work for me: equipment = Equipment.find(params[:id]) question = equipment.questions.find(params[:question_id]) . Any thoughts on how I can combine these two queries into one? – Rich Blumer Jun 24 '13 at 22:21
  • 1
    @RichBlumer that's basically the 2nd query I wrote. You just have different names for your params. `question = Equipment.find(params[:id]).questions.find(params[:question_id])` – Aaron K Jun 25 '13 at 03:14
  • I agree. My issue was I couldn't chain them together like you did. Always got an error on questions, no such method. I just tried it like you have it chaining them together and IT works! Thanks for your help! – Rich Blumer Jun 25 '13 at 13:33
  • It is my understanding/experience that your first query `question = Question.find(params[:id])` will never work on an embedded document. Does creating an index on the parent change this? – emkman Aug 21 '13 at 17:14
  • @emkman I believe it works when you use the IdentityMap and have already retrieved that question once via the nested method. – Aaron K Aug 21 '13 at 19:14
  • Interesting. Thanks @AaronK. I just wrote this really ugly code: `shared_profile = User.find_by('profiles.share_token' => params[:share_token] ).profiles.find_by(share_token: params[:share_token])` and I wish there was a way to clean it up. – emkman Aug 21 '13 at 21:56
  • @emkman That may mean it's time to not use embedded documents and just move to a normal `has_many`. – Aaron K Aug 22 '13 at 15:41
  • @AaronK Agreed, I just need to see if this is the common case or the exception. There may be some refactoring in my future :) Thanks. – emkman Aug 22 '13 at 20:33
3

With rails (3.2.15) and mongoid (3.1.5) I can only do it in this way:

@equipment = Equipment.where('questions._id' => Moped::BSON::ObjectId(params[:id])).first
@question = @equipment.questions.find(params[:id])