【问题标题】:Rails elasticsearch - named scope searchRails elasticsearch - 命名范围搜索
【发布时间】:2014-11-18 02:47:36
【问题描述】:

我刚刚从轮胎 gem 转移到官方 elasticsearch Ruby wrapper 并且正在努力实现更好的搜索功能。

我有一个模型 InventoryItem 和一个模型 StoreStore has_many :inventory_items。我在 Store 上有一个模型范围,名为 local

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

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

【问题讨论】:

    标签: ruby-on-rails elasticsearch


    【解决方案1】:

    您可以做的另一件事是将有效 ID 列表直接发送到 Elastic,这样它就会自行过滤掉记录,然后对留下的记录执行搜索。我们还没有测试它是否更快,但我认为应该,因为elastic毕竟是一个搜索引擎。

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

    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

    【讨论】:

    • 我还没有测试过这个,但我同意它可以更快。我会奖励赏金 - 谢谢你的详细回答。我会对此进行测试并在有机会时提供反馈。
    • 非常感谢!您的回答为我提供了一个不错的模板,我终于能够构建一个使用一组 id 进行过滤的搜索(我在 Rails 上找不到正确的方法,并且一直出现解析错误)。
    • 这太棒了!我一直在寻找一种将范围与弹性搜索结合起来的方法。
    【解决方案2】:

    在赏金结束之前,我会留下这个答案而不接受 - 如果您认为您找到了比以下更好的解决方案,请随时添加答案。

    经过一番挖掘,这个问题的答案最终变得相当简单。

    使用命名范围:

    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 显然有更多信息。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-08-06
      • 1970-01-01
      • 1970-01-01
      • 2014-10-09
      • 1970-01-01
      • 1970-01-01
      • 2019-01-22
      • 1970-01-01
      相关资源
      最近更新 更多