Mongoid Query Related Collection - ruby-on-rails

I'm trying to figure out how to query related collections in Mongoid.
Here is my object model (simplified for brevity):
Category
-------------------
class Category
include Mongoid::Document
field :name, type: String
...
belongs_to: product
end
class Product
include Mongoid::Document
field :name, type: String
...
has_one :category
end
I'm trying to build a query to get all products with category with a particular name e.g. "Toy"
products = Product.where('category.name' => 'Toy')
I get a nil collection back. I think mongoid does not support this types of queries directly. How do I build a query that will accomplish that?

Try the following
category = Category.find_by(name: 'Toy')
# all the following will work
products = Product.where(category: category)
products = Product.where(category_id: category.id)
this will work, but it's not recommended to use mongoid this way
Mongoid provides relational-style associations as a convenience for application developers who are used to dealing with relational databases, but we do not recommend you use these extensively.

Related

Is there a method for polymorphic associate to non mongoid document?

I have an Order class with orderalbe field that is polymorphic. Some products associate with Order, for example Book.
It works fine if Book is Mongoid::Document, but some products not store in local database instead of from API fetching.
Is there a good way to handle this case? Should I implements some methods that can be same as local store?
class Order
include Mongoid::Document
belongs_to :orderable, polymorphic: true
end
class Book
include Mongoid::Document
end
class Car
def self.find(id)
# Fetch from API
new Car()
end
end
o = Order.create(orderable_id: 1, orderable_type: 'Car')
o.orderable
=> nil # why is nil? Not use self.find() method?
I found the answer by looking at the source code of mongoid. If other people have similar needs, I hope some help. Please let me know if there is a better solution.
I looked at the source code of mongoid and found it was actually queried by the where statement(not find method).
class Car
def self.where(*args)
# Fetch from API
[new(fetched_attributes_by_api)]
end
end
o = Order.first
o.orderable
=> #<Car _id: 1>

Rails3: Use joined table's data

I'm trying to access my joined table data but alas..
For example association between authors and books, author have many books:
I'm using join funtion to join the author table to the books table in order to access the author's author_name attribute
class Author < ActiveRecord::Base
has_many :books
attr_accessible :author_name
end
class Book < ActiveRecord::Base
belongs_to :author
end
random_author = Author.first
books = random_author.books.where(id > 5).joins(:author) #this should make all author attributes available to each book, right?
book_1 = books.first
book_1.author_name
=> NoMethodError: undefined method `author_name' for #<Book:0x111111>
Of course using the association would work: book_1.author.author_name but that will require another query, which is what I'm trying to avoid.
I mean the joins operation joins the author's data- there must be a way to access it right?
p.s. I can use includes method. and eager load the author data as well, but since I'm just needing a single attribute- is there a way to accomplished it with only a joins method? Thank you
You need to add
.select('books.*, author.name AS author_name')
So your query becomes
books = random_author.books.where(id > 5).joins(:author).select('books.*, author.name AS author_name')

Querying mongoid for objects with array containing specific values

I'm trying to retrieve only books where only authors in favorite_authors are among the authors for the book. (note: not an exact match, but an exclusion of all books with any authors other than those in favorite_authors).
favorite_authors = User.favorite_authors
=> [BSON::ObjectId('5363c73c4d61635257516000'),
BSON::ObjectId('5363c73c4d61635257516001'),
BSON::ObjectId('5363c73c4d61635257516002'),
BSON::ObjectId('5363c73c4d61635257516003'),
BSON::ObjectId('5363c73c4d61635257516004'),
BSON::ObjectId('5363c73c4d61635257516005'),
BSON::ObjectId('5363c73c4d61635257516006')]
class Author
include Mongoid::Document
field :name, type: String
class Book
include Mongoid::Document
field :name, type: String
field :authors, type: Array
Some naive pseudo code to give you an idea of what I'd like to do:
Book.where(authors: relevant_authors).not.where(authors: !relevant_authors)
Ideally, I'd like to accomplish this using a Mongoid Origin query.

Rails Mongoid includes doesn't load child models

I don't know exactly should it works how i expect or not, but i think that this:
def show
render json: Book.includes(:genres).find(params[:id])
end
Should include genres in book model.
class Book
include Mongoid::Document
...
has_and_belongs_to_many :genres
end
class Genre
include Mongoid::Document
field :name, type: String
end
But on client in contains only list of genre ids.
genre_ids: Array[1]
0: Object
$oid: "53532d3b616c6439c1070000"
I also tried another model Author with book:
class Author
include Mongoid::Document
field :name, type: String
has_many :books, inverse_of: :author
end
# in controller action
render json: Book.includes(:author).find(params[:id])
# returns =>
# {"book":{"_id":{"$oid":"53532d3b616c6439c1140000"},"annotation":null,"author_id":{"$oid":"53532d3b616c6439c1000000"},"author_name":"Сомерсет Моэм","co_author_ids":[],"created_at":"2014-04-20T02:13:16.057Z","date":"1947","genre_ids":[{"$oid":"53532d3b616c6439c1070000"}],"html_path":"/Users/alder/Projects/pro_book_reader/rails-api/storage/GoCUMSZP/_.html","image_url":"GoCUMSZP.jpg","name":"Театр","toc_path":null,"token":"h9beplTN"}}
Same thing, it returns only $oid (author_name is a book model property)
So, could i load all genres models with book model, without extra queries?
Rails 4.1, mongoid 4.0.0.beta1
Also i don't have association in Genre model, because it will save all book ids in genre model.
UPDATED
Identity Map has been removed from mongoid 4. There is new preload_models option, but it's not load genres too when true.
PS
It works with render json: {book: book, genres: book.genres}, so maybe thats fine until includes will be fixed.
PS2
I think perhaps this includes is not what i'm thinking of. I mean maybe it's just helps to avoid extra queries after you appeal to relational model(s). And it doesn't create array from all childs, or add author object to author field (that what any expect, not useless ID).
To display genres for each of all available books:
def show
books = Book.all
render json: books, include: :genres
end
To display genres for a particular book:
def show
book = Book.find(params[:id])
render json: book, include: :genres
end
You have to enable the identity map in mongoid.yml for eager loading to work
identity_map_enabled: true
in options.
Identity Map has been removed from mongoid master (mongoid4). You can check with the issue opened for further details, https://github.com/mongoid/mongoid/issues/3406.
Also mongoid 4 change log shows in Major Changes (Backward Incompatible) as identity map is removed. https://github.com/mongoid/mongoid/blob/006063727efe08c7fc5f5c93ef60be23327af422/CHANGELOG.md.
As they suggested, Eager load will work without identity map now.

Search using Sunspot:solr

I have two models, named as Products and Variants, in which Variant model have association
with Products as a Product have many Variants. Variant model have field named as
"available_on" ... I want to implement search using two dates as check-in n checkout dates.
.
if variants for a product available for each date , check-in date to checkout date, result will map all those products and it is the result....
.
.
guide me how i should give conditions using Sunspot:solr
roughly my models are like this
product
{
product_id integer
has_many variants
}
variant
{
variant_id integer
available_on date
belongs_to product
}
check-in n checkout are the inputs for the search.
it will be easier to answer if you put your models in question but in general way if you have has_many relationship than you should index nested model
#variant model
searchable do
integer :product_id
time :check_in
time :check_out
end
if you need to index something from parent has_many model you can use :multiple=>true option in this way
#product model
def variant_ids
variants.collect(&:id)
end
searchable do
integer :variant_ids, :multiple=>true
...
end
Index each model, and return the variant/product you need from SOLR. If you're using Spree by the way, there's a gem for integrating w/ sunspot and spree. I just forked it: https://github.com/banane/spree_sunspot_search

Resources