How can I skip the default scope for relations in mongoid?
The trashable concern implements a soft-delete on the model, also it adds the following
field :d_at, type: DateTime
default_scope -> { where(d_at: nil) }
If a brand gets trashed I still want to have it available when I load a product that has a reference to that brand
These are the model definitions
class Product
include Mongoid::Document
field :title, type: String
belongs_to :brand, class_name: 'Brand'
end
class Brand
include Mongoid::Document
include Concerns::Trashable
field :title, type: String
end
Example:
product = Product.find([id])
puts product.brand.inspect #This brand is soft-deleted and not fetched because of the default scope
This works, but it breaks more then it fixes
class Product
include Mongoid::Document
field :title, type: String
belongs_to :brand, class_name: 'Brand'
#unscope relation to brand
def brand
Brand.unscoped.find(self.brand_id)
end
end
According to the the fix Support unscoping default_scope in eager_loaded associations, you can skip the default scope manually by specifying the columns to be ignored in your association.
-> { unscope(where: :column_name) }
Or you may use unscoped_associations.
Related
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
I'm at a loss to why Mongoid is creating a new record in an association. I'm stepping closely through the code, but I've never seen anything like this. I've made a test and slimmed down the code. I left the VCR in just in case it might be related.
it "should not create a duplicate entry for MT" do
state = PolcoGroup.create(type: :state, name: 'MT', active: true)
s = state.get_senators
state.junior_senator = s[:state_junior_senator] # !!!!! this creates a new record
state.senior_senator = s[:state_senior_senator] # !!!!! so does this line
expect(Legislator.all.size).to eql(2) # actually equals 4 -- each association creates a new record
end
result is:
Legislator.all.map(&:sortname)
=> ["Tester, Jon (Sen.) [D-MT]", "Walsh, John (Sen.) [D-MT]", "Walsh, John (Sen.) [D-MT]", "Tester, Jon (Sen.) [D-MT]"]
## models
class PolcoGroup
include Mongoid::Document
include Mongoid::Timestamps
include VotingMethods
include DistrictMethods
extend DistrictClassMethods
include StateMethods
field :name, :type => String
...
# STATE RELATIONSHIPS -----------------------------
has_one :junior_senator, class_name: "Legislator", inverse_of: :jr_legislator_state
has_one :senior_senator, class_name: "Legislator", inverse_of: :sr_legislator_state
...
end
class Legislator
include Mongoid::Document
include Mongoid::Timestamps
# the following fields are directly from govtrack
field :govtrack_id, type: Integer
field :bioguideid, type: String
...
belongs_to :jr_legislator_state, class_name: "PolcoGroup", inverse_of: :junior_senator
belongs_to :sr_legislator_state, class_name: "PolcoGroup", inverse_of: :senior_senator
...
end
module StateMethods
def get_senators
...
# just returns the following
{state_senior_senator: senators.first, state_junior_senator: senators.last}
end
end
You can see more code here: https://gist.github.com/tbbooher/d892f5c234053990da70
OK -- never do what I did. I was pulling in an old version of mongo as a test database and then conducting the above. Of course it wasn't working correctly.
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
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)