I am using elasticsearch with rails for live search functionality on a twitter-like feed. I have a User table and a Post table that are stored in Postgres. I only want to index the Post table in ES for searching, however I want the User data for each post so I can show who made the post, what their rank is etc. So essentially I want to join the User and Post data, then index it into elasticsearch. Right now, my Post model looks like this:
class Post < ApplicationRecord
belongs_to :user
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks
index_name Rails.application.class.parent_name.underscore
settings index: { number_of_shards: 1 } do
mapping dynamic: false do
indexes :title, analyzer: 'english'
indexes :description, analyzer: 'english'
end
end
def self.search(query)
__elasticsearch__.search(
{
query: {
multi_match: {
query: query,
type: 'phrase',
fields: ['title^5', 'description']
},
}
)
end
My User model looks like this:
class User < ApplicationRecord
has_many :posts
end
My schema looks like this:
ActiveRecord::Schema.define(version: 2020_04_16_175351) do
create_table "posts", force: :cascade do |t|
t.string "title"
t.text "description"
t.string "image"
t.integer "vouches"
t.bigint "user_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["user_id"], name: "index_posts_on_user_id"
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "username"
t.integer "rank"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
add_foreign_key "posts", "users"
end
And when I do an elasticsearch query on localhost:9200, each Post it returns is an object that looks like this:
{ "id": x , "title": "y" , "description": "z", "image": "", "vouches": 0, "user_id": 1}
I want to be able to have the relevant user object or user data stored in each post object - not just the user_id. Ideally I'd like to see a Post object that looks similar to this:
{ "id": x , "title": "y" , "description": "z", "image": "", "vouches": 0, "user_id": 1, "username": "a", "rank": 0...etc}
I feel like this should happen in the Post model somewhere but I've been unsuccessful in my attempt at using nested mapping among others.
So I was able to solve this using the 'as_indexed_json' method from elasticsearch-models. I just placed it in the Post model under the mappings.
def as_indexed_json(options = {})
self.as_json(
options.merge(
only: [:id, :title, :body, :published_at, :created_at, :updated_at],
include: { user: { only: [:id, :name] } }
)
)
end