I need to get Array response from rails moingo cipher query.
Group By date
[["Mar 26, 2016", 5],["Mar 27, 2016", 5],["Mar 29, 2016",8],["Mar 30, 2016",5],["Apr 1, 2016",5]]
Group by year
[["2013", 15],["2014", 225],["2015",8],["2016",5],["2017",5]]
I have done same thing with ruby group_by and map, But need to do with mongo query.
If you want to use mongo query for group_by, you can use aggregation.
You can read more about aggregation from mongo official doc
https://docs.mongodb.com/manual/reference/operator/aggregation/month/
example:
ModalName.collection.aggregate([ { "$group": { _id: { month: {"$month": "$created_at"}, year: {"$year": "$created_at"} }, count: { "$sum": 1 } } } ] ).to_a
You should use group_by(&:field_name)
#Model
class Flow
include Mongoid::Document
field :title, type: String
field :category_name, type: String
end
# Rails console
Flow.all.group_by(&:category_name)
# Result
{"album"=>
[#<Flow _id: ...)>,]}
Related
I'm trying to sort/order my records to display them in Chart.js
I want to display meal orders from Monday to Sunday with ascending order, there are 3 different meals every day. It's kinda working if every meal if present on each day, but if one kind of meal isn't ordered, for example, the order of the days gets weird.
Here is an example where it isn't working as I intended:
So as you see (don't mind the german language :D) the starting is right Monday, ... Thursday, ... Saturday, that's fine, but the order on Friday (the green one) is another meal as on the other days and so Friday is shown after Saturday, but I want it to be shown between Thursday and Saturday.
Here are my queries for ActiveRecord:
def self.weeklyVollkostOverview
where(day: Date.today.beginning_of_week..Date.today.end_of_week).where(dish_type: 0).group(:day).order("day ASC").count
end
def self.weeklySchonkostOverview
where(day: Date.today.beginning_of_week .. Date.today.end_of_week).where(dish_type: 1).group(:day).order("day ASC").count
end
def self.weeklyVegetarischOverview
where(day: Date.today.beginning_of_week .. Date.today.end_of_week).where(dish_type: 2).group(:day).order("day ASC").count
end
In my controller, I'm mapping the results:
#weeklyVoll = OrderCard.weeklyVollkostOverview.map { |key, value| [I18n.l(key, format: "%A", locale: :'de'), value]}
#weeklySchon = OrderCard.weeklySchonkostOverview.map { |key, value| [I18n.l(key, format: "%A", locale: :'de'), value]}
#weeklyVegetarisch = OrderCard.weeklyVegetarischOverview.map { |key, value| [I18n.l(key, format: "%A", locale: :'de'), value]}
Finally, I display the results in my chart.
Is there anything I could do different or that's easier?
I know I could use Groupdate-Gem, but there's an issue for date objects and time zone conversion, so I'm trying to do it manually.
EDIT:
The variables #weeklyVoll, #weeklySchon, and #weeklyVegetarisch are for display purposes in my view where the charts are:
<%= column_chart [
{ name: "# Bestellungen Vollkost", data: #weeklyVoll },
{ name: "# Bestellungen Schonkost", data: #weeklySchon },
{ name: "# Bestellungen Vegetarisch", data: #weeklyVegetarisch }
],
colors: ["red", "yellow", "green"],
xtitle: "Wochentag",
ytitle: "# Bestellungen",
legend: true,
legend: "bottom",
messages: { empty: "Noch keine Daten vorhanden!" },
library: { scales: { yAxes: [{ gridLines: { display: true }, ticks: { precision: 0 } } ] }, animation: {easing: 'easeOutQuad', duration: 2500} }
%>
Any help is appreciated! Thanks!
I think the issue is having the three different arrays. If you were to group on day/dish_type and then call count at the end it should give a results like ['Day', 'Dish Type'] => 3. You would then have to iterate through and format again as needed. I tend to use each_with_object.
OrderCard.where(day: Date.today.all_week)
.group(:day, :dish_type).count
# => ['Day', 'Dish Type'] => 3
I have classes that look like this:
class Orders < ActiveRecord::Base
has_many :items
has_many :types, through: :items
accepts_nested_attributes_for :items
end
class Item < ActiveRecord::Base
belongs_to :type
belongs_to :order
end
class Type < ActiveRecord::Base
has_many :items
end
An Order has several items on it, here's an overview and item and type:
<Item:0x00007fbcb185c170
id: 1,
finish: "Fir",
size: "38",
type_id: 8,
order_id: 1>
<Type:0x00007fbcace363e8
id: 8,
type: "Door",
name: "Jeldwen Premium Interior">
Items might have the same type but different finish and size attributes. I need to grab an order from the database and present them in a human readable format, something like this:
Order: {
Types:
{
Name: "Jeldwen Premium Interior",
Type: "Door",
Items:
{
finish: "fir",
size: "36",
quanity: "8" # sum of items in the database with same `finish` and `size`
},
{
finish: "fir",
size: "58",
quanity: "8" # sum of items in the database with same `finish` and `size`
}
}
I would like to be able to get all this information with one db call and I got part of the way there, I'm just missing type name-
`Order.where(id: 1).joins(:items).joins(:type).group("items.type_id", "items.size", "items.color").count`
Returns the type_id, size, finish, and quanity:
{[3, "36", "fir"]=>40,
[3, "48", "fir"]=>36,
[3, "36", "oak"]=>90,
[4, "48", "oak"]=>18}
I would like to return type.name and type.name with the count, is that possible? So a row would look something like:
["Jeldwen Premium Interior", "door", 3, "36", "fir"]=>40
I had this issue yesterday and I ended up using find_by_sql
you can test the sql in your sql console or at the following site
Your rails query
.joins(:items).joins(:type).group("items.type_id", "items.size", "items.color").count
can be done with sql
SELECT items.type_id, items.size, items.color, types.name, COUNT(types.id) AS NumberOfTypes, COUNT(types.name) AS NumberOfNames FROM items INNER JOIN types on items.id = types.item_id GROUP BY items.type_id, items.size, items.color;
in a scope
class Item
scope :items_count, -> { find_by_sql("SELECT items.type_id, items.size, items.color, types.name, COUNT(types.id) AS NumberOfTypes, COUNT(types.name) AS NumberOfNames FROM items INNER JOIN types on items.id = types.item_id GROUP BY items.type_id, items.size, items.color;") }
end
and you can call
items = Order.first.items_count
it will return an array of items, without count and without types. Something like:
=> [#<Item:0x00564d6bf08c28 id: nil, type: "type1test", size: 133, color: 'blue'>,
#<Item:0x00564d6bef7108 id: nil, type: "type1test", size: 136, color: 'blue'>,
#<Item:0x00564d6bef6e88 id: nil, type: "type1test", size: 137, color: 'blue'>,
#<Item:0x00564d6bef6cf8 id: nil, type: "type1test", size: 132, color: 'blue'>,
#<Item:0x00564d6bef6af0 id: nil, type: "type1test", size: 141, color: 'blue'>,
#<Item:0x00564d6bef68c0 id: nil, type: "type1test", size: 135, color: 'blue'>]
the items array will not have the types, but it will include a column numberofitems with the result of COUNT
items[0].numberoftypes
=> 6
items[0].numberofnames
=> 4
Anyway this will not work, because you can not GROUP BY different fields in different tables and have 2 COUNT at the same time.
You can try with sql to test it in this console and see that it is not possible.
It will not return the correct number of count for one of your columns, but this technique it is better then the one you are using right now, because you do not need to do 2 JOINS and it will return the objects.
SQL is very easy and you can refer to https://www.w3schools.com/sql/ to better understand any of the sql included in http://guides.rubyonrails.org/active_record_querying.html
Would anyone be willing to give me advice on how I can improve the performance of the following controller method?
def index
#contacts = Hash[current_user.company.contacts.map {|contact| [contact.id, ContactSerializer.new(contact).as_json[:contact]] }]
respond_to do |format|
format.json { render json: { contacts: #contacts } }
end
end
This returns the following data structure:
{
contacts: {
79: {
id: 79,
first_name: "Foo",
last_name: "Bar",
email: "t#t.co",
engagement: "0%",
company_id: 94,
created_at: " 9:41AM Jan 30, 2016",
updated_at: "10:57AM Feb 23, 2016",
published_response_count: 0,
groups: {
test: true,
test23: false,
Test222: false,
Last: false
},
invites: [
{
id: 112,
email: "t#t.co",
status: "Requested",
created_at: "Jan 30, 2016, 8:48 PM",
date_submitted: null,
response: null
}
],
responses: [ ],
promotions: [
{
id: 26,
company_id: 94,
key: "e5cb3bc80b58c29df8a61231d0",
updated_at: "Feb 11, 2016, 2:45 PM",
read: null,
social_media_posts: [ ]
}
]
},
81: {
id: 81,
first_name: "Foo2",
last_name: "Bar2",
email: "foobar2#foobar.com",
engagement: "0%",
company_id: 94,
created_at: "12:55PM Feb 04, 2016",
updated_at: " 4:25PM Feb 19, 2016",
published_response_count: 0,
groups: {
test: true,
test23: true,
Test222: false,
Last: false
},
invites: [
{
id: 116,
email: "foobar2#foobar.com",
status: "Requested",
created_at: "Feb 22, 2016, 9:10 PM",
date_submitted: null,
response: null
}
],
responses: [ ],
promotions: [
{
id: 26,
company_id: 94,
key: "e5cb3bc80b58c29df8a61231d0",
updated_at: "Feb 11, 2016, 2:45 PM",
read: null,
social_media_posts: [ ]
}
]
}
}
}
I need the index method to return a hash where the the keys are the contact IDs, as opposed to an Array, which is what would normally be returned. Additionally, I pass each contact through the serializer so that I get all associated data that my client needs.
This method works fine when there are only a few contacts, however when I have 100 or 1000, it really slows down. I benchmarked it with 100 contacts and it took 4 seconds to finish, which is abysmal. I'm wondering how I can improve my code to get the exact same output in a more performant manner. The key here is that the output needs to remain unchanged. I have no interest in modifying the client-side code (it depends on this data structure for numerous applications), so all changes need to occur on the server-side.
Here is my ContactSerializer for reference:
class ContactSerializer < ActiveModel::Serializer
attributes :id, :first_name, :last_name, :email, :engagement, :company_id, :created_at, :updated_at, :published_response_count, :groups
has_many :invites
has_many :responses
has_many :promotions
def groups
Hash[object.company.groups.map {|group| [group.name, object.groups.include?(group)] }]
end
def published_response_count
object.responses.where(published: true).count
end
def created_at
object.created_at.in_time_zone("Eastern Time (US & Canada)").strftime("%l:%M%p %b %d, %Y")
end
def updated_at
object.updated_at.in_time_zone("Eastern Time (US & Canada)").strftime("%l:%M%p %b %d, %Y")
end
def engagement
object.engagement
end
end
For what it's worth, I am fully aware that returning JSON data like this from Rails is not a great practice and have since moved away from it completely. Unfortunately this piece of code was written quite awhile ago and I can't afford the time to do a full rewrite of the client side to consume a standard output such as an array of contacts.
I started looking into the queries that we're being generated by ActiveRecord.
I realized after a while though that the queries were only accounting for a few ms of the total processing time. I started benchmarking the code starting with the base query company.contacts and then gradually adding on methods I had, such as map and then passing each contact into the serializer, and finally calling as_json[:contact] on the object returned by ContactSerializer.new(contact).
What I found is that calling as_json[:contact] on the serialized object was consuming an average of about 30ms (averaged over 100 runs) per contact. The reason I had that was to remove the root contact node from the JSON that was returned.
When I benchmarked my original code with 198 contacts, it took an average of 10400ms over 10 runs. When I removed as_json[:contact] and set root false on ContactSerializer, as described by "Abusing ActiveModel::Serializers for HAL", I was able to cut the time down from 10400ms to 87ms, while returning the exact same structure to the client, which is astounding.
It might be possible to shave a few ms off that with some query optimizations, but anything else at this point is just icing on the cake.
Using Mongoid (3.1.6)
I have a collection with the following fields:
field :project, type: String
field :date_hierarchy, type: Hash, default: {year: 0, semester: 0, quarter: 0, month: 0, day: 0}
I want to find a document with project: "1", date_hierarchy: {year: 2013, semester:2, quarter: 4, month: 11, day: 12}
and if the document is not found the i want it to be created
How can I do this?
I have tried these and non of them work
MyModel.where(:project=>"1", "date_hierarchy.year"=>2013, "date_hierarchy.semester"=>2, "date_hierarchy.quarter"=>4, "date_hierarchy.month"=>11, "date_hierarchy.day"=>25).first_or_create!
MyModel.where(:project=>"1", "date_hierarchy.year"=>2013, "date_hierarchy.semester"=>2, "date_hierarchy.quarter"=>4, "date_hierarchy.month"=>11, "date_hierarchy.day"=>25).find_or_create_by("date_hierarchy.year"=>2013, "date_hierarchy.semester"=>2, "date_hierarchy.quarter"=>4, "date_hierarchy.month"=>11, "date_hierarchy.day"=>25).
You need pass the date_hierarchy value as a hash, as you would pass to new() or create():
MyModel.find_or_create_by({
project: '1',
date_hierarchy: {year: 2013, semester: 2, quarter: 4, month: 11, day: 25}
})
I have 2 models, Student(has_many schedules) and Schedule and what I want is to find All students that have a active schedule today.
What I have now:
Student model:
scope :training_today, -> { joins(:schedules) & Schedule.active & Schedule.from_today }
Both schedule scopes work fine. See here:
$ Schedule.active.from_today
Schedule Load (1.4ms) SELECT "schedules".* FROM "schedules" WHERE "schedules"."active" = 't' AND "schedules"."day" = 4 ORDER BY day ASC
=> [#<Schedule id: 47, student_id: 2, hour: "11", active: true, created_at: "2014-05-18 23:26:34", updated_at: "2014-06-05 19:04:02", day: 4>,
#<Schedule id: 5, student_id: 1, hour: "08:00", active: true, created_at: "2014-05-16 02:54:21", updated_at: "2014-06-05 20:50:07", day: 4>]
What I want:
From my Student model I want to find only those from the query above(ids 1 and 2). Using the scope Student.training_today I get nothing(empty array). How can I get the correct students ?
I don't know what represents the day value in your Database, but this code implies that is it the week-day of this date:
scope :training_today, -> {
includes(:schedules).where(schedules: { active: true, day: Date.current.wday })
}