【问题标题】:elastic search object association querying through params通过参数进行弹性搜索对象关联查询
【发布时间】:2013-10-22 05:34:35
【问题描述】:

我在使用 Elastic Search 时遇到了一些困难,并且 Tyre 没有返回任何结果。我正在使用 Ruby 1.9.3 和 Rails 3.2.11。

在我的控制器中,我正在调用:

@location_id = 1
@listings = Listing.search(params.merge!(location_id: @location_id))

在我的列表模型中,我有

mapping do
    indexes :id, type: 'integer'
    ...
    indexes :author do
      indexes :location_id,     :type => 'integer', :index => :not_analyzed
      ...
end

def self.search(params={})
      tire.search(load: true, page: params[:page], per_page: 20) do |search|

      search.query  { string params[:query], :default_operator => "AND" } if params[:query].present?
      search.filter :range, posted_at: {lte: DateTime.now}

      search.filter :term, "author.location_id"       => params[:location_id]
end

我有 300 个结果,它们在数据库中的 location_id 都为 1,所以我似乎无法弄清楚为什么它返回一个 nil 集?如果我注释掉 author.location_id 搜索过滤器行,它会按预期返回所有其他结果?

【问题讨论】:

    标签: elasticsearch tire


    【解决方案1】:

    在像您这样的情况下,有几件事需要解决。让我们从一个完整的工作代码开始:

    require 'active_record'
    require 'tire'
    require 'logger'
    
    # Tire.configure { logger STDERR }
    # ActiveRecord::Base.logger = Logger.new(STDERR)
    
    Tire.index('articles').delete
    
    ActiveRecord::Base.establish_connection( adapter: 'sqlite3', database: ":memory:" )
    ActiveRecord::Schema.define(version: 1) do
      create_table :articles do |t|
        t.string :title
        t.integer :author_id
        t.date    :posted_at
        t.timestamps
      end
      create_table :authors do |t|
        t.string  :name
        t.integer :number, :location_id
        t.timestamps
      end
      add_index(:articles, :author_id)
      add_index(:authors, :location_id)
    end
    
    class Article < ActiveRecord::Base
      belongs_to :author, touch: true
      self.include_root_in_json = false
    
      include Tire::Model::Search
      include Tire::Model::Callbacks
    
      mapping do
        indexes :title
    
        indexes :author do
          indexes :location_id, type: 'integer'
        end
      end
    
      def self.search(params={})
        tire.search load: {include: 'author'} do |search|
          search.query do |query|
            query.filtered do |f|
              f.query { params[:query].present? ? match([:title], params[:query], operator: 'and') : match_all }
              f.filter :range, 'posted_at' => { lte: DateTime.now }
              f.filter :term,  'author.location_id' => params[:location_id]
            end
          end
        end
      end
    
      def to_indexed_json
        to_json( only: ['title', 'posted_at'], include: { author: { only: [:location_id] } } )
      end
    end
    
    class Author < ActiveRecord::Base
      has_many :articles
    
      after_touch do
        articles.each { |a| a.tire.update_index }
      end
    end
    
    # -----
    
    Author.create id: 1, name: 'John', location_id: 1
    Author.create id: 2, name: 'Mary', location_id: 1
    Author.create id: 3, name: 'Abby', location_id: 2
    
    Article.create title: 'Test A', author: Author.find(1), posted_at: 2.days.ago
    Article.create title: 'Test B', author: Author.find(2), posted_at: 1.day.ago
    Article.create title: 'Test C', author: Author.find(3), posted_at: 1.day.ago
    Article.create title: 'Test D', author: Author.find(3), posted_at: 1.day.from_now
    
    Article.index.refresh
    
    # -----
    
    articles = Article.search query: 'test', location_id: 1
    puts "", "Documents with location:1", '-'*80
    articles.results.each { |a| puts "* TITLE: #{a.title}, LOCATION: #{a.author.location_id}, DATE: #{a.posted_at}" }
    
    articles = Article.search query: 'test', location_id: 2
    puts "", "Documents with location:2", '-'*80
    articles.results.each { |a| puts "* TITLE: #{a.title}, LOCATION: #{a.author.location_id}, DATE: #{a.posted_at}" }
    puts "(NOTE: 'D' is missing, because is not yet posted)"
    
    articles = Article.search query: 'test b', location_id: 1
    puts "", "Documents with query:B and location:1", '-'*80
    articles.results.each { |a| puts "* TITLE: #{a.title}, LOCATION: #{a.author.location_id}, DATE: #{a.posted_at}" }
    

    首先,像这样创建一个孤立的、提取的案例通常是个好主意。

    在您的示例代码中,我假设您有一个关系Listing belongs_to :author。您需要正确定义映射和序列化,我再次假设您这样做了。

    至于查询本身:

    • 除非您使用分面导航,否则请使用 filtered 查询,而不是顶级过滤器,如我的示例代码中所示。

    • 不要使用 string 查询,除非您真的想向您的用户展示 Lucene 查询字符串查询的所有功能(和脆弱性!)。

    • 使用 match 查询,作为您的“通用目的”查询 -- Tire 在上面撒上一些糖,可以轻松创建 multi_match 查询等 p>

    • 您的示例中的过滤器语法是正确的。当filter 方法在Tire 中被多次调用时,它会创建and 过滤器。

    取消注释 Tire 日志配置(可能还有 ActiveRecord 日志),以查看代码在做什么。

    【讨论】:

      猜你喜欢
      • 2016-05-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-17
      • 2014-09-29
      • 1970-01-01
      • 2013-07-27
      相关资源
      最近更新 更多