Ruby on Rails where query with relations and updated_at compare - ruby-on-rails

I am trying to use where query with relationships and updated_at compare.
When I send this query with updated_at params, I want to fetch only the latest reasons filtered by updated_at date, but it filters with project's updated_at, not by reasons updated_at.
How can I fix this to get reasons data with updated_at filter?
#Getting reasons with updated_at filter
https://server.com/api/v1/entries?updated_at=2016-05-02T01:12:57.204Z
#Model
User
has_many :projects
has_many :reasons, through: :projects
Project
belongs_to :user
has_many :reasons
Reasons
belongs_to :project
#Reasons Controllers
# GET /reasons
def index
reasons = current_user.reasons
updated_at = params[:updated_at]
# Filter with updated_at for reloading from mobile app
if updated_at.present?
# This is my first try, but it shows error because of relations
# reasons = reasons.where("updated_at > ?", DateTime.parse(updated_at))
# This shows reasons data, but it only compares updated_at in Project data, not reasons data...
reasons = reasons.joins(:project).where("projects.updated_at > ?", DateTime.parse(updated_at))
# Get all non deleted objects when logging in from mobile app
else
reasons = reasons.where(deleted: false)
end
render json: reasons
end

You fetching projects.updated_at, but if I understood you right you need to fetch reasons.updated_at
reasons = reasons.joins(:project).where("reasons.updated_at > ?", DateTime.parse(updated_at))
produses that output for me
GET http://localhost:3000/reasons/index?updated_at=2016-05-04T06:43:19.280Z
[
{
"id": 2,
"name": "R2",
"project_id": 3,
"created_at": "2016-05-04T06:43:19.280Z",
"updated_at": "2016-05-04T06:43:19.280Z",
"deleted": false
},
{
"id": 3,
"name": "R3",
"project_id": 3,
"created_at": "2016-05-04T06:43:25.895Z",
"updated_at": "2016-05-04T06:43:25.895Z",
"deleted": false
}
]
And without any filter
GET http://localhost:3000/reasons/index
[
{
"id": 1,
"name": "R1",
"project_id": 3,
"created_at": "2016-05-04T06:43:11.044Z",
"updated_at": "2016-05-04T06:43:11.044Z",
"deleted": false
},
{
"id": 2,
"name": "R2",
"project_id": 3,
"created_at": "2016-05-04T06:43:19.280Z",
"updated_at": "2016-05-04T06:43:19.280Z",
"deleted": false
},
{
"id": 3,
"name": "R3",
"project_id": 3,
"created_at": "2016-05-04T06:43:25.895Z",
"updated_at": "2016-05-04T06:43:25.895Z",
"deleted": false
}
]

It was just changing to reasons.updated_at, thanks!
reasons = reasons.joins(:project).where("reasons.updated_at > ?", DateTime.parse(updated_at))

Related

Get associated record for each object in ActiveRecord array

I have a collection of Posts, where an Organization has many posts. A post belongs to an Account. The models look like the following:
# Post class
class Post < ApplicationRecord
belongs_to :account, foreign_key: 'account_id'
belongs_to :postable, polymorphic: true
validates :content, length: { in: 1..500 }
end
# Organization
class Organization < ApplicationRecord
has_many :posts, as: :postable
end
I can get the posts of an organization (ie in the console, I can do Organization.first.posts, which returns a shape like:
[
{
"id": 101,
"accountId": 50,
"postableType": "Organization",
"postableId": 3,
"content": "Consequatur in illum vel voluptates.",
"createdAt": "2022-11-23T04:57:45.271Z",
"updatedAt": "2022-11-23T04:57:45.271Z"
},
{
"id": 102,
"accountId": 46,
"postableType": "Organization",
"postableId": 3,
"content": "Fugit totam minus et consequatur.",
"createdAt": "2022-11-23T04:57:45.274Z",
"updatedAt": "2022-11-23T04:57:45.274Z"
},
]
With that query, what would be the best way of including the Account object with every Post returned? I tried doing something like: Organization.first.posts.includes(:account) and Organization.first.posts.joins(:account) but that just returned the same thing. Would it be by simply mapping over and doing a separate query to find the account, and merging that Account object with the Post?
For example, I'd ideally like to return something like:
[
{
"id": 101,
"account": {
"id": 46,
"firstName": "Bob"
},
"postableType": "Organization",
"postableId": 3,
"content": "Consequatur in illum vel voluptates.",
"createdAt": "2022-11-23T04:57:45.271Z",
"updatedAt": "2022-11-23T04:57:45.271Z"
},
{
"id": 102,
"account": {
"id": 46,
"firstName": "Bob"
},
"postableType": "Organization",
"postableId": 3,
"content": "Fugit totam minus et consequatur.",
"createdAt": "2022-11-23T04:57:45.274Z",
"updatedAt": "2022-11-23T04:57:45.274Z"
},
]
I was able to do this by doing:
Organization.first.posts.as_json(include: :account)
I'm not sure if there is a better ActiveRecord-y way of doing this, but it worked for my use-case above.

ActiveRecord - include nested associations instead of just id?

I have a rails api with two ActiveRecords, User and Organization. Organization has_many users and User belongs_to Organization. When I find a user, by their email, I automatically get the organization_id as part of the record. When I render the json for the response, right now I am calling user.as_json(include: :organization) which does include the organization. However, this is what I get:
{
"id": 1,
"name": "My Name",
"organization_id": 1,
"organization": {
"id": 1,
"name": "My Organization"
}
}
How can I structure my query to return this?
{
"id": 1,
"name": "My Name",
"organization": {
"id": 1,
"name": "My Organization"
}
}
user.as_json(include: :organization, except: [:organization_id])

Removing duplicate records after query and before returning data

I'm fairly new to Rails and have been familiarizing myself with the generators, but this is the first time I'm trying to create a custom route.
I have a star schema with the three tables Clients, Products, and Features that each have an n/n relationship to each other, so my "star" junction table has the three foreign keys, client_id, product_id, and feature_id. I created an API that'll return a list of clients, which each have a nested list of products, which each have a nested list of features. Here's the method that builds and returns the result:
def return_all
allData = Client
.includes(:products)
.includes(:features)
.to_json(:include => {:products => {:include => :features}})
render json: allData
end
The issue with this is that there are multiple features for every product, so the resulting JSON returns this (I removed all but one client and some of the data for brevity sake):
{
"id": 13,
"name": "client_1",
"created_at": "2015-10-09T18:44:56.000Z",
"updated_at": "2015-10-09T18:44:56.000Z",
"products": [
{
"id": 1,
"name": "product_1",
"created_at": "2015-10-12T03:10:34.000Z",
"updated_at": "2015-10-12T03:10:34.000Z",
"features": [
{
"id": 9,
"name": "email",
"created_at": "2015-10-12T03:10:55.000Z",
"updated_at": "2015-10-12T03:10:55.000Z",
"value": null,
"group": "channel"
},
{
"id": 10,
"name": "sms",
"created_at": "2015-10-12T03:10:55.000Z",
"updated_at": "2015-10-12T03:10:55.000Z",
"value": null,
"group": "channel"
},
{
"id": 11,
"name": "ivr",
"created_at": "2015-10-12T03:10:55.000Z",
"updated_at": "2015-10-12T03:10:55.000Z",
"value": null,
"group": "channel"
},
{
"id": 12,
"name": "print",
"created_at": "2015-10-12T03:10:55.000Z",
"updated_at": "2015-10-12T03:10:55.000Z",
"value": null,
"group": "channel"
}
]
},
{
"id": 1,
"name": "product_1",
"created_at": "2015-10-12T03:10:34.000Z",
"updated_at": "2015-10-12T03:10:34.000Z",
"features": [
{
"id": 9,
"name": "email",
"created_at": "2015-10-12T03:10:55.000Z",
"updated_at": "2015-10-12T03:10:55.000Z",
"value": null,
"group": "channel"
},
{
"id": 10,
"name": "sms",
"created_at": "2015-10-12T03:10:55.000Z",
"updated_at": "2015-10-12T03:10:55.000Z",
"value": null,
"group": "channel"
},
{
"id": 11,
"name": "ivr",
"created_at": "2015-10-12T03:10:55.000Z",
"updated_at": "2015-10-12T03:10:55.000Z",
"value": null,
"group": "channel"
},
{
"id": 12,
"name": "print",
"created_at": "2015-10-12T03:10:55.000Z",
"updated_at": "2015-10-12T03:10:55.000Z",
"value": null,
"group": "channel"
}
]
}
]
}
How do I go about removing the duplicate listings of products? Please let me know if you need any more information about my question.
Thanks
Edit: by request, here are my models:
class ClientsFeaturesProduct < ActiveRecord::Base
belongs_to :client
belongs_to :feature
belongs_to :product
end
class Client < ActiveRecord::Base
has_many :clients_features_products
has_many :features, through: :clients_features_products
has_many :products, through: :clients_features_products
end
class Product < ActiveRecord::Base
has_many :clients_features_products
has_many :clients, through: :clients_features_products
has_many :features, through: :clients_features_products
end
class Feature < ActiveRecord::Base
has_many :clients_features_products
has_many :clients, through: :clients_features_products
has_many :products, through: :clients_features_products
end
try using
has_many :products, -> { distinct }, through: :clients_features_products
on the Client model
see http://guides.rubyonrails.org/association_basics.html#distinct
A couple of other things
code style: use all_data instead of allData. See the style guide for more info: https://github.com/styleguide/ruby
you might want to use preload instead of includes; includes will either act like preload or like joins and you don't know which, so I tend to stay away from it; good article here: http://blog.arkency.com/2013/12/rails4-preloading/
I would try this, but it's just a guess.
def return_all
allData = Client
.joins(:products) # might need to use raw sql
.joins(:features)
.to_json(:include => {:products => {:include => :features}})
render json: allData
end
Alternatively, you could use Active Model Serializers to explicitly define a json view.

rails multiple .each issue

I am using mongoid with my rails and am having a slight issue with running multiple .each
I have 3 models: Users, `Places', and 'Posts'
Users and Places both has_many Posts and Posts belongs_to both Users and Places
class User
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Paperclip
has_many :posts
}
class Place
include Mongoid::Document
include Mongoid::Timestamps
has_many :posts
}
class Post
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Paperclip
belongs_to :place
belongs_to :user
}
i have a method on my user model to get all the users within my friends, including myself like so:
def self.as_objects_with_self(id)
friends = Array.new
self.as_ids_with_self(id).each do | friend |
friends << User.find(friend)
end
friends
end
This returns back an array of all my friends including myself.
What I am trying to do, is get a list of my friends and myself, and under each user have a list of their posts, and within each post have a reference to the place it is tied to... most of this is working
resources = Friendship.as_objects_with_self(current_user.id)
resources.each do |resource|
if resource.posts.count > 0
resource[:posts] = resource.posts.all
resource[:posts].each do |post|
post[:place] = Place.find(post[:place])
end
end
end
The only part to this that isn't working, is the .each on my :posts. I can see in the logs that it's searching the place table, but nothing is being added to the collection.
It adds this output:
[
{
"_id": "556e26844a75730453170000",
"authentication_token": "Q6R4FNs5i3n1-zfQfbp8",
"created_at": "2015-06-02T21:56:20.684Z",
"dob": "1982-08-01",
"email": "test1#gmail.com",
"gender": "Male",
"last_action": null,
"name": "tester1",
"picture_content_type": null,
"picture_file_name": null,
"picture_file_size": null,
"picture_fingerprint": null,
"picture_updated_at": null,
"picture_url": "/pictures/original/missing.png",
"posts": [
{
"_id": "556e32b54a75730453270000",
"created_at": "2015-06-02T22:48:21.962Z",
"media_content_type": null,
"media_file_name": null,
"media_file_size": null,
"media_fingerprint": null,
"media_updated_at": null,
"picture_content_type": "image/jpeg",
"picture_file_name": "8580243774_479d5fe7bf_z.jpg",
"picture_file_size": 191938,
"picture_fingerprint": "61bdc1d21158c76d601b028bf826b437",
"picture_updated_at": "2015-06-02T22:48:21.768+00:00",
"place_id": "556cd5dc4a75730453010000",
"type": "picture",
"updated_at": "2015-06-02T22:48:21.962Z",
"user_id": "556e26844a75730453170000"
},
{
"_id": "556e351f4a75730453280000",
"created_at": "2015-06-02T22:58:39.761Z",
"media_content_type": null,
"media_file_name": null,
"media_file_size": null,
"media_fingerprint": null,
"media_updated_at": null,
"picture_content_type": "image/jpeg",
"picture_file_name": "8580243774_479d5fe7bf_z.jpg",
"picture_file_size": 191938,
"picture_fingerprint": "61bdc1d21158c76d601b028bf826b437",
"picture_updated_at": "2015-06-02T22:58:39.571+00:00",
"place_id": "556cd5dc4a75730453010000",
"type": "picture",
"updated_at": "2015-06-02T22:58:39.761Z",
"user_id": "556e26844a75730453170000"
}
],
"telephone": "5555555555",
"thumb_picture_url": "/pictures/thumbnail/missing.png",
"updated_at": "2015-06-02T21:56:20.705Z",
"username": "tester1"
},
{
"_id": "556cd5934a75730453000000",
"authentication_token": "RErefcuH5eDsSaNw6gCB",
"created_at": "2015-06-01T21:58:43.198Z",
"dob": "1982-08-01",
"email": "test#gmail.com",
"gender": "Male",
"last_action": null,
"name": "tester",
"picture_content_type": null,
"picture_file_name": null,
"picture_file_size": null,
"picture_fingerprint": null,
"picture_updated_at": null,
"picture_url": "/pictures/original/missing.png",
"telephone": "5555555555",
"thumb_picture_url": "/pictures/thumbnail/missing.png",
"updated_at": "2015-06-01T21:58:43.200Z",
"username": "tester"
}
]
Your issue is not with each. It's either with Place.find or with resource[:posts]
Try to replace
post[:place] = Place.find(post[:place])
with
post[:place] = Place.find(post[:place_id])
--- edit ----
replace
resource[:posts].each do |post|
post[:place] = Place.find(post[:place])
end
with
resource[:posts].map! do |post|
post[:place_id] = Place.find(post[:place_id])
post
end
or
resource[:posts].each.with_index do |post, i|
resource[:posts][i][:place] = Place.find(post[:place_id])
end
or
resource.posts.each do |post|
post[:place] = Place.find(post.place)
end

Ruby: Outputting Sequel model associations

I don't think this is possible using just Sequel models, but what I would like to do is have my parent model (Author) output its child model (Book) when I do something like Author.to_json. Here is my code:
require 'sequel'
require 'json'
db = Sequel.connect('postgres://localhost/testing');
class Sequel::Model
self.plugin :json_serializer
end
class Author < Sequel::Model(:author)
one_to_many :book, key: :author_id, primary_key: :id
def get_author
Author.each do |e|
books = Array.new
e.book.each do |f|
books.push(f.values)
end
e.values[:books] = books
puts JSON.pretty_generate(e.values)
end
end
end
class Book < Sequel::Model(:book)
end
author = Author.new
author.get_author
My output looks something like this:
[{
"id": 1,
"name": "Jack Johnson",
"books": [{
"id": 4,
"name": "Songs with Chords",
"genre": "Learning",
"author_id": 1
}]
}, {
"id": 2,
"name": "Mulder",
"books": [{
"id": 2,
"name": "UFOs",
"genre": "Mystery",
"author_id": 2
}, {
"id": 3,
"name": "Unexplained Paranorma",
"genre": "Suspense",
"author_id": 2
}]
}, {
"id": 3,
"name": "Michael Crichton",
"books": [{
"id": 1,
"name": "Jurassic Park",
"genre": "Incredible",
"author_id": 3
}]
}]
That's exactly how I want my output to look, but the way I'm going about it is questionable. Ideally, if there's some function already on the Author model that allows me to do this, that'd be awesome... as I don't want to have to implement a get_model function for all of my models that have different associations. Also, I was hoping NestedAttributes could lend a hand here, but it doesn't look like it.
I'm very new to Ruby and Sequel, so I'd like to know if there's a simpler way of doing this?
Sequel's json_serializer plugin already has support for associations via the :include option. Also, you need to fix your association. Something like this should work:
Author.one_to_many :books
Author.order(:id).to_json(:include=>:books)

Resources