警告
该宝石目前尚未维护,并且开发被搁置。如果您有兴趣接管,请随时与我联系。
需要快速全文搜索您的Ruby脚本,但是Solr和Elasticsearch是过度的? ?
你在正确的地方。 Tantiny是基于Tanti V Y的Ruby的简约全文搜索库(Apache Lucene用Rust的Apache Lucene的出色替代品)。当您的任务需要进行全文搜索时,这非常有用,但是配置成熟的分布式搜索引擎将比任务本身更多。而且,即使您已经在项目中使用了这样的引擎(实际上很可能),但使用Tantiny可能仍然更容易,因为与Solr和Elasticsearch不同,它也不需要任何工作(无单独的服务器或进程或其他任何东西),它纯粹可以嵌入。因此,当您发现自己选择的搜索引擎的情况下会很棘手/不足或需要其他设置时,您始终可以恢复回到一个快速而肮脏的解决方案,尽管如此,该解决方案仍然是灵活而快速的。
Tantiny并不是Ruby与Tantivy的结合,但它试图接近。主要理念是提供对Tantivy倒置索引的低水平访问权限,但具有良好的红宝石风格的API,明智的默认值和额外的功能。
看看最基本的示例:
index = Tantiny :: Index . new ( "/path/to/index" ) { text :description }
index << { id : 1 , description : "Hello World!" }
index << { id : 2 , description : "What's up?" }
index << { id : 3 , description : "Goodbye World!" }
index . reload
index . search ( "world" ) # 1, 3 将此行添加到您的应用程序的gemfile:
gem "tantiny"然后执行:
$ bundle install
或自行安装:
$ gem install tantiny
您不必在系统上安装生锈,因为Tantiny会尝试下载安装过程中GitHub版本上托管的预编译的二进制文件。但是,如果您的系统没有找到预编译的二进制文件(这是平台,架构和Ruby版本的组合),则需要先安装Rust。
警告
仅支持生锈版本高达1.77 。有关更多详细信息,请参阅此问题。
重要的
请确保在声明依赖tantiny时指定次要版本。 API是可以更改的主题,直到达到1.0.0次要版本中的颠簸很可能表示破裂的变化。
您必须指定通往索引存储位置的途径,并定义架构的块:
Tantiny :: Index . new "/tmp/index" do
id :imdb_id
facet :category
string :title
text :description
integer :duration
double :rating
date :release_date
end这是每个字段类型的描述:
| 类型 | 描述 |
|---|---|
| ID | 指定存储文档的ID的位置(默认为:id )。 |
| 方面 | 具有/animals/birds (即分层类别)等价值观的字段。 |
| 细绳 | 带有未象征化的文本字段。 |
| 文本 | 带有指定令牌的文本字段。 |
| 整数 | 具有整数值的字段。 |
| 双倍的 | 具有浮点值的字段。 |
| 日期 | 具有DateTime类型或转换为其他类型的字段。 |
您可以为索引索引任何具有模式中指定的方法的对象,但普通的哈希也有效:
rio_bravo = OpenStruct . new (
imdb_id : "tt0053221" ,
type : '/western/US' ,
title : "Rio Bravo" ,
description : "A small-town sheriff enlists a drunk, a kid and an old man to help him fight off a ruthless cattle baron." ,
duration : 141 ,
rating : 8.0 ,
release_date : Date . parse ( "March 18, 1959" )
)
index << rio_bravo
hanabi = {
imdb_id : "tt0119250" ,
type : "/crime/Japan" ,
title : "Hana-bi" ,
description : "Nishi leaves the police in the face of harrowing personal and professional difficulties. Spiraling into depression, he makes questionable decisions." ,
duration : 103 ,
rating : 7.7 ,
release_date : Date . parse ( "December 1, 1998" )
}
index << hanabi
brother = {
imdb_id : "tt0118767" ,
type : "/crime/Russia" ,
title : "Brother" ,
description : "An ex-soldier with a personal honor code enters the family crime business in St. Petersburg, Russia." ,
duration : 99 ,
rating : 7.9 ,
release_date : Date . parse ( "December 12, 1997" )
}
index << brother为了更新文档,只需再次添加它(只要ID相同):
rio_bravo . rating = 10.0
index << rio_bravo您还可以根据需要删除它:
index . delete ( rio_bravo . imdb_id )如果您需要执行多个写作操作(即多个),则应始终使用transaction :
index . transaction do
index << rio_bravo
index << hanabi
index << brother
end交易小组一口气更改并将其提交索引。这比一一执行这些更改更有效。实际上,当您在交易之外调用它们时,所有写作操作(即<<和delete )都隐含地包裹在交易中,因此,在交易之外致电<< 10次与执行10个单独的交易相同。
Tantiny是线程安全的,这意味着您可以安全地共享线程之间的索引的单个实例。您还可以产生可以从相同索引写入并读取的单独过程。但是,虽然从索引阅读应该是平行的,但写信并非如此。每当您调用transaction或任何其他修改索引(即<<和delete )的操作时,它将在操作期间锁定索引,或等待其他过程或线程释放锁定。唯一的例外是,当有另一个带有索引的过程中,在这种情况下,在这种情况下,修改索引的方法将立即失败。
因此,如果要避免阻止呼叫,最好拥有一个单个作者流程和许多读者进程。做到这一点的正确方法是在初始化索引时将exclusive_writer设置为true :
index = Tantiny :: Index . new ( "/path/to/index" , exclusive_writer : true ) { }这样,索引作者只会仅一次获取一次,这意味着它的内存和索引线程也只会被分配一次。否则,每次执行写作操作时都会获得新的索引作者。
首先重新加载您的索引是最新的:
index . reload并搜索它(终于!):
index . search ( "a drunk, a kid, and an old man" )默认情况下,它将返回10个最佳匹配文档的ID,但是您可以自定义:
index . search ( "a drunk, a kid, and an old man" , limit : 100 )您可能想知道,它到底是如何进行搜索的?好吧,默认行为是在模式中定义的所有text字段上使用smart_query搜索(请参见下文)。因此,您可以传递smart_query在此处接受的参数:
index . search ( "a dlunk, a kib, and an olt mab" , fuzzy_distance : 1 )但是,您可以通过从基本构建块中撰写自己的查询来自定义它:
popular_movies = index . range_query ( :rating , 8.0 .. 10.0 )
about_sheriffs = index . term_query ( :description , "sheriff" )
crime_movies = index . facet_query ( :cetegory , "/crime" )
long_ass_movies = index . range_query ( :duration , 180 .. 9999 )
something_flashy = index . smart_query ( :description , "bourgeoisie" )
index . search ( ( popular_movies & about_sheriffs ) | ( crime_movies & ! long_ass_movies ) | something_flashy )我知道,怪异的味道!但是很酷,是吗?看看下面的所有可用查询。
| 询问 | 行为 |
|---|---|
| all_query | 返回所有索引文件。 |
| empty_query | 完全返回(内部使用)。 |
| term_query | 包含指定术语的文档。 |
| fuzzy_term_query | 包含Levenshtein距离内指定术语的文档。 |
| phrase_query | 包含指定术语顺序的文档。 |
| REGEX_QUERY | 包含与指定正则匹配的术语的文档。 |
| prefix_query | 包含带有指定前缀的术语的文档。 |
| range_query | 记录指定范围内使用integer , double或date字段的文档。 |
| facet_query | 属于指定类别的文档。 |
| smart_query | term_query , fuzzy_term_query和prefix_query的组合。 |
查看签名文件,以查看询问接受哪些参数。
所有查询都可以在Multuple字段上搜索( facet_query除外,因为那里没有意义)。
因此,以下查询:
index . term_query ( %i[ title description ] , "hello" )等同于:
index . term_query ( :title , "hello" ) | index . term_query ( :description , "hello" )所有查询都支持boost参数,该参数允许在搜索中凸起文档位置:
about_cowboys = index . term_query ( :description , "cowboy" , boost : 2.0 )
about_samurai = index . term_query ( :description , "samurai" ) # sorry, Musashi...
index . search ( about_cowboys | about_samurai )smart_query行为smart_query搜索将使用相应的字段Tokenizer从您的查询字符串中提取术语,并在索引中搜索通过term_query包含这些术语的文档。如果指定了fuzzy_distance参数,则将使用fuzzy_term_query 。另外,它允许使用prefix_query未完成最后一项。
因此,以下查询:
index . smart_query ( %i[ en_text ru_text ] , "dollars рубли eur" , fuzzy_distance : 1 )等同于:
t1_en = index . fuzzy_term_query ( :en_text , "dollar" )
t2_en = index . fuzzy_term_query ( :en_text , "рубли" )
t3_en = index . fuzzy_term_query ( :en_text , "eur" )
t3_prefix_en = index . prefix_query ( :en_text , "eur" )
t1_ru = index . fuzzy_term_query ( :ru_text , "dollars" )
t2_ru = index . fuzzy_term_query ( :ru_text , "рубл" )
t3_ru = index . fuzzy_term_query ( :ru_text , "eur" )
t3_prefix_ru = index . prefix_query ( :ru_text , "eur" )
( t1_en & t2_en & ( t3_en | t3_prefix_en ) ) | ( t1_ru & t2_ru & ( t3_ru | t3_prefix_ru ) )请注意,根据我们正在搜索的字段,“美元”和“可”的单词如何有所不同。这是假设我们在模式中具有en_text和ru_text字段,分别使用英语和俄罗斯的词干令牌。
regex_query regex_query接受正则表达式,但必须是Rust Regex,而不是Ruby Regexp 。因此,您需要使用index.regex_query(:description, /hel[lp]/)而不是index.regex_query(:description, "hel[lp]") 。附带说明, regex_query非常快,因为它在内部使用FST板条箱。
因此,我们已经不止一次地提到了令牌。这些是什么?
Tokenizers是Tantivy用来将您的文本切成术语以构建倒置索引的方法。然后,您可以通过这些术语搜索索引。这是一个重要的概念要理解,以便您在index.term_query(:description, "Hello")时不会感到困惑,因为Hello不是一个术语,但是hello 。在搜索索引之前,您必须从查询中提取术语。目前,只有smart_query为您做到这一点。另外,唯一的字段类型是text ,因此对于string字段,您应该使用确切的匹配(即index.term_query(:title, "Hello") )。
默认情况下,使用了simple令牌,但是您可以通过索引选项或本地特定选项在全球范围内指定所需的令牌:
en_stemmer = Tantiny :: Tokenizer . new ( :stemmer )
ru_stemmer = Tantiny :: Tokenizer . new ( :stemmer , language : :ru )
Tantiny :: Index . new "/tmp/index" , tokenizer : en_stemmer do
text :description_en
text :description_ru , tokenizer : ru_stemmer
end简单的令牌剂在标点符号和空间上划定了文本,删除了长令牌,并降低了文本。
tokenizer = Tantiny :: Tokenizer . new ( :simple )
tokenizer . terms ( "Hello World!" ) # ["hello", "world"]Stemmer Tokenizer完全像简单的令牌,但根据指定的语言(默认为英语),还具有额外的词干。
tokenizer = Tantiny :: Tokenizer . new ( :stemmer , language : :ru )
tokenizer . terms ( "Привет миру сему!" ) # ["привет", "мир", "сем"]看看来源,看看支持哪些语言。
ngram tokenizer将您的文本推入指定尺寸的ngram上。
tokenizer = Tantiny :: Tokenizer . new ( :ngram , min : 5 , max : 10 , prefix_only : true )
tokenizer . terms ( "Morrowind" ) # ["Morro", "Morrow", "Morrowi", "Morrowin", "Morrowind"] 您可能已经注意到search方法仅返回文档ID。这是设计。文档本身并未存储在索引中。 Tantiny是一个简约的图书馆,因此它试图使事情变得简单。如果您需要检索完整的文档,请使用Redis之类的键值商店。
检查回购后,运行bin/setup以安装依赖关系。然后,运行rake build以构建本机扩展,然后rake spec运行测试。您还可以运行bin/console以获得交互提示,该提示可以让您进行实验。
我们使用常规承诺自动生成变频器,颠簸语义版本,并发布和发布宝石。您需要做的就是坚持惯例,CI将为您照顾其他一切。
欢迎在https://github.com/baygeldin/tantiny上的GitHub上的错误报告和拉动请求。
根据MIT许可证的条款,该宝石可作为开源。