We have
class Event
include Mongoid::Document
field :categories, type: Array, default: []
end
How to validate an event object to have at least 1 element in categories? Should I use custom validator? Thank you
class Event
include Mongoid::Document
field :categories, type: Array, default: []
validates :categories, length: { minimum: 1 }
end
Should do the trick (http://guides.rubyonrails.org/active_record_validations.html#length)
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.
class Foo
include Mongoid::Document
field :bars, type:Array
end
How to validate that the bars array is not empty?
Try the standard presence validator:
class Foo
include Mongoid::Document
field :bars, type: Array
validates :bars, presence: true
end
This works because the presence validator uses the blank? method to check the attribute during validation.
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"]>
I have two models Item and Tag
class Item
include Mongoid::Document
field :title, type: String
has_many :tags
validates_length_of :tags, minimum: 1
end
class Tag
include Mongoid::Document
field :title, type: String
belongs_to :item
end
The Item must have minimum 1 tag.
When item is created validation works very well:
item = Item.create(title: "black hole")
item.tags << Tag.create(title: "black")
item.tags << Tag.create(title: "heavy")
puts item.valid? # => true
item.save
But validation fails when the existed item is modified:
item = Item.find(item.id)
item.title = "nothing"
puts item.tags.count # => 2, it's ok
puts item.valid? # => false, it's wrong
How to validate count of related documents properly?
Have you tried adding attr_accessible to title?
It would look like this:
class Item
include Mongoid::Document
attr_accessible :title # <-- here
field :title, type: String
has_many :tags
validates_length_of :tags, minimum: 1
end