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.
Related
I am using
MongoId 5.4
Rails 4.2
Want to perform join operation and retrieve the result based on criteria.
Band Table
has_many :band_histories, dependent: :destroy
field :name, type: String
field :description, type: String
Band History
belongs_to :band, foreign_key: :band_id
field :subject, type: String
field :status, type: String
I want to add make a search on Band history where band name is 'Star band'
Normal Query Can be Made like
BandHistory.where(: band_id.in => Band.where(name: 'Star band').pluck(:id))
Trying to use includes to make it more generic
BandHistory.includes(:band).where('bands.name':'Star band').count
Above include and where clause is not returning any result
Can anyone help where i am doing wrong
I have an application where users can create many travels, and they can invite their facebook friends. In the travel document, there is a field "participants" that is an embedded document, Participant model embedded in Travel model.
Here are my models :
class Travel
include Mongoid::Document
include Mongoid::Timestamps
# relations
belongs_to :user
# fields
field :title, type: String
field :description, type: String
field :begin_date, type: Date
field :end_date, type: Date
field :budget, type: Integer
field :go_back, type: Boolean
field :title_namespace, type: String
# friends
embeds_many :participants
accepts_nested_attributes_for :participants
end
class Participant
include Mongoid::Document
include Mongoid::Timestamps
field :name, type: String
field :user_id, type: String
# relations
embedded_in :travel, :inverse_of => :participants
end
When I try to display travel where users have been invited, with this request :
#travel_participations = Travel.where('participants.user_id' => #user.id)
I don't have any result, even if I have this line in byebug :
#<Mongoid::Criteria
selector: {"participants.user_id"=>BSON::ObjectId('592c8da58511989ec850921e')}
options: {}
class: Travel
embedded: false>
So when I put this on my view :
<% unless #participations.nil? %>
<% #travel_participations.each do |travel_participation| %>
<p> <%= travel_participation.title %> </p>
<% end %>
<% end %>
I tried with .all, .first, .to_a, .as_json, no results ... Some one know where is the problem ?
You have this in your embedded model:
field :user_id, type: String
but your query is using a BSON::ObjectId:
Travel.where('participants.user_id' => #user.id)
as shown in the raw query:
selector: {"participants.user_id"=>BSON::ObjectId('592c8da58511989ec850921e')}
Your embedded document probably has a string field like:
"user_id": "592c8da58511989ec850921e"
rather than the ObjectId you're looking for:
"user_id": ObjectId("592c8da58511989ec850921e")
so you won't find what you're looking for due to the type mismatch.
Either fix the embedded field's type:
field :user_id, type: BSON::ObjectId
or query it as the string it is:
Travel.where('participants.user_id' => #user.id.to_s)
Changing the type will involve fix up whatever data you already have, changing the query is ugly in a different way.
Sometimes Mongoid will convert between strings and ObjectIds for you, sometimes it won't. When I used Mongoid I patched to_bson_id methods into BSON::ObjectId, String, Mongoid::Document, ... so that I could say things like:
Model.where(:some_id => some_id.to_bson_id)
and not have to constantly worry about what type some_id was. I also made sure that all ID fields were always specified as BSON::ObjectId.
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.
Using Tire with Mongoid, I'm having trouble figuring out how to structure a query for finding events with ElasticSearch. In particular, I'm trying to find events that users are watching in addition to events with performers the user follows:
# app/models/event.rb
class Event
include Mongoid::Document
include Tire::Model::Search
include Tire::Model::Callbacks
field :name, type: String
has_and_belongs_to_many :performers
has_many :watchers, class_name: 'User'
mapping do
indexes :name
indexes :watcher_ids, type: 'string', index: :not_analyzed
indexes :performer_ids, type: 'string', index: :not_analyzed
end
end
The following query works only for either watchers or performers.
Tire.search 'events' do
query do
string params[:query]
# Only one of these will work at a time:
terms :performer_ids, current_user.followed_performers.collect(&:id).map(&:to_s)
terms :watcher_ids, [current_user.id.to_s]
end
end
small edit because I typed my example wrong.
Here's a solution that seems to be "working"... but feels wrong
Tire.search('events') do
query do
boolean do
should { string params[:query] }
should { string "performer_ids:#{current_user.followed_performers.collect(&:id).map(&:to_s).join(',')}" }
should { string "watcher_ids:#{current_user.id.to_s}" }
end
end
end
You're on a right path, but as advised by Russ Smith, you need to use a filter DSL.
Now, if you just repeatedly call filter, you'll perform a union: AND. If you want to return either events user is watching or by performers the user follows, you have to use a or filter.
Also, for best performance, use the filtered query, as opposed to the top level filter -- in the former case, filters run first, slicing your corpus and perform queries only on this subset.
The code should look like this:
Tire.search 'events' do
query do
filtered do
query do
string params[:query]
end
filter :or, terms: { organization_ids: current_user.followed_performers.collect(&:id).map(&:to_s) },
terms: { watcher_ids: [current_user.id.to_s] }
end
end
end
See the integration tests for more examples:
https://github.com/karmi/tire/blob/master/test/integration/filters_test.rb
https://github.com/karmi/tire/blob/master/test/integration/filtered_queries_test.rb
I think what you are looking for is a filter. This is not fully tested code, but it might lead you in the right direction.
class Event
include Mongoid::Document
include Tire::Model::Search
include Tire::Model::Callbacks
field :name, type: String
has_and_belongs_to_many :performers
has_many :watchers, class_name: 'User'
mapping do
indexes :name
indexes :watcher_ids, type: 'integer', index: :not_analyzed
indexes :performer_ids, type: 'integer', index: :not_analyzed
end
end
Tire.search('events') do
query do
string 'my event'
end
filter :in, :organization_ids, [1,2,3]
filter :in, :watcher_ids, [1]
end
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.