MongoId combined uniqueness index - ruby-on-rails

I have three models
class Org
include Mongoid::Document
field :name, type: String
embeds_many :org_groups
end
class OrgGroup
include Mongoid::Document
field :name, type: String
embedded_in :org
has_and_belongs_to_many :humans
end
class Human
include Mongoid::Document
field :name, type: String
end
One Human can be in many Org, but only in one OrgGroup.
I need set uniqueness index for Human in Org.
How I can do this?

You can create a method that will be call by a callback.
See documentation for callbacks.
You can simply raise something from this method if your conditions are not respected.
Ask if you need a sample.

If you need a unique index in the mongodb, you can do like this:
class Person
include Mongoid::Document
field :first_name
field :last_name
index({ first_name: 1, last_name: 1 }, { unique: true })
end
And the docs are here:
https://docs.mongodb.com/ecosystem/tutorial/mongoid-indexes/
Hope this is helpful for you.

Related

Migrating from active record/postgres to mongodb/mongoid and I cannot create/save

I have the following models in mongoid(i.e., was previously in active record).Trying save product item group along with item selection option but I am running into no method "all_of" error.And I am new to using mongoid/mongobd with ROR.
class ProductItemGroup
include Mongoid::Document
include Mongoid::Timestamps
store_in database: "cnc_product_ms"
field :name, localize: true
field :description, localize: true
field :product_item_selection_type, type: String
embeds_one :item_selection_option
has_and_belongs_to_many :service_categories
field :service_category_ids, type: Array
end
class ItemSelectionOption
include Mongoid::Document
include Mongoid::Timestamps
include SimpleEnum::Mongoid
store_in database: "cnc_product_ms"
as_enum :selection_type,multiple: 1, single: 2
as_enum :mandatory_selection_type, atleast_one: 1, many: 2
field :selection_type, type: Integer
field :selection_mandatory, type: Boolean, default: false
field :mandatory_selection_type, type: Integer
embedded_in :product_item_group
end
controller /form
class ProductItemGroupForm < ApplicationForm
attr_accessor(:product_item_group)
def create
#product_item_group = ProductItemGroup.new(params)
#product_item_group.save!
end
end
on save!, I am running into following error
NoMethodError: undefined method `all_of' for #<Class:0x00007fd96afea600>
from /Users/90srebel/.rvm/gems/ruby-2.6.4/gems/activerecord-6.0.3.1/lib/active_record/dynamic_matchers.rb:22:in `method_missing'
Got the issue. I am closing this question.
What I did wrong was,
I declared has_and_belongs_to_many in product_item_group model but forgot to declare the same thing in service_category model. Silly

Mongoid Polymorphic Association Rails

Work env: Rails 4.2 mongoid 5.1
Below are my models:
class Tag
include Mongoid::Document
include Mongoid::Timestamps
field :name, type: String
belongs_to :entity_tags, :polymorphic => true
end
class EntityTag
include Mongoid::Document
include Mongoid::Timestamps
field :tag_id, type: String
field :entity_id, type: String // Entity could be Look or Article
field :entity_type, type: String // Entity could be Look or Article
field :score, type: Float
end
class Look
include Mongoid::Document
include Mongoid::Timestamps
has_many :tags, :as => :entity_tags
end
class Article
include Mongoid::Document
include Mongoid::Timestamps
has_many :tags, :as => :entity_tags
end
We are trying to implement polymorphic functionality between Looks and Articles to Tags.
i.e. Let's say we have a Tag named "politics", and we would like to add the tag to an Article with the score '0.9' and to a Look with the score '0.6'. The Score should be saved at the EntityTags Model.
The problem:
The first assign of the tag works, but then when I try to assign the same tag to another entity, it removes it and reassigns it from the first one to the latter.
The assignment looks like the following:
entity.tags << tag
Does anybody know the proper way to save associations and create the EntityTag Object with the correct polymorphism and assignment properly?
Thanks!
I've managed to implement a non-elegant working solution based on the following answer in this link

undefined method `bson_type' in mongoid add_to_set

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

Avoiding Mongoid N+1

I'd like to pull all companies that have at least one position title of "CEO".
I could hack it together with a query for each table and an intersect (I know... no joins http://mongoid.org/en/mongoid/docs/tips.html#relational_associations, and N+1 problem in mongoid, and I could just embed positions in company), but any way to do something like:
Company.includes(:positions).where("positions.title" => "CEO")?
Thanks:
class Position
include Mongoid::Document
field :title, type: String
field :profile_id, type: String
field :tenure, type: BigDecimal
belongs_to :company, index: true
class Company
include Mongoid::Document
field :name, type: String
field :linkedin_id, type: String
field :positions_count, type: Integer #Mongo Index
belongs_to :industry, index: true
has_many :positions
index({ positions_count: 1}, {background: true})
To avoid the the N+1 problem enable Mongoid identity_map feature
This will allow you to do the following query:
companies_with_ceo = Position.where(title: 'CEO').includes(:company).map(&:company)
Which should execute only 2 queries to the database.

Tricky extending of mongoid model

Having two models: Car (e.g Audi, Mercedes) and Option (ABS, Laser Headlights, Night Vision ...). Car habtm options.
Suppose both for Audi and Mercedes "Night Vision" option is available.
But to have it in Mercedes you need to pay some extra money for this option. So as I'm guessing I need to extend somehow my Option model to store options's extra price for some cars. Car model I think should also be modified. But can't imagine how.
My aim to achieve behaviour something like this:
Audi.options.night_vision.extra_price => nil
Mercedes.options.night_vision.extra_price => 300
Of course I don't want to duplicate "Night Vision" option in options collection for every car.
Thanks.
This is neither the simplest, or most elegant, just an idea that should work based on assumptions on how you may have implemented the options. Please come back to me if you need more help.
To achieve audi.options.night_vision.extra_price I assume that you have a models such as:
class car
include Mongoid::Document
field :name, type: String
has_and_belongs_to_many :options do
def night_vision
#target.find_by(name:'night_vision')
end
end
end
class option
include Mongoid::Document
field :name, type: String
field :extra_price, type: Float
has_and_belongs_to_many :cars
end
This would enable you to do:
audi = Car.find_by(name:'audi')
audi.options.night_vision.extra_price
If the above assumptions are correct, you should be able to mod your classes like so:
class option
include Mongoid::Document
attr_accessor :extra_price
field :name, type: String
has_and_belongs_to_many :cars
embeds_many :extras
end
class extra
include Mongoid::Document
field :car_id, type: String
field :price, type: String
embedded_in :option
end
class car
include Mongoid::Document
field :name, type: String
has_and_belongs_to_many :options do
def night_vision
extra = #target.find_by(name:'night_vision')
extra_price = extra.prices.find_by(car_id: #target._id.to_s) if extra
extra.extra_price = extra_price.price if extra && extra_price
return extra
end
def find_or_create_option(args)
extra = #target.find_or_create_by(name:args)
price = extra.extras.find_or_create_by(car_id:#target._id.to_s)
price.set(price: args.price
end
end
end
This should then enable you to populate your options like:
audi.options.find_or_create_option({name:'night_vision', price:2310.30})
bmw.options.find_or_create_option({name:'night_vision', price:1840.99})
audi.options.night_vision.extra_price
=> 2310.30
bmw.options.night_vision.extra_price
=> 1840.99
And if you attempted to find night_vision on a car that did not have night_vision you would get:
skoda.options.night_vision
=> nil
skoda.options.night_vision.extra_price
=> NoMethodError (undefined method 'extra_price' for nil:NilClass)

Resources