title: Rails elasticsearch with multiple models
body: <p>Thus far I have implemented a basic search with eleasticsearch. Insert a keyword and find the item according to the title/description. 
  Now I'm trying to sort the results according to distance from the users current location, but don't know how.</p>
  
  <p>I have two models, the <code>items.rb</code> contains the item info and the <code>account.rb</code> has the address of the user that uploaded the item.</p>
  
  <p>This is what I have done thus far:</p>
  
  <p>item.rb</p>
  
  <pre><code>require 'elasticsearch/model'
  
  class Item &lt; ApplicationRecord
    mount_uploader :image, ImageUploader
    belongs_to :category
    belongs_to :user
  
    include Elasticsearch::Model
    include Elasticsearch::Model::Callbacks
  
    settings index: { number_of_shards: 1 } do
      mappings dynamic: 'false' do
        indexes :title, analyzer: 'english', index_options: 'offsets'
        indexes :description, analyzer: 'english'
      end
    end
  
    def self.search(query)
      __elasticsearch__.search(
      {
          query: {
              multi_match: {
                  query: query,
                  fields: ['title^10', 'description']
              }
          },
          highlight: {
              pre_tags: ['&lt;em&gt;'],
              post_tags: ['&lt;/em&gt;'],
              fields: {
                  title: {},
                  description: {},
              }
          }
      }
  )
    end
  end
  
  # Delete the previous articles index in Elasticsearch
  Item.__elasticsearch__.client.indices.delete index: Item.index_name rescue nil
  
  # Create the new index with the new mapping
  Item.__elasticsearch__.client.indices.create \
    index: Item.index_name,
    body: { settings: Item.settings.to_hash, mappings:  Item.mappings.to_hash }
  
  # Index all Item records from the DB to Elasticsearch
  Item.import
  </code></pre>
  
  <p>account.rb</p>
  
  <pre><code>require 'elasticsearch/model'
  
  class Account &lt; ApplicationRecord
  
    geocoded_by :full_address
    after_validation :geocode, if: -&gt;(obj){ obj.full_address.present? }
  
    def full_address
      [street, city, state, zip_code, country].join(",")
    end
  
    def location
      [longitude.to_f, latitude.to_f]
    end
  
    ELASTICSEARCH_MAX_RESULTS = 25
  
    include Elasticsearch::Model
    include Elasticsearch::Model::Callbacks
    include Elasticsearch::Model::Indexing
  
    mapping do
      indexes :location, type: 'geo_point'
      indexes :city,     type: 'string'
      indexes :state,    type: 'string'
    end
  
    def as_indexed_json(_options = {})
      as_json(only: %w(city state))
      .merge(location: {
          lat: lat.to_f, lon: lon.to_f
      })
    end
  
    def self.search(query = nil, options = {})
      options ||= {}
  
      # empty search not allowed, for now
      return nil if query.blank? &amp;&amp; options.blank?
  
      # define search definition
      search_definition = {
          query: {
              bool: {
                  must: []
              }
          }
      }
  
      unless options.blank?
        search_definition[:from] = 0
        search_definition[:size] = ELASTICSEARCH_MAX_RESULTS
      end
  
      # query
      if query.present?
        search_definition[:query][:bool][:must] &lt;&lt; {
            multi_match: {
                query: query,
                fields: %w(city state),
                operator: 'and'
            }
        }
      end
  
      # geo spatial
      if options[:lat].present? &amp;&amp; options[:lon].present?
        options[:distance] ||= 100
  
        search_definition[:query][:bool][:must] &lt;&lt; {
            filtered: {
                filter: {
                    geo_distance: {
                        distance: "#{options[:distance]}mi",
                        location: {
                            lat: options[:lat].to_f,
                            lon: options[:lon].to_f
                        }
                    }
                }
            }
         }
      end
  
      __elasticsearch__.search(search_definition)
     end
  end
  
  # Delete the previous articles index in Elasticsearch
  Account.__elasticsearch__.client.indices.delete index:  Account.index_name rescue nil
  
  # Create the new index with the new mapping
  Account.__elasticsearch__.client.indices.create \
  index: Item.index_name,
    body: { settings: Account.settings.to_hash, mappings: Account.mappings.to_hash }
  
  # Index all Item records from the DB to Elasticsearch
  Account.import
  </code></pre>
  
  <p>search_controller.rb</p>
  
  <pre><code>class SearchController &lt; ApplicationController
  
    def show
      @items = Item.find(params[:id])
    end
  
    def search
      if params[:q].nil?
        @items = []
      else
        @items = Item.search params[:q]
      end
    end
  end
  </code></pre>
  
  <p>search.html.erb</p>
  
  <pre><code>&lt;%= form_for search_path, method: :get do |f| %&gt;
      &lt;p&gt;
        &lt;%= f.label "Search for" %&gt;
        &lt;%= text_field_tag :q, params[:q] %&gt;
        &lt;%= submit_tag "Nearby", name: nil %&gt;
      &lt;/p&gt;
  &lt;% end %&gt;
  
  &lt;ul&gt;
    &lt;% @items.each do |item| %&gt;
        &lt;li&gt;
          &lt;h3&gt;
            &lt;%= link_to item.try(:highlight).try(:title) ? item.highlight.title[0].html_safe : item.title,
                    controller: "search",
                    action: "show",
                    id: item._id%&gt;
          &lt;/h3&gt;
          &lt;% if item.try(:highlight).try(:description) %&gt;
              &lt;% item.highlight.description.each do |snippet| %&gt;
                  &lt;p&gt;&lt;%= snippet.html_safe %&gt;...&lt;/p&gt;
              &lt;% end %&gt;
          &lt;% end %&gt;
        &lt;/li&gt;
    &lt;% end %&gt;
  </code></pre>
  
  <p></p>