Get associations when doing Get request - ruby-on-rails

I am working on ruby on rails as my API with mongoid. Suppose I have 2 models:
class Human
field: salary, type: Integer
has_many: dogs
end
class Dog
field: name, type: String
belongs_to: human
end
I want to get all dogs that human has when I query all humans, how do I do this?
I know embedded documents can do this, but then I cannot find dog document easily. That is why I use association rather than embedded document.
Expected output when I query all human:
[
{
"_id": "1",
"salary": 5000,
"dogs": [
{
"_id": "1",
"name": "dog1",
}
]
},
{
"_id": "2",
"salary": 8000,
"dogs": [
{
"_id": "2",
"name": "dog2",
},
{
"_id": "3",
"name": "dog3",
}
]
}
]
Thanks in advance. I am very new on this and sorry if I asked some stupid questions.

humans = Human.includes(:dog)
humans consists of the required data with each individual containing the dogs they own with details such as their name.

Related

Neo4j recursive cypher query resulting in nested JSON structure

I am trying to figure out cypher query in order to get nested JSON structure as a result. Below I present an example of the graph.
MATCH (user:User {name:"User_1"})
OPTIONAL MATCH (user)-[rel*]->(subUser:User)
RETURN *
Query above allows me to get all the nodes and relationships required to transform everything to JSON structure I want but that requires me to process everything after getting the result from querying the database. To achieve that I need to match identity of nodes and relationship in order to get the nested JSON.
I was wondering if it is possible to achieve that directly from building cypher query.
Important thing is that we do not know how many levels of "child" Users we have starting from User_1
Expected JSON structure:
{
"user": "User_1",
"children": [
{
"user": "User_2",
"children": [
{
"user": "User_5",
"children": []
}
]
},{
"user": "User_3",
"children": [
{
"user": "User_6",
"children": []
}
]
},{
"user": "User_4",
"children": []
}
]
}
Is it possible?
As suggested in the comments by #nimrod serok, you can use the apoc.convert.toTree method, it will give you the tree-structured JSON, as desired, with one caveat, the keys of the JSON will be different. For the data:
MERGE (u1:User{name: 'User1'})
MERGE (u2:User{name: 'User2'})
MERGE (u3:User{name: 'User3'})
MERGE (u4:User{name: 'User4'})
MERGE (u5:User{name: 'User5'})
MERGE (u6:User{name: 'User6'})
MERGE (u1)-[:POINTS]->(u2)-[:POINTS]->(u5)
MERGE (u1)-[:POINTS]->(u3)-[:POINTS]->(u6)
MERGE (u1)-[:POINTS]->(u4)
The query:
MATCH (user:User {name:"User1"})
OPTIONAL MATCH path = (user)-[:POINTS*]->(subUser:User)
WITH collect(path) AS paths
CALL apoc.convert.toTree(paths, true, {nodes: {User: ['name']}})
YIELD value
RETURN value
produces the output:
{
"_type": "User",
"name": "User1",
"_id": 4,
"points": [
{
"_type": "User",
"name": "User3",
"_id": 6,
"points": [
{
"_type": "User",
"name": "User6",
"_id": 9
}
]
},
{
"_type": "User",
"name": "User2",
"_id": 5,
"points": [
{
"_type": "User",
"name": "User5",
"_id": 8
}
]
},
{
"_type": "User",
"name": "User4",
"_id": 7
}
]
}
as you can see, the relationship type POINTS, comes in place of children, and the key name comes for the user name. The other fields _type and _id can be ignored.
apoc.convert.toTree() is certainly the best answer for the question you asked.
If one is interested in a text output then ORDPATH would be another solution. ORDPATH is a concatenated bitstring which sorts in hierarchical order. More on this at this link. A Neo4j user defined function implementing this is at GitHub.

Rails Serialization - fast_jsonapi / active_model_serializers

I am a bit lost getting on board fast_jsonapi / active_model_serializers to build an API. I have the basics down but seem stuck on a custom solution.
I have this as a serializer:
class AreaSerializer
include FastJsonapi::ObjectSerializer
attributes :id, :name, :cost_center, :notes
has_many :children
end
In my area model I have:
has_many :children, -> { Area.where(ancestry: id) }
My controller looks like:
class Api::V1::AreasController < ApiController
def index
render json: AreaSerializer.new(Area.root).serialized_json
end
end
Areas are nested in a hierarchy with the ancestry gem. The output is:
{
"data": [{
"id": "1",
"type": "area",
"attributes": {
"id": 1,
"name": "Calgary",
"cost_center": "123456",
"notes": ""
},
"relationships": {
"children": {
"data": [{
"id": "3",
"type": "child"
}]
}
}
}, {
"id": "2",
"type": "area",
"attributes": {
"id": 2,
"name": "Edmonton",
"cost_center": "78946",
"notes": ""
},
"relationships": {
"children": {
"data": []
}
}
}]
}
I am looking for an out put like this:
{
"data": [{
"id": "1",
"type": "area",
"attributes": {
"id": 1,
"name": "Calgary",
"cost_center": "123456",
"notes": ""
},
"relationships": {
"areas": {
"data": [{
"id": "3",
"type": "area",
"attributes": {
"id": 3,
"name": "Child Area",
"cost_center": "123456",
"notes": ""
}
}]
}
}
}, {
"id": "2",
"type": "area",
"attributes": {
"id": 2,
"name": "Edmonton",
"cost_center": "78946",
"notes": ""
}
}]
}
The idea being where the nested relationship shows the details etc.
I just started using fast_jsonapi in my rails project.
fast_jsonapi adheres to JSON:API spec which you can see here.
So, you will not be able to use the relationship helper functions (:has_many, :belongs_to) to achieve the output you want, which is nesting the attributes of area inside the relationships key.
"relationships": {
"areas": {
"data": [{
"id": "3",
"type": "area",
"attributes": { // nesting attributes of area
"id": 3,
"name": "Child Area",
"cost_center": "123456",
"notes": ""
}
}]
}
}
JSON:API specifies that:
To reduce the number of HTTP requests, servers MAY allow responses
that include related resources along with the requested primary
resources. Such responses are called “compound documents”.
So instead, you will have the attributes of area inside a key called included.
In order to get the included key in your response, you will need to provide an options hash in your controller to the serializer.
I don't quite understand your model Area relationships, but assume an Area has many SubArea.
class Api::V1::AreasController < ApiController
def index
// serializer options
options = {
include: [:sub_area],
collection: true
}
render json: AreaSerializer.new(Area.root, options).serialized_json
end
end
You Cannot use Association in fast_jsonapi . To Get a response in nested format . You need to add methods and need to create another serializer .
class AreaSerializer
include FastJsonapi::ObjectSerializer
set_type 'Area'
attributes :id, :name, :cost_center, :notes
attribute :childrens do |area, params|
ChildrenSerializer.new(area.childrens, {params:
params})
end
end
class ChildrenSerilizer
include FastJsonapi::ObjectSerializer
set_type 'Area'
attributes :id, :name ...
end
I started using the technique listed above but ended up forking and re-writing the jsonapi-serializer gem so it allows nesting (up to 4 levels deep) and does away with the concept of having relationships and attributes keys. I was also frustrated that it only output ID and TYPE keys of which TYPE is redundant most of the time since the object typically stays the same class in an array as an example and people seldom use real polymorphism (although it still supports this and outputs ID/type for polymorphic relationships).
Probably the best part of my re-write is that it allows deterministic field select-ability anywhere within the json key tree via a new fields input.
https://github.com/rubesMN/jsonapi-serializer

fill a JSON file with embedded arrays from Rails

I have a simple "rss" (ApplicationRecord) table indexed by an id. I would like to have a structured JSON that group each user from a family in an array structure. And then each family in a global array. How can I do that ?
my current plain code to put my data in a json file is :
json.rss #rss do |rs|
json.id rs.id
json.name rs.name
json.family rs.family
json.lastdate rs.lastdate
json.last rs.last
json.s1w rs.s1w
json.s2w rs.s2w
end
But the target file that I want is this one :
{
"rss": [
{
"familyname": "Smith",
"children": [
{
"id": "1",
"name": "bob",
"lastdate": "2010-09-23",
"last": "0.88",
"s1w": "0.83",
"s2w": "0.88"
},
{
"id": 2,
"name": "Mary",
"lastdate": "2011-09-23",
"last": "0.89",
"s1w": "0.83",
"s2w": "0.87"
}
]
},
{
"familyname": "Wesson",
"children": [
{
"id": "1",
"name": "john",
"lastdate": "2001-09-23",
"last": "0.88",
"s1w": "0.83",
"s2w": "0.88"
},
{
"id": 2,
"name": "Bruce",
"lastdate": "2000-09-23",
"last": "0.89",
"s1w": "0.83",
"s2w": "0.87"
}
]
}
]
}
The grouping you are trying to achieve can be done in Ruby with:
#rss.group_by(&:family).values
This is assuming #rss is an array-like collection of objects that have a .family method. The result: is an array of arrays of objects grouped by family.
Now it will be up to use to use Jbuilder's array! method to build the desired JSON output.

Can a json response can be partially paginate?

I'm wondering if a json can be partially paginate.
For example
{
"data": [{
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON API paints my bikeshed!",
"body": "The shortest article. Ever."
}
}],
"included": [
{
"type": "people",
"id": 42,
"attributes": {
"name": "John"
}
},
{
...annnd 80000 others
}
}
]
}
Where included have soo many elements (80.000 for examples) than maybe we need pagination?
But if it's paginate and we go on the next page only included elements will change, the json will still return the data.articles.
Is it a correct behavior ?
First proposal :
{
"data": [{
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON API paints my bikeshed!",
"body": "The shortest article. Ever."
},
"relationships": {
"users": {
"link": "https://website.com/api/v1/articles/1/users.json"
}
}
}]
}
To be compliant with the JSON API spec, your compound document must obey the full linkage requirement. Any included resources MUST be identified via relationship data.
In your example, you could fulfill this by adding a data member under the users relationship. You could then link to every included person.
If the relationship data is a partial set, you can use pagination links within the relationship object.

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