Mongoid find or create with embedded hash - ruby-on-rails

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}
})

Related

Hash with 3 different keys each pointing to an array of instances. How do I sort the arrays by id?

I have a hash like so:
def my_requests
result = {
accepted: [],
rejected: [],
pending: [],
}
self.requests.each do |request|
serialized_request = RequestSerializer.new(request)
if request.accept == nil
result[:pending].push(serialized_request)
elsif request.accept
result[:accepted].push(serialized_request)
else
result[:rejected].push(serialized_request)
end
end
result
end
I will have a logged in user. I am trying to organize the logged in user's availabilities by id.
How do I sort each array by id. I know if this was just an array I can do:
array.sort_by{|request| request.id}
But how do I iterate through each key's array? I've tried multiple different ways and the only one that works is if I end up mapping over the hash and then another loop to sort the requests. But that doesn't return a hash. Is there a way for me to keep the structure and sort it?
The availabilities serializer is below:
class RequestSerializer < ActiveModel::Serializer
attributes :id, :start_time, :payment, :number_of_hours, :availability_id, :date, :name, :accept, :postcode, :phone_number
end
Below is one of the key, value pair outputs.
:rejected=>[#<RequestSerializer:0x00007fa416e168a8 #object=#<Request id: 64, payment: 200, number_of_hours: 20, accept: false, start_time: "2000-01-01 16:20:00", venue_id: 1, availability_id: 4, created_at: "2020-08-30 12:15:04", updated_at: "2020-08-30 12:15:52">, #instance_options={}, #root=nil, #scope=nil>, #<RequestSerializer:0x00007fa416e167b8 #object=#<Request id: 4, payment: 160, number_of_hours: 4, accept: false, start_time: "2000-01-01 16:15:00", venue_id: 2, availability_id: 5, created_at: "2020-06-17 21:19:07", updated_at: "2020-06-17 21:21:32">, #instance_options={}, #root=nil, #scope=nil>, #<RequestSerializer:0x00007fa416e166c8 #object=#<Request id: 71, payment: 100, number_of_hours: 1, accept: false, start_time: "2000-01-01 09:45:00", venue_id: 1, availability_id: 6, created_at: "2020-10-01 08:45:43", updated_at: "2020-10-01 08:46:04">, #instance_options={}, #root=nil, #scope=nil>, #<RequestSerializer:0x00007fa416e16560 #object=#<Request id: 66, payment: 30, number_of_hours: 3, accept: false, start_time: "2000-01-01 16:30:00", venue_id: 1, availability_id: 26, created_at: "2020-08-30 12:31:02", updated_at: "2020-08-30 12:32:10">, #instance_options={}, #root=nil, #scope=nil>, #<RequestSerializer:0x00007fa416e163f8 #object=#<Request id: 68, payment: 20, number_of_hours: 3, accept: false, start_time: "2000-01-01 12:00:00", venue_id: 1, availability_id: 28, created_at: "2020-09-01 08:17:26", updated_at: "2020-09-01 13:09:54">, #instance_options={}, #root=nil, #scope=nil>]
Thanks!
result.transform_values { |array| array.sort_by(&:request_id) }
If the arrays are not not arrays of requests, but RequestSerializer, just call .object on them to get the request to sort by.
result.transform_values do |array|
array.sort_by { |serializer| serializer.object.request_id }
end
Another option would be to define request_id on RequestSerializer
You have to sort each hash value separately:
result.each_value { |array| array.sort_by!(&:id) }
Hash#each_value traverses the values and sort_by! sorts the array in-place.
If you need to create a new sorted copy:
result.each_with_object({}) do |(key, value), list|
list[key] = value.sort_by(&:id)
end
As Stefan posted in the comments. I can use a database query to order the requests prior to splitting them into their arrays.
The answer that worked (without doing multiple loops) was:
def my_requests
result = {
accepted: [],
rejected: [],
pending: [],
}
requests.order(:id).each do |request|
serialized_request = RequestSerializer.new(request)
if request.accept == nil
result[:pending].push(serialized_request)
elsif request.accept
result[:accepted].push(serialized_request)
else
result[:rejected].push(serialized_request)
end
end
result
end
Simply by removing self. and using the .order query and the id attribute, everything comes out ordered!
Thanks Stefan! (and everyone else)
(Others worked with multiple methods or loops but as I was using Rails, the query is the quickest and easiest).

Rails, Mongoid, Group by date, month, year on created_at, Line chart

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: ...)>,]}

Check if a value created_at not equal to current date exists in an array in Ruby

I have a collection of user.paid_subscriptions in which each subscription has attributes created_at(datetime) and active(boolean).
How can I check if a PaidSubscription exists such that created_at is not equal to a certain date and active is true?
PaidSubscription looks like this:
[
#<PaidSubscription id: 11457,
user_id: 12,
period: 3,
price: 4000,
expires_at: "2016-03-08 09:44:56",
expires_at: "2016-03-08 09:44:56",
created_at: "2015-12-08 09:44:56",
updated_at: "2016-03-08 23:00:09",
active: false,
giver_id: 20573,
partial: false,
remaining_days: 0>,
#<PaidSubscription id: 13948,
user_id: 12,
period: 1,
price: 1500,
expires_at: "2016-04-11 12:07:40",
created_at: "2016-03-11 13:07:40",
updated_at: "2016-04-11 22:00:11",
active: false,
giver_id: nil,
partial: false,
remaining_days: 0>,
#<PaidSubscription id: 11458....
]
Try this,
If it is a query then it should be like this
user.paid_subscriptions.where("created_at < :date or created_at > :date and active = :active",{date: DateTime.civil(yyyy,mm,dd), active: true})
Or
if it is an array you can use it like below.
user.paid_subscriptions.any? {|ps| ps.active && ( ps.created_at.to_date < Date.civil(yyyy, mm, dd) || ps.created_at.to_date > Date.civil(yyyy, mm, dd))}

Rails 4 scoped model with association also scoped

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 })
}

Rails 4 Partial with Collection

I am doing something obviously wrong here, but I don't get it.
I have a partial call like so:
= render 'shared/purchase', collection: #purchases
#purchases is defined in the controller like so:
#purchases = current_user.purchases
But I get this error:
app/views/shared/_purchase.html.slim where line #3 raised:
undefined local variable or method `purchase' for #<<Class:0x007fd8ca54f970>:0x007fd8ca54eb60>
However, when I change the partial to just render the local_assigns I see this:
[:collection, #<ActiveRecord::Associations::CollectionProxy [#<Purchase id: 40, user_id: 20, purchaseable_id: 6, purchaseable_type: "PurchaseType", frequency: "Weekly", day: "Su", notes: "leave at gate", allergies: "peanuts", created_at: "2013-07-24 16:58:08", updated_at: "2013-07-24 16:58:08", size: "Normal", quantity: 2>, #<Purchase id: 41, user_id: 20, purchaseable_id: 5, purchaseable_type: "PurchaseType", frequency: "Weekly", day: "Su", notes: "leave at gate", allergies: "peanuts", created_at: "2013-07-24 16:58:08", updated_at: "2013-07-24 16:58:08", size: "Jumbo", quantity: 3>, #<Purchase id: 42, user_id: 20, purchaseable_id: 7, purchaseable_type: "PurchaseType", frequency: "Weekly", day: "Su", notes: "leave at gate", allergies: "peanuts", created_at: "2013-07-24 16:58:08", updated_at: "2013-07-24 16:58:08", size: nil, quantity: 1>, #<Purchase id: 43, user_id: 20, purchaseable_id: 8, purchaseable_type: "PurchaseType", frequency: "Weekly", day: "Su", notes: "leave at gate", allergies: "peanuts", created_at: "2013-07-24 16:58:08", updated_at: "2013-07-24 16:58:08", size: nil, quantity: 1>]>]
[:purchase, nil]
Why isn't each collection item getting picked up as the local variable purchase?
I'm tearing my hair out, I'm sure it's something stupid, I've done this many times before, and even compared old working code to this and I can't figure out what the problem is. Either it's a new Rails 4 thing that I'm not seeing in the docs, or I'm an idiot.
Thanks!
The Rails documentation on Layouts and Render in Rails gives an example using the partial parameter for render:
<h1>Products</h1>
<%= render partial: "product", collection: #products %>

Resources