Removing empty hashes {} from array in Active Model Serialized object - ruby-on-rails

I have a few conditions where don't want to serialize the current object and want to skip it. But i haven't found a way to do that so I am ignoring attributes on attribute :foo, if: :condition. And this is generating empty {} in my serialized object inside arrays. How do I fix this?
[
{
"id": 392027,
"name": "ISC Board",
"grades":[
{
"id": 333938,
"name": "1",
"subjects": [
{
"id": 571671,
"subject": "Math"
},
{
"id": 742980,
"subject": "Science"
},
{
"id": 186926,
"subject": "English"
},
{
"id": 658224,
"subject": "Social_Studies"
},
{},
{},
{}
]
},
{
"id": 333943,
"name": "2",
"subjects": [
{
"id": 571671,
"subject": "Math"
},
{
"id": 742980,
"subject": "Science"
},
{
"id": 186926,
"subject": "English"
},
{
"id": 658224,
"subject": "Social_Studies"
},
{},
{},
{}
]
},
]
},
{
"id": 666627,
"name": "NY Board",
"grades":[
{
"id": 333938,
"name": "1",
"subjects": [
{
"id": 571671,
"subject": "Math"
},
{
"id": 742980,
"subject": "Science"
},
{
"id": 186926,
"subject": "English"
},
{
"id": 658224,
"subject": "Social_Studies"
},
{},
{},
{}
]
},
{
"id": 432943,
"name": "2",
"subjects": [
{
"id": 571671,
"subject": "Math"
},
{
"id": 742980,
"subject": "Science"
},
{
"id": 186926,
"subject": "English"
},
{
"id": 658224,
"subject": "Social_Studies"
},
{},
{},
{}
]
},
]
}
]
serializer looks something like this-
class BoardSerializer < ActiveModel::Serializer
#some code
class GradeSerializer < ActiveModel::Serializer
has_many :subjects
#some code
class SubjectSerializer < ActiveModel::Serializer
attribute :id, if: :condition
attribute :name, key: :subject, if: :condition
def condition
#some code
#returns true or false
#will not return both :id and :subject if false- I want to
#skip this current object if condition fails. (returns {})
end
end
end
end
How do I simply skip the current object in the serializer or remove empty hashes? Thanks

Please, check if this is the expected result:
input.transform_values { |v| v.map {|e| e.transform_values { |vv| vv.class == Array ? vv.select { |ee| ee unless ee.empty? } : vv } } }
# => {:grades=>[{:id=>333938, :name=>"1", :subjects=>[{:id=>571671, :subject=>"Math"}, {:id=>742980, :subject=>"Science"}, {:id=>186926, :subject=>"English"}, {:id=>658224, :subject=>"Social_Studies"}]}]}
EDIT: to meet changes in OP question.
input.map { |e| e.transform_values { |v| v.is_a?(Array) ? v.map {|ee| ee.transform_values { |vv| vv.is_a?(Array) ? vv.select { |eee| eee unless eee.empty? } : vv } } : v } }
# => [{:id=>392027, :name=>"ISC Board", :grades=>[{:id=>333938, :name=>"1", :subjects=>[{:id=>571671, :subject=>"Math"}, {:id=>742980, :subject=>"Science"}, {:id=>186926, :subject=>"English"}, {:id=>658224, :subject=>"Social_Studies"}]}, {:id=>333943, :name=>"2", :subjects=>[{:id=>571671, :subject=>"Math"}, {:id=>742980, :subject=>"Science"}, {:id=>186926, :subject=>"English"}, {:id=>658224, :subject=>"Social_Studies"}]}]}, {:id=>666627, :name=>"NY Board", :grades=>[{:id=>333938, :name=>"1", :subjects=>[{:id=>571671, :subject=>"Math"}, {:id=>742980, :subject=>"Science"}, {:id=>186926, :subject=>"English"}, {:id=>658224, :subject=>"Social_Studies"}]}, {:id=>432943, :name=>"2", :subjects=>[{:id=>571671, :subject=>"Math"}, {:id=>742980, :subject=>"Science"}, {:id=>186926, :subject=>"English"}, {:id=>658224, :subject=>"Social_Studies"}]}]}]

You can use #select! for this matter:
input = {
"grades":
[
{
"id": 333938,
"name": "1",
"subjects":
[
{
"id": 571671,
"subject": "Math"
},
{
"id": 742980,
"subject": "Science"
},
{
"id": 186926,
"subject": "English"
},
{
"id": 658224,
"subject": "Social_Studies"
},
{},
{},
{}
]
}
]
}
input[:grades].first[:subjects].select! { |i| !i.empty? }

Related

Problem with Elasticsearch querying nested documents

I am learning ES and I am having problems with this query:
Given 2 products:
products/_source/1
{
"product_id": "58410-2",
"name": [
{
"locale": "en",
"translation": "CBC panel"
},
{
"locale": "vn",
"translation": "CBC panel VN"
}
],
"status": "active",
"category": {
"id": 8,
"name": [
{
"locale": "en",
"translation": "Hematology"
},
{
"locale": "vn",
"translation": "huyết học"
}
]
},
"children": [
{
"product_id": "6690-2",
"name": [
{
"locale": "en",
"translation": "Leukocytes"
},
{
"locale": "vn",
"translation": "Leukocytes vn"
}
],
"status": "active",
"category": {
"id": 8,
"name": [
{
"locale": "en",
"translation": "Hematology"
},
{
"locale": "vn",
"translation": "huyết học"
}
]
},
"children": []
}]}
and
products/_source/2
{
"product_id": "6690-2",
"name": [
{
"locale": "en",
"translation": "Leukocytes"
},
{
"locale": "vn",
"translation": "Leukocytes vn"
}
],
"status": "active",
"category": {
"id": 8,
"name": [
{
"locale": "en",
"translation": "Hematology"
},
{
"locale": "vn",
"translation": "huyết học"
}
]
},
"children": []
}
where a product is a single document but also can be nested in a children array of other products. Both products are different documents in the index.
and this index:
{
"products": {
"aliases": {},
"mappings": {
"dynamic": "false",
"properties": {
"category": {
"properties": {
"name": {
"properties": {
"locale": {
"type": "keyword"
},
"translation": {
"type": "text"
}
}
}
}
},
"children": {
"type": "nested"
},
"name": {
"properties": {
"locale": {
"type": "keyword"
},
"translation": {
"type": "text"
}
}
},
"product_id": {
"type": "keyword"
},
"status": {
"type": "keyword"
}
}
},
"settings": {
"index": {
"routing": {
"allocation": {
"include": {
"_tier_preference": "data_content"
}
}
},
"number_of_shards": "3",
"provided_name": "products",
"number_of_replicas": "1"
}
}
}
}
I want to be able to query for "Leuko" (or the category or the product_id) and retrieve both products, the single product and the root product.
I have tried using object field, nested, flattened but I think the problem is I don't know how to properly write the query, I have tried things like this (I am using a ruby library but I think it is easy to follow):
#query = {
query: {
query_string: {
fields: ['name.translation', 'children.name.translation', 'category.name.translation', 'children.product_id'],
query: "*#{text}*"
}
},
size: 50
}
#query = {
query: {
nested: {
path: 'children',
query: {
bool: {
should: [
term: { 'children.name.translation' => "*#{text}*" },
term: { 'name.translation' => "*#{text}*" }
]
}
}
}
}
}
but I think at some point I dunno what I am doing anymore and I am just randomly trying different stuff from the documentation.
Follow my query suggestion. Note that I had to add the fields in the Nested object to the mapping.
Mapping:
{
"mappings": {
"dynamic": "false",
"properties": {
"category": {
"properties": {
"name": {
"properties": {
"locale": {
"type": "keyword"
},
"translation": {
"type": "text"
}
}
}
}
},
"children": {
"type": "nested",
"properties": {
"product_id": {
"type": "keyword"
},
"category": {
"properties": {
"name": {
"properties": {
"locale": {
"type": "keyword"
},
"translation": {
"type": "text"
}
}
}
}
},
"name": {
"properties": {
"locale": {
"type": "keyword"
},
"translation": {
"type": "text"
}
}
},
"status": {
"type": "keyword"
}
}
},
"name": {
"properties": {
"locale": {
"type": "keyword"
},
"translation": {
"type": "text"
}
}
},
"product_id": {
"type": "keyword"
},
"status": {
"type": "keyword"
}
}
}
}
Query:
{
"query": {
"bool": {
"minimum_should_match": 1,
"should": [
{
"nested": {
"path": "children",
"query": {
"wildcard": {
"children.name.translation": "leuko*"
}
}
}
},
{
"wildcard": {
"name.translation": "leuko*"
}
}
]
}
}
}
hint
See that you use translation. Avoid using array to make your queries simpler.
What I would do in your case is to create a field for each language, this makes the use of analyzer more flexible for each type of language and you stop using an array and work with an object.
PUT test
{
"mappings": {
"properties": {
"name":{
"type": "text",
"fields": {
"es":{
"type": "text",
"analyzer":"english"
},
"vn":{
"type": "text"
}
}
}
}
}
}
POST test/_doc/
{
"name": "Leukocytes"
}
An example query using field languages.
GET test/_search
{
"query": {
"multi_match": {
"query": "Leukocytes",
"fields": ["name.es", "name.vn"]
}
}
}

Defining Array of Objects in Swagger Documentation

I'm using swagger for quite a bit now, we have started documenting our code using it, in one place there's an API response which returns multiple objects in the included block.
Example:
{
"data": {
"id": "1",
"type": "schoolPositions",
"attributes": {
"description": "teases the students",
"mustHaves": "principle"
},
"relationships": {
"schoolLocation": {
"data": {
"id": "72",
"type": "schoolLocations"
}
},
"schoolCompensation": {
"data": {
"id": "75",
"type": "schoolCompensations"
}
},
"jobSpecs": {
"data": [
{
"id": "82",
"type": "schoolAttachments"
}
]
}
}
},
"included": [
{
"id": "72",
"type": "schoolLocations",
"attributes": {
"city": "Berhampore",
"state": "West Bengal",
"postalCode": "742101",
"country": "India",
"globalRegionId": 30,
"regionId": 683
}
},
{
"id": "75",
"type": "schoolCompensations",
"attributes": {
"salary": "",
"bonus": "",
"equity": "",
"currencyId": null,
"equityType": "percent",
"salaryDescription": null
}
},
{
"id": "82",
"type": "schoolAttachments",
"attributes": {
"attachmentType": "JobSpecificationAttachmentType",
"fileFileName": "vs.jpg",
"fileContentType": "image/jpeg",
"fileFileSize": 2410039,
"fileUpdatedAt": "2018-12-12T07:06:38Z",
"downloadUrl": "001-vs.jpg?1544598398",
"klass": "SchoolAttachments"
}
}
]
I have wasted an entire day on the internet and documentation trying to document the included part, but I'm going wrong somewhere
response 200 do
key :description, 'School Data'
schema do
property :data do
key :type, :array
items do
key :'$ref', :School
end
end
property :included do
key :type, :array
items do
key :'$ref', :SchoolLocations
key :'$ref', :SchoolCompensations
key :'$ref', :SchoolAttachments
end
end
end
end
This shows only the SchoolAttachments in the included part.
I have tried using allOff but it doesn't work.

Swagger array of different objects

For example I need to represent following structure in swagger yaml format:
"included": [
{
"type": "people",
"id": "42",
"attributes": {
"name": "John",
"age": 80,
"gender": "male"
}
}
]
With single object everything is fine:
included:
type: "array"
items:
type: object
properties:
type:
type: "string"
# and so on
The question is what if I need to describe something like:
"included": [{
"type": "people",
"id": "9",
"attributes": {
"first-name": "Dan",
"last-name": "Gebhardt",
"twitter": "dgeb"
},
"links": {
"self": "http://example.com/people/9"
}
}, {
"type": "comments",
"id": "5",
"attributes": {
"body": "First!"
},
"relationships": {
"author": {
"data": { "type": "people", "id": "2" }
}
},
"links": {
"self": "http://example.com/comments/5"
}
}, {
"type": "comments",
"id": "12",
"attributes": {
"body": "I like XML better"
},
"relationships": {
"author": {
"data": { "type": "people", "id": "9" }
}
},
"links": {
"self": "http://example.com/comments/12"
}
}]
I know that in OpenAPI it's possible ( aka swagger 3 ) and tried some workarounds in current version but with no luck.

JSON API Resources relationship attributes

I have two models related :
class Item < ActiveRecord::Base
belongs_to :carousel
end
And
class Carousel < ActiveRecord::Base
has_many :items
end
I trying to open API to carousels using the JSON API Resources gem, i need to show carousel attributes and some items attributes, something like this:
{
"data": [
{
"id": "1",
"type": "carousels",
"links": {
"self": "http://localhost:3000/api/v1/carousels/1"
},
"attributes": {
"name": "primary",
"items": [
{
"title": "first item",
"file-url": "url",
"index": 0
},
{
"title": "second item",
"file-url": "url",
"index": 1
}
]
}
}
]
}
My carousel resource:
module Api
module V1
class CarouselResource < JSONAPI::Resource
immutable
attributes :name, :items
def self.records(options = {})
user = options[:context][:current_user]
user.carousels
end
end
end
end
My result:
{
"data": [
{
"id": "1",
"type": "carousels",
"links": {
"self": "http://localhost:3000/api/v1/carousels/1"
},
"attributes": {
"name": "primary",
"items": [
{
"id": 3,
"carousel_id": 1,
"file": {
"url": "url"
},
"title": "first item",
"kind": 0,
"index": 0,
"created_at": "2017-02-23T10:31:53.592-03:00",
"updated_at": "2017-03-01T10:30:52.533-03:00"
},
{
"id": 5,
"carousel_id": 1,
"file": {
"url": "url"
},
"title": "second item",
"kind": 0,
"index": 1,
"created_at": "2017-03-01T10:30:07.011-03:00",
"updated_at": "2017-03-01T10:30:07.011-03:00"
}
]
}
}
]
}

Rails - better flow control - loop

I am grabbing value data: name, uid, highschool_name, graduateschool_name like this:
def add_friends
facebook.get_connections("me", "friends", :fields => "name, id, education").each do |hash|
self.friends.where(:name => hash['name'],
:uid => hash['id'],
:highschool_name => hash['education']['school']['name'] unless hash["education"].blank?,
:graduateschool_name => hash['education']['school']['name'] unless hash["education"].blank?).
first_or_create
end
end
From an array of hash:
"education": [
{
"school": {
"id": "110703012290674",
"name": "Kunskapsgymnasiet Malmö"
},
"year": {
"id": "136328419721520",
"name": "2009"
},
"type": "High School"
},
{
"school": {
"id": "112812485399398",
"name": "Malmö University"
},
"year": {
"id": "118118634930920",
"name": "2012"
},
"concentration": [
{
"id": "104076956295773",
"name": "Computer Science"
}
],
"type": "Graduate School",
"classes": [
{
"id": "165093923542525",
"name": "Programmering",
"description": "Kursen fokuserar på metoder och tekniker vid utveckling av webbapplikationer med hjälp av HTML5."
}
]
}
],
EDIT:
This code dosent work. I would like to pick every hichschool and Graduate School from this array of hash and save it.
high_schools = response['education'].collect{|ed| ed['school']['name'] if ed['type'] == "High School" }
grad_schools = response['education'].collect{|ed| ed['school']['name'] if ed['type'] == "Graduate School" }

Resources