Generate json with multiple records from DB (RoR) - ruby-on-rails

I have the following code which is expected to return complex JSON containing data selected from DB by multiple parameters and includes data from other models:
def search
params[:beacon] = [{ 'id' => 1, 'prox_uuid' => 12345453453 }, { 'id' => 2, 'prox_uuid' => 5345634564536435 }]
beacons = Beacon.includes(:ads, :venue).where("id in (?) and proximity_uuid in (?)", params[:beacon][:ids], params[:beacon][:prox_uuids])
data = beacons.map { |beacon| {
id: beacon.id,
name: beacon.name,
:venue => {
id: beacon.venue.id,
name: beacon.venue.name,
logo: URI.join(request.base_url, beacon.venue.logo.url).to_s
},
ads: beacon.ads.inject([]) do |sum, add|
sum << {
id: add.id,
title: add.name,
cover: URI.join(request.base_url, add.file_url.url).to_s,
price: add.price,
description: add.description
}
end
}
}
render json: data.to_json
end
params[:beacon] = [{ 'id' => 1, 'prox_uuid' => 12345453453 }, { 'id' => 2, 'prox_uuid' => 5345634564536435 }] is a placeholder for array of parameters coming from POST request.
The problem is that I receive the following error at the query line:
no implicit conversion of Symbol into Integer
I also tried going through the array of parameters in the loop and querying each record however json generates only the last one.

You have params[:beacon][:ids] and params[:beacon][:prox_uuids] but params[:beacon] is an array, and so should be indexed by integer, eg. params[:beacon][0]. So the error is telling you that :ids (a Symbol) cannot be implicitly converted to an Integer in order to correctly reference into the params[:beacon] array.

Related

Create a deep nested hash using loops in Ruby

I want to create a nested hash using four values type, name, year, value. ie, key of the first hash will be type, value will be another hash with key name, then value of that one will be another hash with key year and value as value.
The array of objects I'm iterating looks like this:
elements = [
{
year: '2018',
items: [
{
name: 'name1',
value: 'value1',
type: 'type1',
},
{
name: 'name2',
value: 'value2',
type: 'type2',
},
]
},
{
year: '2019',
items: [
{
name: 'name3',
value: 'value3',
type: 'type2',
},
{
name: 'name4',
value: 'value4',
type: 'type1',
},
]
}
]
And I'm getting all values together using two loops like this:
elements.each do |element|
year = element.year
element.items.each |item|
name = item.name
value = item.value
type = item.type
# TODO: create nested hash
end
end
Expected output is like this:
{
"type1" => {
"name1" => {
"2018" => "value1"
},
"name4" => {
"2019" => "value4"
}
},
"type2" => {
"name2" => {
"2018" => "value2"
},
"name3" => {
"2019" => "value3"
}
}
}
I tried out some methods but it doesn't seems to work out as expected. How can I do this?
elements.each_with_object({}) { |g,h| g[:items].each { |f|
h.update(f[:type]=>{ f[:name]=>{ g[:year]=>f[:value] } }) { |_,o,n| o.merge(n) } } }
#=> {"type1"=>{"name1"=>{"2018"=>"value1"}, "name4"=>{"2019"=>"value4"}},
# "type2"=>{"name2"=>{"2018"=>"value2"}, "name3"=>{"2019"=>"value3"}}}
This uses the form of Hash#update (aka merge!) that employs a block (here { |_,o,n| o.merge(n) } to determine the values of keys that are present in both hashes being merged. See the doc for definitions of the three block variables (here _, o and n). Note that in performing o.merge(n) o and n will have no common keys, so a block is not needed for that operation.
Assuming you want to preserve the references (unlike in your desired output,) here you go:
elements = [
{
year: '2018',
items: [
{name: 'name1', value: 'value1', type: 'type1'},
{name: 'name2', value: 'value2', type: 'type2'}
]
},
{
year: '2019',
items: [
{name: 'name3', value: 'value3', type: 'type2'},
{name: 'name4', value: 'value4', type: 'type1'}
]
}
]
Just iterate over everything and reduce into the hash. On the structures of known shape is’s a trivial task:
elements.each_with_object(
Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) } # for deep bury
) do |h, acc|
h[:items].each do |item|
acc[item[:type]][item[:name]][h[:year]] = item[:value]
end
end
#⇒ {"type1"=>{"name1"=>{"2018"=>"value1"},
# "name4"=>{"2019"=>"value4"}},
# "type2"=>{"name2"=>{"2018"=>"value2"},
# "name3"=>{"2019"=>"value3"}}}

how to get total records using $project and $group in mongodb with ror

I want to fetch total records for my product group by product title and order by product title. I am using monogomapper in ror.
I tried :
tetsing code
#test_product_details_array_full=Product.collection.aggregate([
{"$match" => {:store_id => #store_id, :product_active=> "yes"}},
{"$project"=> {"product_name"=> 1, "output"=> { "$toLower"=> "$product_name" }}},
{"$sort"=> { "output"=>1 } },
{"$project"=> {product_name: 1, _id:0}},
{"$group" => {_id: "$product_name", Product: { "$push"=> "$$ROOT"}}},
]);
testing code
I am getting blank array in out put. So how do I get proper result.

ActiveModel::ForbiddenAttributesError in Rails 4?

I am getting the following error when trying to update a Rails model via the ActiveModel update method:
ActiveModel::ForbiddenAttributesError
I am aware of the strong parameters requirement in Rails 4 per the link below, but how do I whitelist the params in my case - an array of hashes? I cannot make sense of the documentation.
http://guides.rubyonrails.org/action_controller_overview.html#strong-parameters
Here are the json params that I am trying to process:
{
id: 1,
month: 'April',
measurements: [
{ id: 1, name: 'PT', location_1: '1.1', location_2: '1.2' },
{ id: 1, name: 'OT', location_1: '1.1', location_2: '1.2' },
.
.
]
}
Controller action:
def update
#Trying to update all measurements associated with this parent object
#params.permit(measurements: [{ :name, :location_1, :location_2 } ])
#This attempt causes a syntax error
measurements = params[:measurements]
measurements.each do |measurement|
current_measurement = Measurement.find(measurement[:id])
new_measurement = measurement.except(:id)
current_measurement.update(new_measurement)
end
.
.
end
to whitelist an array of attributes you'd code it this way...
params.permit(measurements: [ :name, :location_1, :location_2 ])

What is the correct way to chain map_reduce calls in Mongoid?

I have an Element model that belongs to User. I am trying to calculate the following hash: how many users have element count of 1, 2, 3, etc. The approach I take is to first generate a hash of {user -> num elements}, then I sort-of invert it using a second map-reduce.
Here's what I have so far:
Element.map_reduce(%Q{
emit(this.user_id, 1);
}, %Q{
function(key, values) {
return Array.sum(values);
}
}).out(inline: true).map_reduce(%Q{
if (this.value > 1) {
emit(this.value, this._id);
}
}, %Q{
function(element_count, user_ids) {
return user_ids.length;
}
}).out(inline: true)
This gives me an "undefined method `map_reduce'" error. I couldn't find the answer in the docs. Any help would be great.
I calculated the hash using aggregate instead mapreduce, first grouping by user, and then grouping again by elements count:
Element.collection.aggregate([
{
"$group" => {
"_id" => "$user_id", "elements_count" => {"$sum" => 1}
}
},
{
"$group" => {
"_id" => "$elements_count", "users_count" => {"$sum" => 1}
}
},
{ "$project" => {
"_id" => 0,
"users_count" => '$users',
"elements_count" => '$_id',
}
}
])
This returns the following array:
[
{"users_count"=>3, "elements_count"=>2},
{"users_count"=>4, "elements_count"=>3},
...
]
If needed it can also be sorted using $sort operator

Rails to_json - exact order of existing columns

I have a task to form JSON data for jqGrid. It requires a special format:
{
total: 50,
page:"1",
records: "1500",
rows: [
{ 20, "{2ae39c44-ca9d-4565-9e05-bbd875c1579c}", "Description 1"},
{ 23, "{e1aaf69d-1040-4afa-8995-fd15c3a591b3}", "Description 2"},
{ 25, "{e3df29c7-ef34-46ba-bf66-7838aca7c137}", "Description 3"},
{ 29, "{768ec164-28e5-4614-a259-63257b79e8e0}", "Description 4"}
]
}
So the basic rules for "rows" are: do not generate root object name, list fields without their names, list fields in exact order to bind to corresponding columns.
Can I force to_json method to modify output as I need?
Currently the to_json produces:
myobjs : [
myobj : { id: 20, uuid: "{2ae39c44-ca9d-4565-9e05-bbd875c1579c}", name: "Description 1"},
myobj : { id: 20, uuid: "{e1aaf69d-1040-4afa-8995-fd15c3a591b3}", name: "Description 2"},
myobj : { id: 20, uuid: "{e3df29c7-ef34-46ba-bf66-7838aca7c137}", name: "Description 3"},
myobj : { id: 20, uuid: "{768ec164-28e5-4614-a259-63257b79e8e0}", name: "Description 4"}
]
You can't do it with a model-level to_json call, you'll need to build an intermediary data representation as #Paul said. Something like:
class MyObj
def to_json
[id, uuid, name]
end
end
And then in the controller:
class MyController < ApplicationController
def grid_data
objs = MyObj.all
json_data = {
:total => objs.count,
:page => 1,
:records => 1500,
:rows => objs.collect {|o| o.to_json}
}
... send json as usual ...
end
end
Note that I set your model up to generate an array, not a hash as you specified, as I think you copied that wrong - your JSON example above is not valid. { 20, 'foo', 'bar' } is not valid JSON as "{...}" represents a hash, which must be keyed, and is not ordered.

Resources