Rails scope json field on joins relation - ruby-on-rails

I have 2 models
Form.rb
has_many :elements
Element
belongs_to :form
I want to query Elements like this:
Element.joins(:form).where(hash)
hash = {
form: {
name: "some name",
surname: "some surname"
},
element_name: "some name"
}
these attributes are both from Form and Element models, and this works fine. Now i need to scope Forms with 1 more attribute (date) which is stored in meta attribute (json field) so Form object looks like:
Form
- name
- surname
- meta: {date: "2018-10-10"}
I have created scope that can work like this:
scope :by_date, ->(date) { where('JSON_EXTRACT(meta, "$.date") = ?', date) }
Form.by_date("2018-10-10")
and this works fine, but how can i use this scope in above scenario since i can't do:
hash = {
form: {
name: "some name",
surname: "some surname",
date: "2018-10-10"
},
element_name: "some name"
}

Related

Using group_by but return an array of hashes

I simply want to group cities by their state and from there have the array of the hash key (i.e. State Name) return an array of hash data pertaining to it's cities. Right now I have something like this:
City.all.group_by { |c| c.state.name }
Which will return:
{
"Illinois": [# < City id: 3, name: "Chicago", state_id: 3 > ],
"Texas": [# < City id: 2, name: "Houston", state_id: 2 > ],
"California": [# < City id: 1, name: "Los Angeles", state_id: 1 > ],
"New York": [# < City id: 4, name: "New York City", state_id: 4 > ]
}
Notice how it returns an array of rails objects. Instead I want to return an array of hashes with certain attributes, like their id and name.
The reason the grouped values are Rails objects (your models) is due to the fact that you also start with these objects. You can use the attributes method to retrieve the attributes of a model instance as a hash.
The following achieves the result you want:
City.all.group_by { |city| city.state.name }
.transform_values { |cities| cities.map(&:attributes) }
If you only want specific attributes, use slice instead:
City.all.group_by { |city| city.state.name }
.transform_values { |cities| cities.map { |city| city.slice(:id, :name) } }
Note that slice will return an ActiveSupport::HashWithIndifferentAccess instance. Which mostly can be used in the same manner as a normal hash, but returns the same value for both hash[:name] and hash['name']. If you rather use a normal hash append a to_hash call after the slice call.
This should be enough for you
City.all.group_by { |c| c.state.name }.map {|k,v| [k, v.attributes] }.to_h
and to select only specified attributes do
v.attributes.slice(:name, :id)
One of the easiest approach is to convert it into json object
City.all.as_json.group_by { |c| c.state.name }
this will fix the issue

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"]}

How can i map one query result to another query result in ruby on rails

Hi I am new to Ruby on Rails development. I have two queries with different model. My first_query is get from question model and second query is get from favourite model. I want to map with a column user_favourite from second query result to first query result.
this is my controller queries
def index
#first_query = Question.order('created_at DESC').page(params[:page]).per( (ENV['ILM_QUESTIONS_PER_PAGE'] || 5).to_i )
#second_query=Favourite.with_user_favourite(#user)
#combined_queries = #first_query + #second_query
end
favourite.rb
scope :with_user_favourite, -> (user) {
joins(:user).
where(favourites: {user_id: user})
}
index.json.builder
json.questions #combined_events
json for the result is
{
questions: [ #this is first query result
{
id: 88,
user_id: 28,
content: "test32",
image: {
url: null,
thumb: {
url: null
},
mobile: {
url: null
}
}
},
{
id: 87,
user_id: 18,
content: "testing riyas",
image: {
url: null,
thumb: {
url: null
},
mobile: {
url: null
}
}
},
{ #this is second query result
id: 1,
user_id: 2,
question_id: 84,
created_at: "2016-05-12T06:51:54.555-04:00",
updated_at: "2016-05-12T06:51:54.555-04:00"
},
{
id: 2,
user_id: 2,
question_id: 81,
created_at: "2016-05-12T07:23:47.770-04:00",
updated_at: "2016-05-12T07:23:47.770-04:00"
}
]
}
i want response like
{
questions: [
{ #first query result
id: 88,
user_id: 28,
content: "test32",
image: {
url: null,
thumb: {
url: null
},
mobile: {
url: null
}
},
user_favorite: { #corresponding result from second query result
id: 1,
user_id: 2,
question_id: 88
}
},
{ #first query result
id: 87,
user_id: 18,
content: "testing riyas",
image: {
url: null,
thumb: {
url: null
},
mobile: {
url: null
}
},
user_favorite: {} #corresponding result from second query result if there is no result for particular question in favourite table
},
]
}
The model relationships are:
class Question
belongs_to :user
has_many :favourite
end
class Favourite
belongs_to :user
belongs_to :question
end
class User
has_many :questions
has_many :favourite
end
You should modify your jBuilder template to support nesting.Since your model association is like one question has_many favorite so it will be an array and you can easily nest one object inside another.
json.array! #questions do |question|
json.user_id question.user_id
json.content question.content
json.user_favorites question.favorites do |json,favorite|
json.id question.favorite.id
json.user_id question.favorite.user.id
json.question_id question.id
end
end
Here is a link that you can refer to for more clarity.
Generate a nested JSON array in JBuilder
Using JBuilder to create nested JSON output in rails
Hope it helps!.
You can add an association between user_favourite and question so that you can select all user favourites on one question.
Question.rb:
has_many :user_favourites
UserFavourite.rb:
belongs_to :question
Then, as your web action:
def index
#questions = Question.all.order('created_at DESC').page(params[:page]).per((ENV['ILM_QUESTIONS_PER_PAGE'] || 5).to_i)
end
And finally, in index.json.builder:
json.questions #questions do |question|
json.user_favourites question.user_favourites
end
including whatever other fields you want.

How to group-by and nest results in each group?

I tried to do this:
Things.order("name").group("category_name")
I was expecting the results to be something like this:
[
{
"category_name": "Cat1",
"things":
[
{ "name": "Cat1_Thing1" },
{ "name": "Cat1_Thing1" }
]
},
{
"category_name": "Cat2",
"things":
[
{ "name": "Cat2_Thing3" },
{ "name": "Cat2_Thing4" }
]
}
]
So I would have expected to get an array of "categories" each with an array of "items" which are within that category. Instead, it appears to give me a list of things, sorted by the field I grouped on.
Note: category_name is a column in the thing table.
Try something like
my_grouping = Category.includes(:things).
select("*").
group('categories.id, things.id').
order('name')
=> #<ActiveRecord::Relation [#<Category id: 1, name: "Oranges">, #<Category id: 2, name: "Apples">]>
Though, you'll still have to access the Thing objects via my_grouping.things, they'll already be at your hand, and you won't have to wait for the results. This is likely the sort of interaction you're looking for, vs. mapping them into an actual Array.
One option is to do the grouping in Rails (it returns a hash)
Things.order("name").group_by(&:category_name)
#=> {"cat1" => [thing1,thing2,..], "cat2" => [thing3,thing4,..],..}
ActiveRecord::Base#group performs a SQL GROUP BY. I think, but i'm not sure (depends on your db adapter) that as you don't specify any SELECT clause, you get the first record for each category.
To achieve what you want, there are different ways.
For instance, using #includes :
Category.includes(:things).map do |category|
{
category_name: category.name,
things: things.sort_by(&:name).map{|t| {name: t.name} }
}
end.to_json
Note that the standard (albeit often frowned upon) way to serialize models as json is to use (and override if need be) as_json and to_json. so you would have something along the lines of this :
class Category < ActiveRecord::Base
def as_json( options = {} )
defaults = { only: :name, root: false, include: {links: {only: :name}} }
super( defaults.merge(options) )
end
end
Use it like this :
Category.includes(:links).map(&:to_json)
EDIT
As category_name is only a column, you can do :
Thing.order( :category_name, :name ).sort_by( &:category_name ).map do |category, things|
{ category_name: category, things: things.map{|t| {name: t.name} } }
end.to_json
such thing could belong in the model :
def self.sorted_by_category
order( :category_name, :name ).sort_by( &:category_name ).map do |category, things|
{ category_name: category, things: things.map{|t| {name: t.name} } }
end
end
so you can do :
Thing.sorted_by_category.to_json
this way, you can even scope things further :
Thing.where( foo: :bar ).sorted_by_category.to_json

Date in Mongoid queries

I have a model:
class SimpleAction
include Mongoid::Document
field :set_date, :type => Date
and I have some data in collection:
{ "_id" : ObjectId("4f6dd2e83a698b2518000006"), "name" : "lost",
"notes" : "", "set_date(1i)" : "2012", "set_date(2i)" : "3",
"set_date(3i)" : "25", "set_date(4i)" : "13", "set_date(5i)" : "57",
"duration" : 15, "todo" : "4" }
You can see that mongoid store date in the five fields - set_date(ni).
I have two question:
How can I filter data by set_date field in the mongo console client? Something like this:
db.simple_actions.find({ set_date : { "$lte" : new Date() } })
My query didn't return any data.
How can I filter data by set_date field in my Rails controller? Something like this:
#simple_actions = SimpleAction.where(:set_date => { '$lte' => Date.today })
I would recommend not using Date, but instead DateTime:
field :set_date, :type => DateTime
Now not only will it be stored in 1 field, like so:
"set_date" : ISODate("2012-03-14T17:42:27Z")
But Mongoid will correctly handle various conversions for queries like you want:
SimpleAction.where( :set_date => { :$lte => Date.today } )
You can use it with class Time.
Rails is so good with Api for mobile (such as Android),when you sent date from mobile to Api like: 1482723520. (last_created=1482723520)
You can use it like that:
time = Time.at(params[:last_created].to_i)
And in Rails you can query like that:
Number.where(created_at: { :$gte => time })

Resources