ember-rails not loading hasMany Association - ruby-on-rails

Gems Used:
Using ember-source (1.5.1.1)
Using ember-data-source (1.0.0.beta.7)
Using ember-rails (0.15.0)
Using handlebars-source (1.3.0)
Using active_model_serializers (0.8.1)
This is the ember app code:
window.App = Ember.Application.create
LOG_TRANSITIONS: true
rootElement: '#app-ember-root'
App.store = DS.Store.extend({});
App.ApplicationAdapter = DS.ActiveModelAdapter.extend({});
App.Router.map ()->
#resource 'quotes',
path: '/'
App.QuotesRoute = Ember.Route.extend
model: ()->
this.store.find 'quote'
App.CompaniesQuote = DS.Model.extend
quote: DS.belongsTo 'quote'
App.Quote = DS.Model.extend
message: DS.attr 'string'
createdAt: DS.attr 'date'
companiesQuotes: DS.hasMany('companiesQuote')
companiesQuotesCount: (->
this.get('companiesQuotes.length')
).property('companiesQuotes')
Serializers:
class QuoteSerializer < ActiveModel::Serializer
attributes :id, :message, :created_at
has_many :companies_quotes
end
class CompaniesQuoteSerializer < ActiveModel::Serializer
attributes :id, :company_id
end
Template quotes.handlebars:
<div class="quotes-list">
<div class="quote-container">
{{#each}}
{{ companiesQuotesCount }} companies quote for this quote {{ id }}
{{/each }}
</div>
</div>
/quotes JSON response:
{
"quotes":[
{
"id":10,
"message":"Quote 10!",
"created_at":"2014-06-04T17:00:01.000Z",
"companies_quotes":[
{
"id":27,
"company_id":1
},
{
"id":28,
"company_id":2
},
{
"id":26,
"company_id":3
}
]
},
{
"id":11,
"message":"Quote 11!",
"created_at":"2014-06-11T14:45:02.000Z",
"companies_quotes":[
{
"id":30,
"company_id":1
},
{
"id":31,
"company_id":2
},
{
"id":29,
"company_id":3
}
]
}
]
}
With this env/code, the property companiesQuotesCount is always 0.
What am I missing?
Solved
Using the #kingpin2k's response, I changed the JSON structure modifying QuoteSerializer:
class QuoteSerializer < ActiveModel::Serializer
embed :ids, include: true
attributes :id, :message, :created_at
has_many :companies_quotes
end

Ember Data doesn't do embedded records by default.
You'll either need to fix the json using a client side serializer, or fix it server side.
It should be in this format:
{
"quotes":[
{
"id":10,
"message":"Quote 10!",
"created_at":"2014-06-04T17:00:01.000Z",
"companies_quotes":[27, 28, 29]
},
{
"id":11,
"message":"Quote 11!",
"created_at":"2014-06-11T14:45:02.000Z",
"companies_quotes":[30, 31, 32]
]
}
],
companies_quotes:[
{
"id":27,
"company_id":1
},
{
"id":28,
"company_id":2
},
{
"id":26,
"company_id":3
},
....
]
}
Here's an example for using extractArray http://emberjs.com/api/data/classes/DS.RESTSerializer.html#method_extractArray
Additionally your computed property is just watching the reference, which won't change as the length changes.
companiesQuotesCount: (->
this.get('companiesQuotes.length')
).property('companiesQuotes.length')

Related

How to filter documents by computed embedded field

I have the following schema
class User
include Mongoid::Timestamps::CamelCaseCreated
include Mongoid::Timestamps::CamelCaseUpdated
embeds_many :intervals
field :name, type: String
# ...
end
class Interval
embedded_in :user
field :startTime, type: DateTime
field :endTime, type: DateTime
# ...
end
I need to get users where at least one interval has duration greater or equal to 4 hours.
So for the following data I should receive only Walter White
users = [
{
name: 'Walter White',
intervals: [
{ startTime: '2021-01-27T08:00:00Z', endTime: '2021-01-27T16:00:00Z' }
]
},
{
name: 'Jesse Pinkman',
intervals: [
{ startTime: '2021-01-27T08:00:00Z', endTime: '2021-01-27T10:00:00Z' }
{ startTime: '2021-01-27T10:00:00Z', endTime: '2021-01-27T13:00:00Z' }
]
},
{
name: 'Saul Goodman',
intervals: []
}
]
I know that on a document itself I could filter by computed field like this
User.collection.aggregate([
{ '$project' => { 'durationInMilliseconds' => { '$subtract' => ['$updatedAt', '$createdAt'] } } },
{ '$match' => { 'durationInMilliseconds' => { '$gte' => four_hours } } }
])
# or
User.where('$expr' => { '$gt' => [{ '$subtract': ['$updatedAt', '$createdAt']}, four_hours] })
I was hoping that those would work with $elemMatch, but I receive the following errors
User.where(
intervals: {
'$elemMatch' => [
{ '$project' => { 'durationInMilliseconds' => { '$subtract' => ['$endTime', '$startTime'] } } },
{ '$match' => { 'durationInMilliseconds' => { '$gt' => four_hours } } }
]
}
)
=> $elemMatch needs an Object
User.where(
intervals: {
'$elemMatch' => {
{ '$expr' => { '$max' => some_logic_here } },
}
}
)
=> $expr can only be applied to the top-level document
Is there a way to make it work with the embedded collection?
versions that I'm using:
gem mongo (2.10.2)
gem mongoid (6.1.1)
mongo --version - 4.2.9

Custom json on rails 5 api only, using to_json

I am working on a rails 5 api only. I can get all the data that i want, but i would like to customize the output.I tried some solutions, but couldn't find it yet.
This is my portfolio controller:
def show
render json: #portfolio.to_json(
:only => [:title],
:include => {
:cryptos => {
:only => [],
:include => [
{:radarcrypto => { :only => [:ticker, :price] }},
]},
}
)
end
this is the output
{
title: "Portfolio 1",
cryptos: [
{
radarcrypto: {
ticker: "BTC",
price: "190919.85",
}
},
{
radarcrypto: {
ticker: "ETH",
price: "12220.18",
}
},
],
}
But i would like something like that:
{
title: "Portfolio 1",
cryptos:
{
ticker: "BTC",
price: "190919.85",
},
{
ticker: "ETH",
price: "12220.18",
},
}
this are my models
class Portfolio < ApplicationRecord
has_many :cryptos
end
class Radarcrypto < ApplicationRecord
has_many :cryptos
end
class Crypto < ApplicationRecord
belongs_to :portfolio
belongs_to :radarcrypto
end
I already tried to use without success:
class Radarcrypto < ApplicationRecord
ApplicationRecord.include_root_in_json = false
end
I don't know if there is a better solution to this customization, if would be better to use the views, json.jbuilder for example. thanks.
It's not possible to include a collection of objects as a value to a key without wrapping them in an array in JSON. (source (W3Schools))
It is possible, on the other hand, to have a collection of objects, but each one would have to have its own unique key. So the response could be shaped as such:
{
title: "Portfolio 1",
cryptos: {
BTC: {
ticker: "BTC",
price: "190919.85",
},
ETH: {
ticker: "ETH",
price: "12220.18",
},
}
(A different key is going to have to be used if more than one radarcrypto can have the same ticker)
I'm not sure how that would be achieved using the to_json optionsĀ (e.g. include and only). You could probably do it manually by doing:
def show
portfolio = {};
portfolio['title'] = #portfolio.title
portfolio['cryptos'] = {}
#portfolio.cryptos.each do |crypto|
radarcrypto = crypto.radarcrypto
portfolio['cryptos']["#{radarcrypto.ticker}"] = {
'ticker': radarcrypto.ticker,
'price': radarcrypto.price
}
end
render json: portfolio.to_json
end
Side note
A gem such as Jbuilder may be a good candidate if nested shaping of JSON responses is done in multiple controllers.

Spell check Ngram for elastic Search not working with rails

I have used in my model to include spell check such that if the user inputs data like "Rentaal" then it should fetch the correct data as "Rental"
document.rb code
require 'elasticsearch/model'
class Document < ApplicationRecord
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks
belongs_to :user
Document.import force: true
def self.search(query)
__elasticsearch__.search({
query: {
multi_match: {
query: query,
fields: ['name^10', 'service']
}
}
})
end
settings index: {
"number_of_shards": 1,
analysis: {
analyzer: {
edge_ngram_analyzer: { type: "custom", tokenizer: "standard", filter:
["lowercase", "edge_ngram_filter", "stop", "kstem" ] },
}
},
filter: {
edge_ngram_filter: { type: "edgeNGram", min_gram: "3", max_gram:
"20" }
}
} do
mapping do
indexes :name, type: "string", analyzer: "edge_ngram_analyzer"
indexes :service, type: "string", analyzer: "edge_ngram_analyzer"
end
end
end
search controller code:
def search
if params[:query].nil?
#documents = []
else
#documents = Document.search params[:query]
end
end
However, if I enter Rentaal or any misspelled word, it does not display anything.
In my console
#documents.results.to_a
gives an empty array.
What am I doing wrong here? Let me know if more data is required.
Try to add fuzziness in your multi_match query:
{
"query": {
"multi_match": {
"query": "Rentaal",
"fields": ["name^10", "service"],
"fuzziness": "AUTO"
}
}
}
Explanation
Kstem filter is used for reducing words to their root forms and it does not work as you expected here - it would handle corectly phrases like Renta or Rent, but not the misspelling you provided.
You can check how stemming works with following query:
curl -X POST \
'http://localhost:9200/my_index/_analyze?pretty=true' \
-d '{
"analyzer" : "edge_ngram_analyzer",
"text" : ["rentaal"]
}'
As a result I see:
{
"tokens": [
{
"token": "ren"
},
{
"token": "rent"
},
{
"token": "renta"
},
{
"token": "rentaa"
},
{
"token": "rentaal"
}
]
}
So typical misspelling will be handled much better with applying fuzziness.

Rails ElasticSearch model with fields

I'm using elasticsearch-rails and mongoid
I have the following simple mapping with fields:
"title": {
"type": "string",
"fields": {
"raw": {
"type": "string",
"index": "not_analyzed"
}
}
}
My model looks like this:
class ArticlesEvent
include Mongoid::Document
include Elasticsearch::Model
field :title, type: String
attr_accessible :title
def as_indexed_json(options={})
as_json(except: [:id, :_id])
end
Can anyone show an example how to define the rails model with the title.raw field, and how to access that field? Since the multi fields have been deprecated it's hard to find a working example with rails and mongoid.
Thanks
You should be able to achieve this with the following attribute definition in your model:
attribute :title, String, mapping: {
fields: {
raw: { type: 'string', index: 'not_analyzed' }
}
}
Looks like this is still an open issue with elasticsearch-rails: https://github.com/elastic/elasticsearch-rails/issues/79
You can define below mapping in your model
indexes :name_of_field,type: :keyword, fields: {
raw: {
type: :text
}
}

Ember, Ember-data and Rails relations error: "Cannot read property 'typeKey' of undefined"

I connect Ember to a Rails API which delivers some JSON.
I'm trying to get relations working (product hasMany images) but it keeps giving me this error:
Cannot read property 'typeKey' of undefined
My Models:
App.Product = DS.Model.extend({
images: DS.hasMany('image'),
title: DS.attr('string'),
});
App.Image = DS.Model.extend({
product: DS.belongsTo('product')
});
Rails renders json as:
{
"products":[
{
"id": 1,
"title": "product title",
"images":[
{
"id": 1,
"urls":
{
"thumb":"http://domain.com/thumb/image.jpg",
"original":"http://domain.com/original/image.jpg"
}
}
]
}
]
}
Turns out I needed to "sideload" my images in Rails, so the JSON became:
{
"products":[
{
"id": 1,
"title": "product title",
"image_ids": [1]
}
],
"images":[
{
"id": 1,
"urls":
{
"thumb":"http://domain.com/thumb/image.jpg",
"original":"http://domain.com/original/image.jpg"
}
}
]
}
Rails' ProductSerializer:
class ProductSerializer < ActiveModel::Serializer
embed :ids, :include => true
attributes :id, :title
has_many :images
methods :image_urls
end
It seems that you were using embedded JSON in your example. You need to use the EmbeddedRecordsMixin https://github.com/emberjs/data/blob/master/packages/ember-data/lib/serializers/embedded_records_mixin.js and set the appropriate flag to mark images as being embededd

Resources