How to get one field over collection in mongoid/rails? - ruby-on-rails

I have a model
class MyClass
include Mongoid::Document
include Mongoid::Timestamps
field :a, type: String
field :b, type: String
field :c, type: String
end
So how to get all the a-s, b-s or c-s as a list/array from all objects in the MyClass collection? Does Rails/Ruby/Mongoid have any syntactic sugar for this?
I know, it is possible to do so:
all_a = []
MyClass.desc(:created_at).each do |my_object|
all_a.push(my_object.a)
end
But I thought about:
MyClass.get(:fields => :a)
MyClass.get(:fields => :a,:b)
Update:
I found something:
MyClass.create(a: "my_a_string")
MyClass.create(a: "my_another_a_string")
Now:
MyClass.only(:a)
should work, but instead I get:
=> #<Mongoid::Criteria
selector: {}
options: {:fields=>{"a"=>1}}
class: MyClass
embedded: false>
When MyClass.only(:a).to_a
=> [#<MyClass _id: 525f3b9e766465194b000000, created_at: nil, updated_at: nil, a: "my_a_string", b: nil, c: nil>, #<MyClass _id: 525f4111766465194b180000,
created_at: nil, updated_at: nil, a: "my_another_a_string", b: nil, c: nil>]
But I thought about:
["my_a_string", "my_another_a_string"]
or
[{a: "my_a_string"}, {a: "my_another_a_string"}]

MyClass.only(:a), will return every other field as nil, and the selected fields with their values...
You can use MyClass.pluck(:a) instead.
But you can pass only one field.
If you want more than one field, you can do this:
MyClass.only(:a,:b).map {|obj| [obj.a,obj.b]}

MyClass.distinct(:a) is also sometimes better than pluck since it'll only return distinct values, which is better for populating dropdowns and so on.

Related

Ruby on Rails - as_json remove some attributes

I have a response ready with some attributes. However, I dont want some of them to get passed on to the response. How do I do that? I think I need to use except somehow.
The :only and :except options can be used to limit the attributes
included, and work similar to the attributes method.
user.as_json(only: [:id, :name])
# => { "id" => 1, "name" => "Konata Izumi" }
user.as_json(except: [:id, :created_at, :age])
# => { "name" => "Konata Izumi", "awesome" => true }
You can use except parameter of as_json like so:
2.5.0 :001 > {foo: 1, bar: 2, baz: 3}.as_json(except: [:baz])
=> {"foo"=>1, "bar"=>2}
You can use only or except for your use case.
Something like below should work:
#<Address id: nil,
address1: nil,
address2: nil,
city_name: nil,
zipcode: nil,
phone: nil,
state_id: nil,
country_id: nil,
created_at: nil,
updated_at: nil,
address_type: nil,
latitude: nil,
longitude: nil,
city_id: nil,
locality: nil>
json = address.as_json(only: %i[id
address1
address2
locality
city_id
zipcode
address_type
latitude
longitude],
methods: [:city_slug]) # yes, you can use methods as attributes also

Form select for an array attribute

I have a model Usergroup which has 2 attributes which are arrays:
Usergroup.create(name: "Group 1", account_id: 7, hix_modules: ['cs-seh','cs-ddr'], users: [61,83,77])
Now I want to create a form to create a usergroup. What is the best way to do this for the array attributes? I'm thinking of using selects combined with either Cocoon or Stimulus in the end to add a variable number of users or hix_modules. But to start simple with just one fixed select: how does it look like to send a valid array to the controller?
edit your model to make the field serialized to an array
class Usergroup < ActiveRecord::Base
serialize :hix_modules,Array
serialize :users,Array
end
test it out in console
a = Usergroup.new
=> #<Usergroup id: nil, hix_modules: [], users: [], name: nil, account_id: nil, created_at: nil, updated_at: nil>
a.hix_modules
=> []
a.hix_modules << "cs-seh"
=> ["cs-seh"]
I soled it by using a multi select:
= f.select :hix_modules, options_for_select(#hix_modules), { prompt: "Select module" }, { multiple: true, size: 10}
This sends these params values:
Parameters: {"usergroup"=>{"account_id"=>"7", "name"=>"Test", "hix_modules"=>["", "CS-Agenda", "CS-DDR"], "users"=>["", "65", "77", "46"]}

Use string as PK and FK on Rails 4

I`m trying to use String as Primary/Foreign key on a small crawler that I'm making. But I keep receiving the following error when I try to use the Associations methods (eg.: a.crawler_details - Where a is an object called Asin):
RangeError: 8532503039 is out of range for ActiveRecord::Type::Integer with limit 4
Record example:
#<Asin asin: "8532503039", title: "O FĂ­sico", image_url: nil, active: false, created_at: "2015-05-04 03:30:29", updated_at: "2015-05-04 03:30:36">
Here are the details:
2.1.2 :001 > Asin.new
=> #<Asin asin: nil, title: nil, image_url: nil, active: true, created_at: nil, updated_at: nil>
2.1.2 :002 > CrawlerDetail.new
=> #<CrawlerDetail id: nil, amazon_price: nil, feed_price: nil, first_place: nil, second_place: nil, third_place: nil, fp_price: nil, sp_price: nil, tp_price: nil, run: nil, created_at: nil, updated_at: nil>
class Asin < ActiveRecord::Base
has_many :crawler_details, :foreign_key => 'id', :primary_key=> 'asin'
self.primary_key = 'asin'
...
end
class CrawlerDetail < ActiveRecord::Base
has_one :asin, :foreign_key => 'asin', :primary_key => 'id'
end
I also tried the belongs_to relation but with no luck. Any ideas here?
I think that is not association error , did you defined field name "asin" type integer in your database table ?
it may help please try to alter your database table from type integer to string .

Grouping by attribute of associated objects

class Event < ActiveRecord::Base
has_many :meetings
end
class Meeting < ActiveRecord::Base
belongs_to :event
end
How to write mysql query to search all events group_by meeting DATE(start_at)?
Event.inludes(:meetings).group ...
As a result I want to get a Hash:
{"2014-01-24"=>[#<Event id: , title: "First", created_at: "2014-01-24 16:02:52", updated_at: "2014-01-24 16:02:52">, #<Event id: 2, title: "Second", created_at: "2014-01-24 16:02:52", updated_at: "2014-01-24 16:02:52">], "2013-01-29"=>[#<Event id: 3, title: "Third", created_at: "2013-01-29 05:30:40", updated_at: "2014-01-29 05:30:40">], ...]}
P.S: I am using PostgreSQL
Now I get it by this way:
hash = {}
Meeting.where("extract(month from start_at) = ?", Date.today.month).pluck('DATE(start_at)').uniq.each do |date|
hash[date] = Event.includes(:meetings).where("DATE(meetings.start_at) = ?", date).references(:meetings)
end
But it produced so many queries to the database :(
Event.joins(:meetings).group('meetings.start_at') should do. But want you want is a group_by array method http://apidock.com/ruby/Enumerable/group_by so what you should do is
#events.group_by {|e| e.meeting.start_date}
In case of many to many you should be better off with
result = Hash.new
Meeting.include(:events).each {|m| result[m.start_at]||=[]; result[m.start_at] << m.events}
and with one liner you could
Meeting.includes(:events).inject(Hash.new) do |result, m|
result[m.start_at]||=[]
result[m.start_at] << w.events
result
end
This code should execute two database calls i think

Why is Rail2's to_json's include option returning empty hashes?

I'm trying to use Rails(2)'s to_json model serializing mechanism to emit some data from associated models. As my guide I'm referencing the following essentially identical documentation URLs:
http://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html
http://apidock.com/rails/ActiveRecord/Serialization/to_json
Here is some of the relevant model code:
class WorkEffortAssignment < ActiveRecord::Base
belongs_to :work_effort
belongs_to :assigned_to, :polymorphic => true
belongs_to :assigned_by, :polymorphic => true
Here is the controller code, I'm just trying to dump some JSON for initial testing purposes:
def dump_work_effort_assignments
WorkEffort.include_root_in_json = false
all_assignments = WorkEffortAssignment.all
options = {:include => [:work_effort, :assigned_to, :assigned_by], :only => [:work_effort_id, :assigned_to_id, :assigned_by_id]}
ext_json = "{data:#{WorkEffortAssignment.all.to_json(options)}}"
render :inline => ext_json
end
Here's the first record of Json data with empty hashes for work_effort, assigned_to and assigned_by:
{data:[{"assigned_to":{},"work_effort_id":"9","assigned_to_id":3,"assigned_by":{},"work_effort":{},"assigned_by_id":3}, //etcetera
But below is my console session showing the associations that I'd like to represent in my Json. So what am I doing wrong in my controller when trying to specify the include option for to_json, such that I can easily send associated model data back to the browser. Thanks in advance
>> assignment = WorkEffortAssignment.first
=> #<WorkEffortAssignment id: 1, assigned_at: nil, assigned_from: nil, assigned_
thru: nil, unassigned_at: nil, assigned_to_id: 3, assigned_to_type: "Party", ass
igned_by_id: 3, assigned_by_type: "Party", created_at: nil, updated_at: nil, wor
k_effort_id: "9">
>> assignment.work_effort
=> #<WorkEffort id: 9, description: "Software Architecture Document", type: nil,
started_at: nil, finished_at: nil, projected_completion_time: nil, actual_compl
etion_time: nil, created_at: "2011-08-18 13:39:30", updated_at: "2011-08-25 13:3
9:30", facility_id: nil, facility_type: nil, work_effort_record_id: nil, work_ef
fort_record_type: nil, projected_cost_id: nil, actual_cost_id: nil, parent_id: n
il, lft: 1, rgt: 2>
>> assignment.assigned_to
=> #<Party id: 3, description: "George Jempty", business_party_id: 2, business_p
arty_type: "Individual", list_view_image_id: nil, enterprise_identifier: nil, cr
eated_at: "2011-08-29 14:21:41", updated_at: "2011-08-29 14:21:41">
>> assignment.assigned_by
=> #<Party id: 3, description: "George Jempty", business_party_id: 2, business_p
arty_type: "Individual", list_view_image_id: nil, enterprise_identifier: nil, cr
eated_at: "2011-08-29 14:21:41", updated_at: "2011-08-29 14:21:41">
The :only specifier you have there is throwing it off. The following will include the child-objects:
def dump_work_effort_assignments
WorkEffort.include_root_in_json = false
all_assignments = WorkEffortAssignment.all
options = {:include => [:work_effort, :assigned_to, :assigned_by]}
ext_json = "{data:#{WorkEffortAssignment.all.to_json(options)}}"
render :inline => ext_json
end
...but will not filter out the attributes of the parent. If you want to do that, it may be simpler to just build a hash and convert it to json:
def dump_work_effort_assignments
data = WorkEffortAssignment.all.map {|wea| {
:work_effort => wea.work_effort,
:assigned_to => wea.assigned_to,
:assigned_by => wea.assigned_by
}}
ext_json = "{data:#{data.to_json}}"
render :inline => ext_json
end

Resources