あなたのような状況で対処する必要があるいくつかのことがあります。完全に機能するコードから始めましょう:
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
クエリを「汎用」クエリとして使用します。タイヤmulti_match
に砂糖をまぶして、クエリなどを簡単に作成できるようにします。
あなたの例のフィルター構文は正しいです。このメソッドがTirefilter
で複数回呼び出されると、作成およびフィルタリングされます。and
タイヤロギング構成(および場合によってはActiveRecordロギングも)のコメントを解除して、コードが何をしているかを確認します。