I'm using mongoid, and have the following code:
class Users::User
include Mongoid::Document
field :username, type: String
has_many :emails, class_name: "Users::Email"
end
class Users::Email
include Mongoid::Document
field :email, type: String
belongs_to :user, class_name: "Users::User", inverse_of: :emails
end
database:
#users collection
{
"_id" : ObjectId("5162de8a359f10cbf700000c"),
"username" : "bilbo"
}
#emails collection
{
"_id" : ObjectId("5162de8a359f10cbf700000b"),
"email" : "bilbo#jenkins.com",
"user_id" : ObjectId("5162de8a359f10cbf700000c"),
}
I'm trying to find with the following query:
Users::User.includes(:emails).any_of({username: login},{"emails.email"=> login}).first
and I don't know why, but this query ignoring search in emails relation.
When login = "bilbo" => true, but when login = "bilbo#jenkins.com" => nil
So, what I'm doing wrong?
You need a join to do what you're trying to do and Mongoid doesn't have joins. If you only need to access the emails through a user, you could denormalize them and embed them in Users::User.
class Users::User
include Mongoid::Document
field :username, type: String
embeds_many :emails, class_name: "Users::Email"
end
class Users::Email
include Mongoid::Document
field :email, type: String
embedded_in :user, class_name: "Users::User", inverse_of: :emails
end
That way, you can query on a user's emails:
irb(main):011:0> login = "bilbo#jenkins.com"
=> "bilbo#jenkins.com"
irb(main):012:0> Users::User.any_of({username: login},{"emails.email"=> login}).first
=> #<Users::User _id: 5163ee96e44f7b0301000001, username: "bilbo">
If Users::Email's only property is email you could go even a step further and omit the model completely and store the string in an array:
class Users::User
include Mongoid::Document
field :username, type: String
field :emails, type: Array
end
Querying gets even easier:
Users::User.any_of({username: login},{"emails"=> login}).first
=> #<Users::User _id: 5163ef95e44f7b6254000001, username: "bilbo", emails: ["bilbo#jenkins.com"]>
Related
I have the models
class PersonInfo
include Mongoid::Document
field :first_name, type: String
field :last_name, type: String
embedded_in :person
validates :first_name, :last_name, presence: true
end
and
class Person
include Mongoid::Document
field :account_id, type: String
validates :account_id, presence: true
embeds_one :person_info, class_name: PersonInfo
validates :person_info, presence: true
end
I need to add a new attribute with a default that uses account_id from Person
I've tried adding to PersonInfo
field :account_id, type: String, default: -> { person.account_id }
but in the rails console when I run person.person_info.account_id I keep getting NoMethodError
Any idea how to walk backward through an embedded_in relationship?
You can only references a relationship outside of a default value.
You can add a method to PersonInfo
def person_account_id
Person.account_id
end
But you can't access this method or Person.account_id from inside a default value
I have:
gem 'rails', '~> 5.1.1'
gem 'mongoid', '~> 6.1.0'
I have 3 models, related to this question.
class Book
include Mongoid::Document
field :name, type: String
field :author, type: String
field :description, type: String
field :status, type: String
field :image, type: String
has_many :histories, dependent: :destroy
has_many :likes, dependent: :destroy
has_many :comments, dependent: :destroy
def ordered_histories
histories.order(taken_in: :desc)
end
def book_rating
rating_array = likes.map { |like| like.rate.to_i }
return 0 if rating_array.empty?
(rating_array.sum / rating_array.size).round
end
end
class Like
include Mongoid::Document
field :rate, type: Integer
belongs_to :book
belongs_to :user
end
class History
include Mongoid::Document
field :taken_in, type: DateTime
field :returned_in, type: DateTime
belongs_to :book
belongs_to :user
end
I need to get top 5 books which have the most count of likes and histories.
As I know - mongoid not support joins.
I thought about storeing likes_count and histories_count in Book model. But I think that it is not best way. Please help me to find better solution to get top 5 books from mongodb.
You should use the aggregation framework.
Try the following code to get the ids of the top 5 books with most histories count. The same approach can be used for the Like class.
History.collection.aggregate([
{"$group" => {
"_id" => "$book_id",
"histories" => {"$sum" => 1}
}},
{"$sort" => { "histories" => -1}},
{"$limit" => 5 }
])
I found another way to make it works.
I have add counter_cash to model Like and History:
class Like
include Mongoid::Document
field :rate, type: Integer
belongs_to :book, counter_cache: :likes_count
belongs_to :user
end
class History
include Mongoid::Document
field :taken_in, type: DateTime
field :returned_in, type: DateTime
belongs_to :book, counter_cache: :histories_count
belongs_to :user
end
After that I use one query to get top 5 books which have the most count of likes and histories:
#top_books = Book.all.order_by(likes_count: :desc, histories_count: :desc).limit(5)
I think it is one of possible way to do this. May be it help somebody. Tell me please, if I made any mistakes.
not sure what I'm doing wrong when adding an object to set, I'm getting the error undefined method 'bson_type' for #<Favorite:0x007ffadd59ba48> at the following line
favorite = Favorite.new(
item_id: params[:item_id],
item_type: params[:item_type],
duid: params[:duid],
name: params[:name]
)
profile.add_to_set(favorites: favorite)
models
class Favorite
include Mongoid::Document
embedded_in :profile
field :item_id, type: String
field :item_type, type: String
field :name, type: String
field :duid, type: String
end
class Profile
include Mongoid::Document
field :profile_id, type: String
field :name, type: String
field :image, type: String
embeds_many :favorites
end
One way to solve this issue is to use:
favorite = Favorite.new(item_id: params[:item_id], ... yada yada)
favorite.profile = profile
favorite.save!
Another way to solve this issue is to use the following code -- which I personally prefer:
profile.favorites.create!( item_id: params[:item_id], ...yada yada )
Extra Info can be ignored
class Profile
...
embeds_many :favorites, cascade_callbacks: true
...
end
to allow the running of the callbacks found in the Favorite model
I'm having some problems trying to understand how Mongoid does its sorting. I have 2 models, Gig and Venue, both of which are associated by a belong_to has_many relationship.
I'm trying to sort objects from Gig by the attribute 'name' of the Venue Object to no avail.
I'm hoping someone out there would be able to point me to the right direction.
Below are a truncated model description.
My Query is also below:
# Gig Model
class Gig
include Mongoid::Document
include Mongoid::Paperclip
include SearchMagic
belongs_to :owner, :class_name => "User", :inverse_of => :owns
belongs_to :venue
has_and_belongs_to_many :attenders, :class_name => "User", :inverse_of => :attending
has_and_belongs_to_many :artistes
<snip>
end
# Venue Model
class Venue
include Mongoid::Document
include Mongoid::Paperclip
include SearchMagic
has_many :gigs
field :foursquare_id, type: String
embeds_one :address
embeds_many :user_ratings
field :facebook, type: String
field :twitter, type: String
field :website, type: String
field :name, type: String
field :postal, type: String
field :tel, type: String
field :venue_type, type: String
field :description, type: String
field :rating, type: Float, default: 0.0
<snip>
end
# Console
>> Gig.desc('venue.name').map{|f| f.venue.name}
=> ["*SCAPE", "Velvet Underground", "Blujaz Lounge", "Velvet Underground", "Home Club", "Wh
ite House, Emily Hill", "Zouk", "Zouk", "The Pigeonhole", "Home Club", "Home Club", "Home C
lub"]
# sorting doesn't work
You can't join in mongo. If you need joins, use a relational database. A "feature" of non-relational databases is that you can't do joins.
You have basically two choices:
a before_save callback, which will inject the name of the venue into the gig as an additional field (see for instance https://github.com/rewritten/timebank/blob/master/lib/mongoid/denormalize.rb)
a map-reduce task, which after any modification of any venue or gig, will update the venue name into the gig as an additional field.
In the end, you need a field in the Gig collection to order it.
I can't seem to figure out why Mongoid won't set the nested attributes for a child object when I create a new parent. I want to create a new Folio, add one child Feature, then push it on the Folios array on Profile.
I have a Profile, which embed many Folios, which embed many Features:
class Profile
include Mongoid::Document
include Mongoid::Timestamps::Updated
#regular fields here; removed for brevity
embeds_many :folios, class_name: "Folio"
end
class Folio
include Mongoid::Document
include Mongoid::Timestamps::Updated
accepts_nested_attributes_for :features
embedded_in :profile
field :name
field :desc
field :order, type: Integer, default:0
embeds_many :features
attr_accessible :name, :desc, :order
end
class Feature
include Mongoid::Document
include Mongoid::Timestamps::Updated
embedded_in :folio
belongs_to :project
field :content_type, type: Integer #ContentType
field :content_id
field :txt, type: String
field :order, type: Integer, default:0
attr_accessible :project_id, :content_type, :content_id, :txt, :order
end
Controller:
def new
#folio = Folio.new
#folio.features.build
end
def create
#folio = Folio.new(params[:folio])
##folio.features is still empty here.
#profile.folios << #folio
#profile.save
render "create_or_update.js"
end
In create, the param hash looks good:
{"folio"=>{"id"=>"new", "name"=>"new name", "desc"=>"new description", "features_attributes"=>{"0"=>{"project_id"=>"4ea0b68e291ebb44a100000a", "content_type"=>"1", "content_id"=>"4ea0b68e291ebb44a100000d", "txt"=>"note here"}}}, "commit"=>"Save", "action"=>"create", "controller"=>"folios"}
But #folio.features is still empty.
This worked fine with AR, if I remember. Strangely, there is no features_attributes=() method on Folio. I thought that was required for the nested attributes to work? What am I missing?
This is on Rails 3.1 with Mongoid 2.2.3.
have you tried enabling AutoSave true for features in Folio document
class Folio
include Mongoid::Document
include Mongoid::Timestamps::Updated
accepts_nested_attributes_for :features , :autosave => true
embedded_in :profile
end