Rails Elasticsearch-model model mapping - raw and not_analyzed - ruby-on-rails

I am using the Elasticsearch-rails gem in my application. I need my index mapping in the model to look like the JSON you see below (shortened only to include only relevant pieces). The part I don't understand relates to "name." How would that mapping be setup if this is what I need it to look like?
JSON Mapping I want to Rails model to output
{
"recipes":{
"mappings":{
"list":{
"dynamic":"false",
"properties":{
"creator_name":{
"type":"string"
},
"name":{
"type":"string",
"fields":{
"raw":{
"type":"string",
"index":"not_analyzed"
}
}
},
"permalink":{
"type":"string"
},
"user_id":{
"type":"string"
}
}
}
}
}
}
Current Rails model mapping
settings :index => { :number_of_shards => 1 } do
mapping :dynamic => 'false' do
indexes :user_id, :type => 'string'
indexes :permalink, :type => 'string'
indexes :creator_name, :type => 'string'
indexes :name, :type => 'string' # How do i make this match the json?
end
end
How do I set indexing for 'name' in the model to look like the JSON?
Thanks a lot!

Related

How to query more fields on mongo DB aggregation query?

I would like to know how to add an extra field to the response of collection.aggregate?
The query below groups activities by user_id. And I would like to know how to also include the user_name in the response.
DB Models
class Activity
include Mongoid::Document
field :hd_race_id, type: Float
field :metric_elapsed_time, type: Float
field :metric_distance, type: Float
field :user_name, type: String
belongs_to :user
...
class User
include Mongoid::Document
field :user_name, type: String
has_many :activities
...
Query
Activity.collection.aggregate([
{
"$group" => {
"_id" => "$user_id",
"distance" => { "$sum" => "$metric_distance" },
"time" => { "$sum" => "$metric_elapsed_time" },
},
},
{ "$sort" => { "distance" => -1 } },
])
Thank you in advance
Use the operator $first (aggregation accumulator) inside the $group stage.
For example:
"user_name": {"$first": "$user_name"}
or for the programming language you are using (not sure what it is), try something like:
"user_name" => {"$first" => "$user_name"},
For an example, see the "Group & Total" chapter in my Practical MongoDB Aggregations book

Elastic search 6.3 term filter not working on integer columns

I have integrated elasticsearch in a ROR application.
I am using elastic search to find products named 'xyz' and filtered on basis of star count = 1
This is my mapping:
{
:product => {
:dynamic => false,
:properties => {
:name => {
:analyzer => "english",
:type => "text"
},
:description => {
:analyzer => "english",
:type => "text"
},
:available => {
:type => :boolean
},
:last_updated => {
:type => :date
},
:categories_names => {
:type => "text"
},
:stars => {
:type => :integer
}
}
}
}
I am using the following query:
{
"query": {
"bool": {
"must": [
{
"multi_match": {
"query": "xyz",
"fields": ["name", "description"],
"type": "most_fields"
}
},
{
"match": {
"available": true
}
}
],
"filter": {
"term": {
"stars": 1
}
}
}
}
}
This gives me no match but I have product records with stars having value 1.
Also, range query is not giving any results with field stars.
This is the record that should match the above query:
#<Product> {
"id" => 90,
"name" => "xyz",
"last_updated" => Tue, 22 May 2018 15:04:43 UTC +00:00,
"stars" => 1,
"description" => "training",
"available" => true,
"created_at" => Thu, 21 Jun 2018 11:56:15 UTC +00:00,
"updated_at" => Fri, 29 Jun 2018 10:30:06 UTC +00:00,
}
try adding doc_value: true while indexing the field
indexes :stars, type: 'integer', doc_values: true
Most fields are indexed by default, which makes them searchable. The inverted index allows queries to look up the search term in unique sorted list of terms, and from that immediately have access to the list of documents that contain the term.
Sorting, aggregations, and access to field values in scripts require a different data access pattern. Instead of looking up the term and finding documents, we need to be able to look up the document and find the terms that it has in a field.
Doc values are the on-disk data structure, built at document index time, which makes this data access pattern possible. They store the same values as the _source but in a column-oriented fashion that is way more efficient for sorting and aggregations. Doc values are supported on almost all field types, with the notable exception of analyzed string fields.
Though default doc_values is true but explicitly adding it solved my purpose
Mapping and query looks good, maybe the issue in within the data (or maybe the mapping in your code is not the same in your cluster?).
Also may I suggest you move the "available: true" match as a "term" filter in the filter part? Filter is an array too, like must.

Grabbing a specific hash in an array that meets a specific criteria

I have a huge array full of a bunch of hashes. What I need to do is single out one index hash from the array that meets a specific criteria. (doing this due to an rspec test, but having trouble singling out one of them)
My array is like this
[
{
"name" => "jon doe",
"team" => "team2",
"price" => 2000,
"eligibility_settings" => {}
},
{
"name" => "jonny doe",
"team" => "team1",
"value" => 2000,
"eligibility_settings" => {
"player_gender" => male,
"player_max_age" => 26,
"player_min_age" => 23,
"established_union_only" => true
}
},
{
"name" => "jonni doe",
"team" => "team3",
"price" => 2000,
"eligibility_settings" => {}
},
]
I need to single out the second one, based on its eligibility settings. I just took three of them from my array, have lots more, so simple active record methods like (hash.second) won't work in this instance.
I've tried things like
players.team.map(&:hash).find{ |x| x[ 'eligibility_settings?' ] == true}
However when I try this, I get a nil response. (which is odd)
I've also looked into using the ruby detect method, which hasn't gotten me anywhere either
Players.team.map(&:hash).['hash.seligibiltiy_settings'].detect { true }
Would anybody have any idea what to do with this one?
Notes
players.team.map(&:hash).find{ |x| x[ 'eligibility_settings?' ] == true}
Players.team.map(&:hash).['hash.seligibiltiy_settings'].detect { true }
Is is players or Players ?
Why is it plural?
If you can call map on team, it probably should be plural
Why do you convert to a hash?
eligibility_settings? isn't a key in your hash. eligibility_settings is
eligibility_settings can be a hash, but it cannot be true
If you want to check if it isn't empty, use !h['eligibility_settings'].empty?
Possible solution
You could use :
data = [
{
'name' => 'jon doe',
'team' => 'team2',
'price' => 2000,
'eligibility_settings' => {}
},
{
'name' => 'jonny doe',
'team' => 'team1',
'value' => 2000,
'eligibility_settings' => {
'player_gender' => 'male',
'player_max_age' => 26,
'player_min_age' => 23,
'established_union_only' => true
}
},
{
'name' => 'jonni doe',
'team' => 'team3',
'price' => 2000,
'eligibility_settings' => {}
}
]
p data.find { |h| !h['eligibility_settings'].empty? }
# {"name"=>"jonny doe", "team"=>"team1", "value"=>2000, "eligibility_settings"=>{"player_gender"=>"male", "player_max_age"=>26, "player_min_age"=>23, "established_union_only"=>true}}
If h['eligibility_settings'] can be nil, you can use :
data.find { |h| !h['eligibility_settings'].blank? }
or
data.find { |h| h['eligibility_settings'].present? }

How to deserialize parameters with relationships using ActiveModelSerializers?

I used to code below to deserialize the JSON API data send from client,
def action_record_params
ActiveModelSerializers::Deserialization.jsonapi_parse!(params)
end
When I pass the following data from the client, the deserializer cannot see relationships attributes.
Client send parameter
params = {"data": {"type": "action_record", "attributes": {"value": ""}}, "relationships": {"card": {"data": {"type": "card", "id": "#{card.id}"}}}}
Server deserialized data
{:value=>""}
How to deserialize parameters with relationships using ActiveModelSerializers?
Base on AMS documentation Deserialization section, which can be found below
https://github.com/rails-api/active_model_serializers/blob/master/docs/general/deserialization.md
The relationships can be extracted by via the option only: [:relatedModelName]. only is act as a whitelist in this case.
Sample Data
document = {
'data' => {
'id' => 1,
'type' => 'post',
'attributes' => {
'title' => 'Title 1',
'date' => '2015-12-20'
},
'relationships' => {
'author' => {
'data' => {
'type' => 'user',
'id' => '2'
}
},
'second_author' => {
'data' => nil
},
'comments' => {
'data' => [{
'type' => 'comment',
'id' => '3'
},{
'type' => 'comment',
'id' => '4'
}]
}
}
}
}
AMS deserialization with options
ActiveModelSerializers::Deserialization
.jsonapi_parse(document, only: [:title, :date, :author],
keys: { date: :published_at },
polymorphic: [:author])
Output hash
# {
# title: 'Title 1',
# published_at: '2015-12-20',
# author_id: '2',
# author_type: 'user'
# }

ElasticSearch with Tire doesn't include custom analyzer with STI model

I have an STI model which I want to be searchable with ElasticSearch and Tire. The issue I am having is when Tire creates the mappings it seems to ignore my custom analyzers for the second model. Below is an example of my models.
class Account < ActiveRecord::Base
attr_accessible :name, :type
include Tire::Model::Search
include Tire::Model::Callbacks
tire.settings :analysis => {
:analyzer => {
"custom_search_analyzer" => {
"tokenizer" => "keyword",
"filter" => "lowercase"
},
"custom_index_analyzer" => {
"tokenizer" => "keyword",
"filter" => ["lowercase","substring"]
}
},
:filter => {
:substring => {
"type" => "nGram",
"min_gram" => 1,
"max_gram" => 20
}
}
} do
mapping do
indexes :id, :type => 'integer', :include_in_all => false
indexes :name, :type => 'string', :search_analyzer => :custom_search_analyzer, :index_analyzer=>:custom_index_analyzer
end
end
def to_indexed_json
hash = {}
hash[:id] = id
hash[:name] = name
hash.to_json
end
end
class StandardAccount < Account
tire.index_name 'accounts'
end
class SuperAccount < Account
tire.index_name 'accounts'
end
When I create the index through tire, either with the rake task or through creating a model it creates the mappings but for the inherited models it doesn't apply the custom analyzers to them. If I look at the mappings using
curl -XGET 'http://127.0.0.1:9200/accounts/_mapping?pretty=1'
I get:
{
"accounts" : {
"account" : {
"properties" : {
"id" : {
"type" : "integer",
"include_in_all" : false
},
"name" : {
"type" : "string",
"index_analyzer" : "custom_index_analyzer",
"search_analyzer" : "custom_search_analyzer"
}
}
},
"standard_account" : {
"properties" : {
"id" : {
"type" : "long"
}
"name" : {
"type" : "string"
}
}
},
"super_account" : {
"properties" : {
"id" : {
"type" : "long"
}
"name" : {
"type" : "string"
}
}
}
}
}
Even if I move the mapping declarations to the inherited classes it seems to be only the first model created that picks up the extra options. I can manually create the indexes through ElasticSearch but was wondering if there was a way to do it with Tire? Or do I have something set up incorrectly
You might have figured out the answer already, but I think this might help you, or others having the same problem:
https://stackoverflow.com/a/13660263/1401343
-Vlad

Resources