Searchkick: Filtering by Geolocation for multiple locations per user?

Created on 22 Jul 2016  路  11Comments  路  Source: ankane/searchkick

I setup SearchKick and have it working nicely when each user has one location and the location data is stored in the User model. This part works fine. The next step was for me to move locations to their own model so that each user could list multiple locations. The problem is that I'm not sure how to index this. I've reviewed the documentation as well as several tutorials and I see how to do multiple models and that you need to map the field, but not sure how that works for SearchKick in the context of needing both latitude and longitude.

class User < ActiveRecord::Base
  searchkick language: "dutch", callbacks: :async, merge_mappings: true, 
    mappings: {
      user: {
        properties: {
          locations: {
            type:"geo_point"
          }
        }
      }
    },
    locations: ["location"]
    scope :search_import, -> { includes(:locations) }

  def search_data
    attributes.merge(
      location: [locations.map(&:latitude), locations.map(&:longitude)]
    )
end

That is what I have in the User model. When I reindex it does pull from locations. If you look at the line that is third from last you'll see where I have two map functions and I don't think this is correct. I couldn't find anything in the SearchKick docs to indicate how to handle locations that are in a separate model. It doesn't seem like it would be all that uncommon.

Also, for clarity, here is the query that I'm using:

User.search '*', where: { location: { near: [52.3734, 4.89406], within: '2km' } }

Again, this works fine if I have the data within the User table. I'm actually not 100% certain that SearchKick actually supports multiple locations but it seems as though it does. Please advise if that is not the case.

question

Most helpful comment

Actually locations (with an s) works:

locations: {
    near: { lat: lat, lon: lon },
    within: "#{params[:distance] || 30}mi"
  }

All 11 comments

Hey @blimey85, I've never tried to use multiple locations in the same field, so it's not intended to be supported. However, if it works as is, we should add a test case for it to make sure future updates don't break it.

It works when you have a User model that has longitude and latitude as fields in that model/table. In my case I need to be able to have a User that has multiple locations so I moved locations to their own model and did has many / belongs to associations. But now I'm not sure how to properly index them so that they are picked up when queried. I posted on Stack Overflow and Reddit but no advice on either one. I saw directions on how to handle multiple values by using map, but this is two values that need to get converted to geo_point. I'm very new to both SearchKick and Elasticsearch so still feeling my way along. I'll update if I'm able to figure anything out.

@blimey85 did you manage to do it? I'm facing the same situation :-/

Try this:

class User
  has_many :locations
  searchkick locations: ["locations"]
  scope :search_import, -> { includes(:locations) }

  def search_data
    attributes.merge(
      locations: locations.map { |loc| {lat: loc.latitude, lon: loc.longitude} }
    )
  end
end

Using:

class Event < ApplicationRecord
  has_many :performances

  scope :search_import, -> { includes(:performances) }

  searchkick locations: ['locations'] #, callbacks: :async

  def search_data
    {
      title: title,
      description: description,
      group_name: group.name,
      locations: performances.map { |l| { lat: l.latitude, lon: l.longitude } }
     }
  end
end

and searching with:

Event.search '*', where: {
  location: {
    near: { lat: lat, lon: lon },
    within: "#{params[:distance] || 30}mi"
  }
},
order: {
  _geo_distance: {
    location: "#{lat},#{lon}",
    order: 'asc',
    unit: 'mi'
  }
}

results in (User substituted with Event):

[400] {"error":{"root_cause":[{"type":"query_parsing_exception","reason":"failed to find geo_point field [location]","index":"events_development_20161024125441974","line":1,"col":147}],"type":"search_phase_execution_exception","reason":"all shards failed","phase":"query","grouped":true,"failed_shards":[{"shard":0,"index":"events_development_20161024125441974","node":"sDW2RccdQwmGmUkW0zW_5w","reason":{"type":"query_parsing_exception","reason":"failed to find geo_point field [location]","index":"events_development_20161024125441974","line":1,"col":147}}]},"status":400}

Actually locations (with an s) works:

locations: {
    near: { lat: lat, lon: lon },
    within: "#{params[:distance] || 30}mi"
  }

@chrise86 so SearchKick supports multiple locations on a model?

It appears to yes

Awsome, thanks!

@chrise86 I have done the same thing as you, but I am getting the following error: Searchkick::InvalidQueryError: [400] {"error":{"root_cause":[{"type":"query_parsing_exception","reason":"failed to find geo_point field [location]","index":"pres_development","line":1,"col":120}],"type":"search_phase_execution_exception","reason":"all shards failed","phase":"query","grouped":true,"failed_shards":[{"shard":0,"index":"pres_development","node":"C1pDlIIVSEm2w-VvW5YRqQ","reason":{"type":"query_parsing_exception","reason":"failed to find geo_point field [location]","index":"pres_development","line":1,"col":120}}]},"status":400}

This is my config of searchkick in my model:

class Pre
    searchkick  locations: ["locations"]

  def search_data
    {
      name: name,
      hidden: hidden,
      users_gender: users_gender,
      median_age: median_age,
      amount_of_participants: amount_of_participants,
      locations: [{ lat: 37, lon: -114 }, {lat: 37, lon: -113}, {lat: 32, lon: -114}]
     }
  end

end

Any idea?

From the error, it looks like you're trying to query on a field named location instead of locations.

"reason":"failed to find geo_point field [location]"

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rajatgarg79 picture rajatgarg79  路  4Comments

namila picture namila  路  4Comments

yurgon picture yurgon  路  4Comments

omgaz picture omgaz  路  5Comments

netwire88 picture netwire88  路  3Comments