シンプルだが強力な、Redis 上の検索エンジンである RediSearch の Ruby ラッパー。
まず、Redis と RediSearch をインストールする必要があります。
Redis は https://redis.io/download からダウンロードでき、インストール手順はここで確認できます。あるいは、macOS または Linux では、Homebrew 経由でインストールできます。
RediSearch をインストールするには、https://oss.redislabs.com/redisearch/Quick_Start.html をチェックしてください。 RediSearch を構築したら、Docker を使用していない場合は、 redis.conf ファイルを更新して、常にloadmodule /path/to/redisearch.so
を使用して RediSearch モジュールをロードすることができます。 (macOS では、 redis.conf ファイルは/usr/local/etc/redis.conf
にあります)
Redis と RediSearch が起動して実行されたら、Gemfile に次の行を追加します。
gem 'redi_search'
その後:
❯ bundle
または、自分でインストールします。
❯ gem install redi_search
そしてそれを要求します:
require 'redi_search'
gem がインストールされて必要になったら、Redis 構成を使用して gem を構成する必要があります。 Rails を使用している場合は、これをイニシャライザ ( config/initializers/redi_search.rb
) に含める必要があります。
RediSearch . configure do | config |
config . redis_config = {
host : "127.0.0.1" ,
port : "6379"
}
end
RediSearch は検索インデックスを中心に展開しているため、検索インデックスとは何かを定義することから始めましょう。 Swiftype によると:
検索インデックスは、特定のクエリに関連する結果を検索するときに検索エンジンが参照する構造化データの本体です。インデックスは、検索エンジンのアルゴリズムの特定の情報取得方法に合わせて調整する必要があるため、検索システムにとって重要な部分です。このように、アルゴリズムとインデックスは互いに密接に関係しています。 Index は動詞 (インデックス作成) としても使用でき、検索エンジンのアルゴリズムに合わせて構造化された形式で非構造化 Web サイトのデータを収集するプロセスを指します。
インデックスについて考える 1 つの方法は、検索インフラストラクチャとオフィスのファイリング システムの間の次の類似点を考慮することです。あなたがインターンに何千もの紙片 (書類) の束を渡し、会社がより効率的に情報を見つけられるようにファイルキャビネット (インデックス) にこれらの紙片を整理するように指示したと想像してください。インターン生はまず書類を整理し、書類に含まれるすべての情報を把握する必要があります。次に書類をファイリングキャビネットに整理するシステムを決定し、最後に書類が何であるかを決定する必要があります。ファイルがキャビネットに入ったら、ファイルを検索して選択するための最も効果的な方法です。この例では、論文を整理してファイリングするプロセスが Web サイトのコンテンツにインデックスを付けるプロセスに対応し、これらの整理されたファイルを検索して最も関連性の高いファイルを見つける方法が検索アルゴリズムに対応します。
これにより、インデックス内のフィールドとそれらのフィールドのプロパティが定義されます。スキーマは単純な DSL です。各フィールドは、地理、数値、タグ、またはテキストの 4 つのタイプのいずれかであり、多くのオプションを指定できます。スキーマの簡単な例は次のとおりです。
RediSearch :: Schema . new do
text_field :first_name
text_field :last_name
end
各タイプでサポートされているオプションは次のとおりです。
オプションなし: text_field :name
text_field :name, weight: 2
text_field :name, phonetic: 'dm:en'
text_field :name, sortable: true
sortable
と併用すると便利です。フィールドにno_index
があり、 sortable
がない場合、そのフィールドはインデックスによって無視されます。text_field :name, no_index: true
text_feidl :name, no_stem: true
オプションなし: numeric_field :price
numeric_field :id, sortable: true
sortable
と併用すると便利です。フィールドにno_index
があり、 sortable
がない場合、そのフィールドはインデックスによって無視されます。numeric_field :id, no_index: true
オプションなし: tag_field :tag
tag_field :tag, sortable: true
sortable
と併用すると便利です。フィールドにno_index
があり、 sortable
がない場合、そのフィールドはインデックスによって無視されます。tag_field :tag, no_index: true
tag_field :tag, separator: ','
オプションなし: geo_field :place
geo_field :place, sortable: true
sortable
と併用すると便利です。フィールドにno_index
があり、 sortable
がない場合、そのフィールドはインデックスによって無視されます。geo_field :place, no_index: true
Document
Redis ハッシュの Ruby 表現です。
.get
クラスのメソッドを使用してDocument
を取得できます。
get(index, document_id)
指定されたdocument_id
のIndex
内の単一のDocument
フェッチします。 .for_object(index, record, only: [])
クラス メソッドを使用してDocument
インスタンスを作成することもできます。 Index
インスタンスと Ruby オブジェクトを受け取ります。そのオブジェクトは、 Index
のSchema
で指定されたすべてのフィールドに応答する必要があります。はスキーマからフィールドの配列only
を受け入れ、 Document
に渡されるフィールドを制限します。
Document
のインスタンスを取得すると、 Index
のSchema
でメソッドとdocument_id
として指定されたすべてのフィールドに応答します。 document_id
、一意性を確保するためでない限り、自動的にIndex
の名前の前に付加されます。 Index
名を先頭に追加するのは、異なるIndex
に同じ ID を持つ 2 つのDocument
がある場合、 Document
が互いにオーバーライドされないようにするためです。先頭に付加されたインデックス名を削除する#document_id_without_index
メソッドもあります。
最後に、 Index
からDocument
削除する#del
メソッドがあります。
Index
初期化するには、文字列またはシンボルとしてIndex
の名前とSchema
ブロックを渡します。
RediSearch :: Index . new ( name_of_index ) do
text_field :foobar
end
create
false
を返します。いくつかのオプションを受け入れます。max_text_fields: #{true || false}
add_field
使用して追加のフィールド (32 を超える) を追加できるようになります。no_offsets: #{true || false}
no_highlight
暗黙的に示します。temporary: #{seconds}
seconds
続くと期限切れになる軽量の一時インデックスを作成します。内部アイドル タイマーは、インデックスが検索または追加されるたびにリセットされます。このようなインデックスは軽量であるため、パフォーマンスに悪影響を与えることなく、そのようなインデックスを何千も作成できます。no_highlight: #{true || false}
no_highlight
はno_offsets
にも暗黙的に含まれます。no_fields: #{true || false}
no_frequencies: #{true || false}
drop(keep_docs: false)
Index
を削除し、ブール値を返します。失敗時に例外を発生させる bang メソッドが付属しています。 Index
がすでに削除されている場合はfalse
返します。オプション キーワード arg であるkeep_docs
を受け取ります。これにより、デフォルトで Redis 内のすべてのドキュメント ハッシュが削除されます。exist?
Index
存在を示すブール値を返します。info
Index
に関するすべての情報を含む構造体オブジェクトを返します。fields
Index
内のフィールド名の配列を返します。add(document)
Document
オブジェクトを取得します。失敗時に例外を発生させる bang メソッドが付属しています。add_multiple(documents)
Document
オブジェクトの配列を取得します。これにより、複数のドキュメントをIndex
に追加するためのより効率的な方法が提供されます。 add
と同じオプションを受け入れます。del(document)
Index
からDocument
削除します。document_count
Index
内のDocument
の数を返します。add_field(name, type, **options, &block)
Index
に追加します。index.add_field(:first_name, :text, phonetic: "dm:en")
reindex(documents, recreate: false)
recreate
がtrue
の場合、 Index
は削除され、再作成されます。 検索は、連結可能な句を含むRediSearch::Index
インスタンスから開始されます。検索すると、すべてのスキーマ フィールドのパブリック リーダー メソッドを含むDocument
の配列が返されます。
main ❯ index = RediSearch :: Index . new ( "user_idx" ) { text_field :name , phonetic : "dm:en" }
main ❯ index . add RediSearch :: Document . for_object ( index , User . new ( "10039" , "Gene" , "Volkman" ) )
main ❯ index . add RediSearch :: Document . for_object ( index , User . new ( "9998" , "Jeannie" , "Ledner" ) )
main ❯ index . search ( "john" )
RediSearch ( 1.1 ms ) FT . SEARCH user_idx `john`
=> [ #<RediSearch::Document:0x00007f862e241b78 first: "Gene", last: "Volkman", document_id: "10039">,
#<RediSearch::Document:0x00007f862e2417b8 first: "Jeannie", last: "Ledner", document_id: "9998">]
シンプルなフレーズクエリ- hello AND world
index . search ( "hello" ) . and ( "world" )
完全に一致するフレーズのクエリ- hello FOLLOWED BY world
index . search ( "hello world" )
ユニオンクエリ- hello OR world
index . search ( "hello" ) . or ( "world" )
否定クエリ- hello AND NOT world
index . search ( "hello" ) . and . not ( "world" )
複雑な交差と結合:
# Intersection of unions
index . search ( index . search ( "hello" ) . or ( "halo" ) ) . and ( index . search ( "world" ) . or ( "werld" ) )
# Negation of union
index . search ( "hello" ) . and . not ( index . search ( "world" ) . or ( "werld" ) )
# Union inside phrase
index . search ( "hello" ) . and ( index . search ( "world" ) . or ( "werld" ) )
すべての用語は、適用できるいくつかのオプションをサポートしています。
接頭辞の用語: 接頭辞で始まるすべての用語と一致します。 (SQL のlike term%
)
index . search ( "hel" , prefix : true )
index . search ( "hello worl" , prefix : true )
index . search ( "hel" , prefix : true ) . and ( "worl" , prefix : true )
index . search ( "hello" ) . and . not ( "worl" , prefix : true )
オプションの用語: オプションの用語を含むドキュメントは、含まれないドキュメントよりも上位にランクされます。
index . search ( "foo" ) . and ( "bar" , optional : true ) . and ( "baz" , optional : true )
ファジー用語: レーベンシュタイン距離 (LD) に基づいて照合が実行されます。サポートされる最大レーベンシュタイン距離は 3 です。
index . search ( "zuchini" , fuzziness : 1 )
where
句を使用して、検索語の範囲を特定のフィールドに限定することもできます。
# Simple field specific query
index . search . where ( name : "john" )
# Using where with options
index . search . where ( first : "jon" , fuzziness : 1 )
# Using where with more complex query
index . search . where ( first : index . search ( "bill" ) . or ( "bob" ) )
数値フィールドの検索には範囲を指定します。
index . search . where ( number : 0 .. 100 )
# Searching to infinity
index . search . where ( number : 0 .. Float :: INFINITY )
index . search . where ( number : - Float :: INFINITY .. 0 )
slop(level)
in_order
slop
と組み合わせて使用されます。クエリ用語間のオフセットに関係なく、クエリ用語がクエリ内と同じ順序でDocument
内に表示されるようにします。no_content
Document
ID のみを返し、コンテンツは返しません。これは、 Document
属性が重要ではなく、 ActiveRecord
オブジェクトに変換される Rails モデルで RediSearch が使用されている場合に便利です。language(language)
Document
をクエリする場合、クエリ用語を適切にトークン化するために、これを中国語に設定する必要があります。サポートされていない言語が送信された場合、コマンドはエラーを返します。sort_by(field, order: :asc)
:asc
または:desc
ですlimit(num, offset = 0)
offset
で指定されたnum
に制限します。デフォルトの制限は10
に設定されています。count
Document
の数を返します。highlight(fields: [], opening_tag: "<b>", closing_tag: "</b>")
fields
強調表示されるフィールドの配列です。verbatim
no_stop_words
with_scores
Document
の相対的な内部スコアを含めます。これを使用して、複数のインスタンスからの結果をマージできます。これにより、返されたDocument
インスタンスにscore
メソッドが追加されます。return(*fields)
Document
から返されるフィールドを制限します。explain
スペルチェックはRediSearch::Index
インスタンスから開始され、スペルが間違っている検索語の候補を提供します。スペル提案の最大レーベンシュタイン距離であるオプションのdistance
引数を取ります。各要素に各検索語の候補と、インデックス内の出現に基づいた正規化されたスコアが含まれる配列を返します。
main ❯ index = RediSearch :: Index . new ( "user_idx" ) { text_field :name , phonetic : "dm:en" }
main ❯ index . spellcheck ( "jimy" )
RediSearch ( 1.1 ms ) FT . SPELLCHECK user_idx jimy DISTANCE 1
=> [ #<RediSearch::Spellcheck::Result:0x00007f805591c670
term : "jimy" ,
suggestions :
[ #<struct RediSearch::Spellcheck::Suggestion score=0.0006849315068493151, suggestion="jimmy">,
#<struct RediSearch::Spellcheck::Suggestion score=0.00019569471624266145, suggestion="jim">]>]
main ❯ index . spellcheck ( "jimy" , distance : 2 ) . first . suggestions
RediSearch ( 0.5 ms ) FT . SPELLCHECK user_idx jimy DISTANCE 2
=> [ #<struct RediSearch::Spellcheck::Suggestion score=0.0006849315068493151, suggestion="jimmy">,
#<struct RediSearch::Spellcheck::Suggestion score=0.00019569471624266145, suggestion="jim">]
Rails との統合は非常に簡単です。モデル内からschema
キーワード引数を使用してredi_search
呼び出します。元:
class User < ApplicationRecord
redi_search do
text_field :first , phonetic : "dm:en"
text_field :last , phonetic : "dm:en"
end
end
これにより、 Index
インスタンスで呼び出した場合と同じように動作するUser.search
とUser.spellcheck
メソッドが自動的に追加されます。
User.reindex(recreate: false, only: [])
も追加され、 RediSearch::Index#reindex
と同様に動作します。相違点には次のようなものがあります。
Document
を最初のパラメータとして渡す必要はありません。 search_import
スコープが自動的に呼び出され、すべてのレコードがDocument
に変換されます。only
パラメーターを受け入れます。スキーマを変更し、特定のフィールドのインデックスのみを作成する必要がある場合に便利です。スキーマを定義する際、オプションでブロックを渡すことができます。ブロックが渡されない場合は、値を取得するためにモデルに対してname
が呼び出されます。ブロックが渡された場合、フィールドの値はブロックの呼び出しを通じて取得されます。
class User < ApplicationRecord
redi_search do
text_field :name do
" #{ first_name } #{ last_name } "
end
end
end
モデルのsearch_import
スコープをオーバーライドして、インデックス作成時に関係を一括ロードしたり、インデックスを作成するレコードを制限したりすることができます。
class User < ApplicationRecord
scope :search_import , -> { includes ( :posts ) }
end
検索すると、デフォルトでDocument
のコレクションが返されます。検索クエリで#results
呼び出すと、検索が実行され、データベース内で見つかったすべてのレコードが検索され、ActiveRecord リレーションが返されます。
モデルIndex
のデフォルトのIndex
名は#{model_name.plural}_#{RediSearch.env}
です。 redi_search
メソッドは、インデックス名の前に付加されるオプションのindex_prefix
引数を受け取ります。
class User < ApplicationRecord
redi_search index_prefix : 'prefix' do
text_field :first , phonetic : "dm:en"
text_field :last , phonetic : "dm:en"
end
end
User . search_index . name
# => prefix_users_development
RediSearch をモデルに統合すると、レコードの作成と更新後に自動的にインデックスが作成され、破棄時にIndex
から削除されます。
公開されている便利なメソッドがさらにいくつかあります。
search_document
RediSearch::Document
インスタンスとして返しますremove_from_index
Index
からレコードを削除しますadd_to_index
Index
に追加しますsearch_index
RediSearch::Index
インスタンスを返しますリポジトリをチェックアウトした後、 bin/setup
実行して依存関係をインストールします。次に、 rake test
実行して、単体テストと統合テストの両方を実行します。それらを個別に実行するにはrake test:unit
またはrake test:integration
実行できます。 bin/console
実行して対話型プロンプトを表示し、実験することもできます。
この gem をローカル マシンにインストールするには、 bundle exec rake install
実行します。新しいバージョンをリリースするには、 bin/publish (major|minor|patch)
を実行します。これにより、 version.rb
内のバージョン番号が更新され、そのバージョンの git タグが作成され、git のコミットとタグがプッシュされ、 .gem
ファイルが Rubygems にプッシュされます。 .org と GitHub。
バグレポートとプルリクエストは GitHub で歓迎されます。このプロジェクトは、安全で居心地の良いコラボレーションの場となることを目的としており、貢献者は貢献者規約の行動規範を遵守することが期待されます。
この gem は、MIT ライセンスの条件に基づいてオープン ソースとして利用できます。
RediSearch プロジェクトのコードベース、問題トラッカー、チャット ルーム、メーリング リストでやり取りするすべての人は、行動規範に従うことが期待されます。