Rails Mongoid : Query embedded document and access to Mongoid criteria - ruby-on-rails

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.

Related

Mongoidalize throws NameError, uninitialized constant Mongoid::Relations

project isn't complicated, but finding my way around mongoid relationships has been. It would be nice to find some in depth explanation.
I couldn't make my code work however I set them up so I managed to find Mongoid_Alize: https://github.com/dzello/mongoid_alize
Yet when I do it with alize or alize_to/_from, console gives me back an error on Mongoid::Relations in Ruby2.3.3/lib/ruby/gems/2.3.0/bundler/gems/mongoid_alize-b9175f4e3165/lib/mongoid/alize/macros.rb:79:in `_alize_relation_types'"
I have also included eager loading with identity_map_enabled: true in mongoid.yml
So here's the code:
Currency.rb
class Currency
include Mongoid::Document
include Mongoid::Alize
field :name, type: String
field :state, type: String
field :code, type: Integer
field :unit, type: Integer
has_many :currency_value
has_many :value, :class_name => 'Value', :inverse_of => :currency
alize_to :value
end
Value.rb
class Value
include Mongoid::Document
include Mongoid::Alize
field :date, type: DateTime
field :buying, type: String
field :middle, type: String
field :selling, type: String
belongs_to :value_currency, :class => 'Currency', :inverse_of => :currency_value
belongs_to :currency, :class_name => 'Currency', :inverse_of => :value
alize_from :currency
end
ApiService.rb
class ApiService
# Has some api data coming from external function getApiData
def update()
#arrayOfHashes= getApiData() #array of hashes
#arrayOfHashes.each do |hash|
#currency = Currency.new({
name: hash["Name"],
state: hash["State"],
code: hash["Currency code"],
unit: hash["Unit"]
})
#currency.create_value(
date => hash["Date of value"],
buying => hash["Value when buying"],
middle => hash["Middle value"],
selling => hash["Value when selling"])
#currency.save
end
return #currency
end
end
Expected result: save all 13 iterations in MongoDB with their relations.
It would be much obliged to hear some explanation here cause I'm uninspired and stuck in place. I have some background in programming and i have a feeling that it shouldn't be that complicated.
I can save either Currency model or Value but not both with their relations. Other idea is to do it manually by adding a custom foreign_id and making a method in the model which will populate hash. But that could lead to future performance issues.
Any help is appreciated, thank you :)

Links between class rails

I'm new to rails and in my project I have 2 classes who have a relationship betwen them. The problem is they dont can list services with organs. The following is the code I have:
modelos
class Admin::Organ
include Mongoid::Document
include Mongoid::Search
include Mongoid::Pagination
field :name, type: String
field :address, type: String
field :acronym, type: String
field :phones, type: String
field :emails, type: String
field :image, type: String
field :permalink, type: String
field :schedules, type: Array
field :coordinates, type: Hash
has_many :services, class_name: "Service"
has_many :units, class_name: "Admin::Unit"
before_save :touch_permalink
search_in :name
paginates_per 10
def url
"/orgao/#{permalink}"
end
private
def touch_permalink
self.permalink = self.name.parameterize
end
end
class Service
include Mongoid::Document
include Mongoid::Search
include Mongoid::Pagination
field :name, type: String
field :acronym, type: String
field :popular_names, type: Array
field :description, type: String
field :free, type: Boolean
field :applicants, type: Array
field :estimated_time, type: Hash
field :steps, type: Array
field :permalink, type: String
field :other_informations, type: String
belongs_to :organ, class_name: "Admin::Organ"
has_and_belongs_to_many :units, class_name: "Admin::Unit"
has_and_belongs_to_many :audiences, class_name: "Admin::Audience"
has_and_belongs_to_many :categories, class_name: "Admin::Category"
before_save :touch_permalink
search_in :name, :popular_names
paginates_per 10
def organ_id
read_attribute(:organ_id).to_s
end
def url
"/servico/#{permalink}"
end
private
def touch_permalink
self.permalink = self.name.parameterize
end
end
#controlers
class ServicesController < ApplicationController
def index
#organs = Admin::Organ.all
#services = Service.page(params[:page].to_i).per(3)
end
def show
#service = Service.where(permalink: params[:permalink]).first
end
end
class OrgansController < ApplicationController
def index
#organs = Admin::Organ.page(params[:page]).per(2)
end
def show
#organ = Admin::Organ.where(permalink: params[:permalink]).first
#organs = Admin::Organ.page(params[:page]).per(1)
end
end
#call in index.html
<%= organs.services.name%>
This appears to be returning an error whenever I run it
Note: On mongo relationships when you call a relationship method (such as services in your case) all of the documents associated with it are returned. Also for the pagination plugin you are using (I'm assuming kaminari by the syntax), this will return a collection of documents as well.
If so, it could be expected that you have a view for organs that has something a little like this:
<% #organs.each do |organ| %>
<div>Name: <%= organ.name %></div>
<div>Address: <%= organ.address %></div>
<div>Services:</div>
<% organ.services.each do |service| %>
<div>Name: <%= service.name %></div>
<div>Acronym: <%= service.acronym %></div>
<% end %>
<% end %>
<%= paginate #organs %>
You'll notice that to print out the services you need to first have a handle on the current document within #organs, and then you need to iterate the services within it. If you were to call name (which is a method of a relationship) you will get the relationship name (I believe this is what you are seeing).
I hope this helps. I had to do some guessing between the lines, come back to me if you need any further help.

How to define has_and_belongs_to association in rails

I am following this video http://railscasts.com/episodes/258-token-fields-revised and i implement this also sucessfully. But now i am using namespace.
I have lends_controller inside folder employee inside asset folder.
this is my model of lend controller
class Employee::Asset::Lend
include Mongoid::Document
include Mongoid::Timestamps
field :name, type: String
field :text, type: String
field :date
field :asset_tokens
field :user_id, type: String
has_and_belongs_to_many :assets
belongs_to :tags
def asset_tokens=(tokens)
self.asset_ids = Asset.ids_from_tokens(tokens)
end
end
Now i have another model asset. There i have to define has and belongs to this lend model also I did this
class Asset
include Mongoid::Document
field :name, type: String
field :description, type: String
field :serial_number, type: String
field :status, type: Integer
field :tag_tokens
field :quantity, type: Integer
validates_presence_of :name
validates :serial_number,:uniqueness => true
has_and_belongs_to_many :employee_asset_lends
has_and_belongs_to_many :tags
def self.tokens(query)
assets = where(name: /#{Regexp.escape(query)}/i)
end
form for lend controller is
<%= f.label :asset_tokens, "Assets" %>
<%= f.text_field :asset_tokens, data: {load: #employee_asset_lend.assets}%><br>
<%= f.input :date,:input_html => { :class => "dp1"},:label=> "Lend Date"%>
inside coffescript file for lend.js.coffee
jQuery ->
$('#employee_asset_lend_asset_tokens').tokenInput '/assets.json'
theme: 'facebook'
prePopulate: $('#employee_asset_lend_asset_tokens').data('load')
But it gives error uninitialized constant EmployeeAssetLend from asset views.
and from lend view it gives error like undefined methodall_of' for Employee::Asset:Module`
pleaes check the right way to make HABTM-has_and_belongs_to_many Assosiation for more details

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.

Nested attributes in mongodb confusion

I am using rails with database Mongodb.
I am using devise . Devise has model name user.The id of the user is in
:id => current_user.id
I want to make a model such that when data is save from the form the id of the current user will also save in the collection.
The model of my employee is
class Employee
include Mongoid::Document
field :first_name, type: String
field :middle_name, type: String
field :last_name, type: String
field :otherid, type: String
field :licennum, type: String
field :citizennum, type: String
field :licenexp, type: String
field :gender, type: String
field :state, type: String
field :marital_status, type: String
field :country, type: String
field :birthdate, type: String
field :nickname, type: String
field :description, type: String
validates_presence_of :first_name
{
}
end
what should i put inside curly bracket so that when data is saved from that model it saves the current user id also inside it?
The code you show only contains personal information fields like birth date and such. I suppose the simplest solution would be to place them inside the User class, and change them using built-in devise actions such as devise/registrations#edit, which applies the changes to current_user as default.
Alternatively, if you want to keep Employee as a separate class, you could try embedding Employee in User class, like this:
class User
include Mongoid::Document
embeds_one :employee
(...)
class Employee
include Mongoid::Document
embedded_in :user
In this case, you would set up the relation at controller level, e.g.:
class EmployeesController < ApplicationController
def create
current_user.create_employee(params[:employee])
end
After creation, you could access the user's ID from Employee class as user.id.

Resources