Rails elasticsearch - 命名范围搜索

时间:2014-09-23 19:06:59

标签: ruby-on-rails elasticsearch

我只是从Tire gem转移到官方弹性搜索Ruby wrapper,我正在努力实现更好的搜索功能。

我有一个模型InventoryItem和一个模型StoreStore has_many :inventory_items。我在商店中有一个名为local

的模型范围
scope :local, lambda{|user| near([user.latitude, user.longitude], user.distance_preference, :order => :distance)}

我希望搜索只返回此范围的结果,所以我尝试了:InventoryItem.local(user).search....但它搜索整个索引,而不是范围。在做了一些研究后,看起来过滤器是实现这一目标的好方法,但我不确定如何实现。我也愿意采用其他方式实现这一目标。我的最终目标是能够根据商店位置搜索InventoryItem模型的子集。

2 个答案:

答案 0 :(得分:6)

您可以做的另一件事是将有效ID列表发送到弹性,因此它会自行过滤记录,然后对剩下的记录执行搜索。我们没有测试它是否更快,但我认为它应该,因为弹性是一个搜索引擎。

我将尝试使用您的类+变量和我们的经验撰写一个示例:

def search
  # retrieve ids you want to perform search within
  @store_ids = Store.local(current_user).select(:id).pluck(:id)
  # you could also check whether there are any ids available
  # if there is none - no need to request elastic to search
  @response = InventoryItem.search_by_store_ids('whatever', @store_ids)
end

一个模特:

class InventoryItem
  # ...

  # search elastic only for passed store ids
  def self.search_by_store_ids(query, store_ids, options = {})       
    # use method below
    # also you can use it separately when you don't need any id filtering
    self.search_all(query, options.deep_merge({
      query: {
        filtered: {
          filter: {
            terms: {
              store_id: store_ids
            }
          }
        }
      }
    }))
  end

  # search elastic for all inventory items
  def self.search_all(query, options = {})
    self.__elasticsearch__.search(
      {
        query: {
          filtered: {
            query: {
              # use your fields you want to search, our's was 'text'
              match: { text: query },
            },
            filter: {},
            strategy: 'leap_frog_filter_first' # do the filter first
          }
        }
      }.deep_merge(options)
      # merge options from self.search_by_store_ids if calling from there
      # so the ids filter will be applied
    )
  end
  # ...
end

这样你也必须索引store_id

您可以详细了解过滤器here

答案 1 :(得分:0)

我会留下这个答案而不接受,直到赏金结束 - 如果您认为自己找到了更好的解决方案,请随意添加答案。

经过一番挖掘后,答案才变得相当简单。

使用命名范围:

scope :for_stores, lambda{ |stores| where(store_id: stores.map(&:id)) }

我的控制器方法:

def search
  @stores = Store.local(current_user) # get local stores
  response = InventoryItem.search 'whatever' # execute the search
  @inventory_items = response.records.for_stores(@stores) # call records
end

在弹性搜索回复中,您可以拨打recordsresults。仅调用results只会产生您可以显示的索引的结果等。调用records实际上会拉出AR记录,这样您就可以像上面那样链接方法。凉!显然,docs中有更多信息。