搜索引擎
此处列出的通常是高级搜索引擎使用的底层引擎.
Java编写的开源搜索库.
被Elasticsearch和Apache Solr使用.
被Elasticsearch和Apache Solr使用.
Rust编写的全文搜索引擎库, 受Apache Lucene启发, 作者在其博客详述了技术细节.
tantivy比Lucene更快.
文档很差, 有很多地方与代码不一致或干脆没有文档, 阅读源码是必须的.
文档很差, 有很多地方与代码不一致或干脆没有文档, 阅读源码是必须的.
中文支持是由第三方分词器完成的.
tantivy使用LZ4压缩文档存储.
为英文维基百科的500万篇文章建立索引, 速度很快, 索引在合并后甚至比原始数据还要小一点.
作为使用硬盘存储的搜索引擎, 查询性能很好, 2个关键字的OR查询可以在100ms左右返回相关性排序的结果.
即使在低内存的单核机器上也非常有效率.
作为使用硬盘存储的搜索引擎, 查询性能很好, 2个关键字的OR查询可以在100ms左右返回相关性排序的结果.
即使在低内存的单核机器上也非常有效率.
tantivy的分词器设计要求在分词时返回文本的位置信息, 但由于jieba在分词时不会提供位置信息,
cang-jie采用了一种先进行分词, 然后再生成位置信息的方案:
https://github.com/DCjanus/cang-jie/blob/b828f21939dae6dcb0a4cdaaa0f147e0e8263af2/src/stream.rs
cang-jie采用了一种先进行分词, 然后再生成位置信息的方案:
https://github.com/DCjanus/cang-jie/blob/b828f21939dae6dcb0a4cdaaa0f147e0e8263af2/src/stream.rs
然而, 对于All/ForSearch这样的选项, 由于会将歧义内容多次分词, 返回的文本是比实际的文本更多的.
将这些选项用于分词时, 返回的位置信息一定是错误的.
将这些选项用于分词时, 返回的位置信息一定是错误的.
与cang-jie不同, 使用了jieba-rs的TokenizeMode, 在输出时会产生正确的位置信息.
考虑到tantivy还没有达到v1.0版本, 加上各种API缺失的问题,
没有必要考虑这些绑定方式, 自己封装可能还比较方便.
没有必要考虑这些绑定方式, 自己封装可能还比较方便.
tantivy官方的Python3绑定.
除非修改源代码, 否则无法在tantivy-py使用第三方tokenizer.
同时也缺少配置这些可配置tokenizer的选项, 例如cang_jie的hmm开关和分词方案.
https://github.com/tantivy-search/tantivy-py/issues/25
同时也缺少配置这些可配置tokenizer的选项, 例如cang_jie的hmm开关和分词方案.
https://github.com/tantivy-search/tantivy-py/issues/25
不支持在搜索时用facet缩小结果范围.
https://github.com/tantivy-search/tantivy-py/pull/21
https://github.com/tantivy-search/tantivy-py/pull/21
用neon实现的第三方Node.js绑定.
该项目的包装事实上是非常浅的, 并且不是TypeScript.
作者只给出了最基础的示例, 高级使用方式必须从源码倒推.
作者只给出了最基础的示例, 高级使用方式必须从源码倒推.
面临和tantivy-py一样的第三方tokenzier支持问题.
用neon实现的第三方Node.js绑定, 使用的是napi.
已经两年没有更新.
面临和tantivy-py一样的第三方tokenzier支持问题.
面临和tantivy-py一样的第三方tokenzier支持问题.
tantivy的Node.js包装, 使用stdio作为RPC接口.
由于采用了这种包装形式, 代码比neon要复杂得多.
由于采用了这种包装形式, 代码比neon要复杂得多.
面临和tantivy-py一样的第三方tokenzier支持问题.
- 1.创建索引的schema, tantivy的索引是静态的, 因此必须事先创建好.
- 2.创建一个具有固定大小内存缓冲区的索引写入器IndexWriter.
- 3.将文档(JSON)插入到索引编写器的队列.
当文档占用空间超出缓冲区时, 写入器会阻塞直到它将文档编制成serialized segment. - 4.提交更新(commit), 索引写入器将segment正式应用到索引, 变成committed segment.
commit完成之前, segment被称作uncommitted segment, 无法被搜索到.
频繁调用commit会加剧segment的碎片化, 进而导致需要更多的时间执行merge.
执行commit所需的时间与未纳入索引的文档量直接相关.. - 5.合并(merge)碎片段(fragmented segments):
tantivy索引由segment组成, 这一步骤即是将索引里碎片化的segment合并.
执行此操作会减少索引的磁盘用量(尤其是对全新的索引来说, 体积会大幅减小), 并且提高冷索引的查询性能.
该步骤是可选的, 执行merge需要很长时间, 因此会长时间阻塞IndexWriter, 并且需要临时占用额外的磁盘空间.
文档对于schema的描述寥寥, 建议阅读源代码.
从tantivy 0.17开始, 支持JSON字段, 从而支持无模式索引, 无模式在一些场景里会有一定性能损失.
- name 字段名称
[a-zA-Z0-9_]
str
u64
i64
f64
HierarchicalFacet
分面类型是一种值必须以/
开头的文本类型数据, 可以会以数组的形式表示多个值, 例如:["/category/search/server", "/language/rust"]
在查询时可以以前缀的形式在查询字符串中指定需要查询的facet.bytes
Date
(rfc3339): 在内部会转换为u64类型的UTC时间戳(根据文档, 实际上是i64).
根据Document的文档和源码, 可知tantivy里文档的字段允许有多个值(内存表示为
同名的重复字段JSON里是以数组的形式表达的, 但 只有最后一个值会被索引.
(field, value)[]
),同名的重复字段JSON里是以数组的形式表达的, 但 只有最后一个值会被索引.
- stored 决定是否将值持久化存储, 未持久化的字段将不会出现在返回的文档中.
停止持久化那些仅用于搜索的字段, 可以大幅缩小tantivy的数据库体积.
- indexing, 此项可为
null
, 代表不索引: - record:
basic
只记录文档id.freq
记录文档id和词频, 词频会被用于排序.position
记录文档id, 词频和出现在文档中的位置, 必须开启此项才能使用短语查询.
- tokenizer: 分词器
default
raw
不分词default
简单分词en_stem
struct Token { offset_from: usize, offset_to: usize, position: usize, text: String, position_length: usize,}
- indexing, boolean值, 决定是否索引.
- fast:
等价于Apache Lucene的DocValues.
这是一种列式索引, 可以优化排序和分面(facet)的性能.
该属性的取值来源于源代码中名为Cardinality的枚举.
- SingleValue: JSON表示为
single
, 顾名思义只能有一个元素.
内部使用IntFastFieldWriter进行写入, 在存储时还会更新该字段当前的最大值和最小值. - MultiValues: JSON表示为
multi
, 顾名思义可以有多个元素, 该选项被用于支持数字数组.
需要的内存和CPU比SingleValue要多.
HierachicalFacet的属性是隐含的.
在内部, 尽管HerachicalFacet字段是文本类型, 但它会自动获得值为MultiValues的fast属性.
在内部, 尽管HerachicalFacet字段是文本类型, 但它会自动获得值为MultiValues的fast属性.
tantivy使用QueryParser自定的语法进行查询.
order_by_u64_field和order_by_fast_field仅支持降序排序:
https://github.com/tantivy-search/tantivy/issues/906
https://github.com/tantivy-search/tantivy/issues/906
一种临时的解决方案是通过tweak_score或custom_score自行实现自定义排序.
在编写测试时会发现, 使用TopDocs搜索出来的结果排序不稳定, 尽管文档声称它是稳定的.
深入研究会发现, 排序不稳定的原因实际上和TopDocs无关,
而是因为IndexWriter的多线程写入导致文档的顺序不稳定:
https://github.com/quickwit-oss/tantivy/issues/1284
深入研究会发现, 排序不稳定的原因实际上和TopDocs无关,
而是因为IndexWriter的多线程写入导致文档的顺序不稳定:
https://github.com/quickwit-oss/tantivy/issues/1284
https://docs.rs/tantivy/0.15.2/tantivy/query/struct.QueryParser.html
tantivy自带的查询解析器, 有自己特定的语法.
parse_query只能按term查找, term的大小写不敏感.
parse_query只能按term查找, term的大小写不敏感.
关键字转义通过给关键字左右加上双引号实现.
text:"OR"
单独的
因此会抛出
如果用
-field:value
是不能像一般意义上的"排除某值"那样正常工作的,因此会抛出
QueryParserError::NoDefaultFieldDeclared
.如果用
()
包装 negative terms, 可以绕过错误检查, 但也同样不能如期工作.以下形式都是错误的:
-field:value
* -field:value
field:* -field:value
another_field:some_value -field:value
正确的使用方式是
field:value1 -field:value2
.IndexWriter.commit函数返回仅代表索引更新完毕, 并不能代表IndexReader重新加载了索引.
因此会出现调用commit后立即通过IndexReader进行搜索, 却与最新数据不符的情况.
因此会出现调用commit后立即通过IndexReader进行搜索, 却与最新数据不符的情况.
根据文档, IndexReader默认的自动重新加载会在10ms完成, 但实际使用中似乎需要更多时间.
在测试环境下, 应该通过IndexReader.reload函数手动重新加载, 以确保加载了最新的数据.
在测试环境下, 应该通过IndexReader.reload函数手动重新加载, 以确保加载了最新的数据.
tantivy支持基于term的删除, 因此提交的文档应该带有某个可以作为唯一标识的字段.
与写入一样, 删除操作只有在提交后才会正式应用到索引, 即使对
IndexWriter.delete_all_documents
也不例外.Go编写的全文搜索引擎库, 受Apache Lucene启发.
没有内置中文分词器.
在一项2019年的性能测试中, bleve是最慢的底层引擎.
https://github.com/blugelabs/bluge
Go编写的文本索引库.
由C++编写的提供RESTful API的搜索引擎.
有非常多的commits和很大的版本号, 足以证明这是一个成熟的项目.
有非常多的commits和很大的版本号, 足以证明这是一个成熟的项目.
已经2年没有积极维护, 一些绑定已经有10年没有实质性维护.
不支持中文.
PISA是一个将索引保存在内存里的搜索引擎, 可建立的索引大小受内存限制.
全文搜索前端, 当前后端为PostgreSQL.
为Django/Python设计的全文搜索前端, 支持Solr, Elasticsearch, Whoosh, Xapian作为后端.
https://github.com/django-haystack/django-haystack
https://github.com/django-haystack/django-haystack
商业公司出品, 部分开源.
Java编写, 基于Lucene.
Java编写, 基于Lucene.
8GB(最低), 16GB, 32GB, 64GB(推荐)
推荐将50%的内存分配给堆内存.
堆内存超过32GB时会遇到性能问题.
堆内存超过32GB时会遇到性能问题.
2~8核心
单机的性能比主机数量重要.
当需要构建非常复杂的搜索时, Elasticsearch这样的重型通用搜索引擎可以很好的发挥作用(例如日志分析).
与之相对的, 如果只需要一些简单的关键字查询, Elasticsearch很可能是大材小用.
与之相对的, 如果只需要一些简单的关键字查询, Elasticsearch很可能是大材小用.
Apache出品, 开源.
Java编写, 基于Lucene.
Java编写, 基于Lucene.
内存用量取决于具体情况,
但通常来说至少需要分配1GB的堆内存才能让Solr正常运作, 因此需要至少2GB的物理内存.
但通常来说至少需要分配1GB的堆内存才能让Solr正常运作, 因此需要至少2GB的物理内存.
基于tantivy的搜索引擎.
具有比Bayard高级的功能, 可以用JSON构建查询.
该项目还远未达到完善的程度, 几乎没有文档, 并且在2019年的活跃之后就缺少大的项目进展.
该项目还远未达到完善的程度, 几乎没有文档, 并且在2019年的活跃之后就缺少大的项目进展.
基于tantivy实现的搜索引擎前端, 具有CLI, gRPC和REST服务器等多种访问方式.
和tantivy距离很近, 但比tantivy的文档全面.
内置了n-gram分词器.
支持中文分词, 由cang-jie实现(jieba-rs的tantivy绑定).
支持中文分词, 由cang-jie实现(jieba-rs的tantivy绑定).
Bayard直接通过JSON定义tantivy的schema, 但没有做任何抽象层:
Schema结构体是直接通过serde_json读取JSON得来的, 因为Schema实现了serde的Deserialize特型.
Schema结构体是直接通过serde_json读取JSON得来的, 因为Schema实现了serde的Deserialize特型.
如果需要使用多个索引, 则必须开多个服务器.
任何形式的错误都会导致bayard服务器停止响应.
可以认为该项目目前还非常不成熟, 没有使用价值.
例如commit和merge是通过GET方法调用的.
基于bleve的搜索引擎.
不支持中文, 但支持日文分词器.
基于bluge的搜索引擎.
https://github.com/prabhatsharma/zinc
基于Xapian的RESTful搜索引擎.
该项目缺乏积极维护.
使用外部全文搜索引擎需要从DBMS数据同步, 这会带来很多问题.
内建于DBMS全文搜索引擎可以免去数据同步过程, 直接在数据库里建立索引.
尽管这一优点常常与缺乏中文分词支持的DBMS无缘, 但仍然可以免去同步其他非全文搜索字段的麻烦.
内建于DBMS全文搜索引擎可以免去数据同步过程, 直接在数据库里建立索引.
尽管这一优点常常与缺乏中文分词支持的DBMS无缘, 但仍然可以免去同步其他非全文搜索字段的麻烦.
PostgreSQL内置了全文搜索功能, 可以与GIN索引组合实现全文搜索引擎.
https://github.com/zombodb/zombodb
ZomboDB是一项PostgreSQL扩展, 可以让ElasticSearch成为PostgreSQL的表索引.
SQLite内置了全文搜索引擎FTS5.
由Rust编写的即时搜索引擎, 在提供搜索栏功能时, 比Elasticsearch快很多.
MeiliSearch的灵感源于Algolia发布的博客文章, 某种程度上可以视作是Algolia的开源实现.
v0.20及以下版本的引擎是为千万级以下的文档设计的, 和Typesense一样使用Trie数据结构.
据称v0.21开始使用的新引擎可以支持千万级数据.
据称v0.21开始使用的新引擎可以支持千万级数据.
支持中文分词(通过jieba-rs实现).
项目的文档很全面, 使用起来极其简单.
目前不能分布式部署.
项目的文档很全面, 使用起来极其简单.
目前不能分布式部署.
建立索引的过程是异步的, 仅传输数据到MeiliSearch的速度非常快,
但至少在v0.20版本里, 仅支持单线程编制索引, 速度非常慢, 比Sonic还要慢得多.
据称在v0.21的新引擎里, 支持多线程编制索引
(即便如此最终生成的索引大小也仍不可小觑, 所以对大型数据集来说仍然很难称得上有可用性).
异步也导致一些错误的操作不会立即被发现, 例如facted search的字段类型错误.
但至少在v0.20版本里, 仅支持单线程编制索引, 速度非常慢, 比Sonic还要慢得多.
据称在v0.21的新引擎里, 支持多线程编制索引
(即便如此最终生成的索引大小也仍不可小觑, 所以对大型数据集来说仍然很难称得上有可用性).
异步也导致一些错误的操作不会立即被发现, 例如facted search的字段类型错误.
MeiliSearch不是仅依靠内存工作的,
它使用的存储引擎Lightning Memory-Mapped Database Manager (LMDB)是一个具有ACID特性的内存映射数据库.
MeiliSearch会尽可能利用内存, 内存越大, 则读取硬盘的次数越少, 搜索速度也就越快.
当内存和硬盘上的索引大小相同时, MeiliSearch将达到最高性能.
注: MeiliSearch创建的索引被认为比一般的全文搜索引擎要大.
它使用的存储引擎Lightning Memory-Mapped Database Manager (LMDB)是一个具有ACID特性的内存映射数据库.
MeiliSearch会尽可能利用内存, 内存越大, 则读取硬盘的次数越少, 搜索速度也就越快.
当内存和硬盘上的索引大小相同时, MeiliSearch将达到最高性能.
注: MeiliSearch创建的索引被认为比一般的全文搜索引擎要大.
在v1.0发布之前, 数据库在不同版本之间不兼容.
可以修改查询的相关性排名规则, 手动指定某个字段来影响排序结果.
MeiliSearch支持过滤器, 过滤字段必须是number, boolean, string或这三种数据类型之一的数组.
所有类型都可以使用
number类型支持
在比较string时, 不会区分大小写.
=
, !=
运算符.number类型支持
>
, >=
, <
, <=
运算符.在比较string时, 不会区分大小写.
支持
逻辑运算是短路的.
NOT
, AND
, OR
.逻辑运算是短路的.
MeiliSearch提供的一种缩小搜索结果的方式, 它可以将数据按特定字段的值划分为小的子集,
从而减少搜索的文档数量, 概念上很接近于数据库的分区技术.
从而减少搜索的文档数量, 概念上很接近于数据库的分区技术.
分面搜索的字段被定义在索引的设置里, 会导致索引的重建.
从性能的角度考虑, 应该优先使用分面搜索.
为了快速查询, MeiliSearch在搜索时只会考虑查询字符串的 前10个词.
文档中的单个字符串字段最多只包含1000个词, 超过1000个词时, 剩余的内容不会被索引.
MeiliSearch建立的索引体积非常大, 在常见的情况下, 会产生同等JSON文件10倍左右大小的索引,
尽管实际使用的物理内存可能远小于索引大小, 但如此巨大的索引体积仍然是不可接受的.
尽管实际使用的物理内存可能远小于索引大小, 但如此巨大的索引体积仍然是不可接受的.
此外, MeiliSearch也没有回收掉已经删除的空间的能力, 被删除的空间只能被用于后来插入的新文档.
由C++编写的轻量级开源搜索引擎.
文档比较简陋.
文档比较简陋.
在功能定位上与MeiliSearch非常相似.
Typesense将索引 完全载入到内存, 将文档保存在RocksDB里.
出于Typesense的升级性考虑, 启动时需要从文档里重新索引, 因此在大数据集上需要很长的启动时间.
Typesense将索引 完全载入到内存, 将文档保存在RocksDB里.
出于Typesense的升级性考虑, 启动时需要从文档里重新索引, 因此在大数据集上需要很长的启动时间.
可分布式部署.
即时搜索引擎服务商, 提供搜索即服务(SaaS).
客户将数据通过RESTful API推送到Algolia, 然后将搜索框添加到网页上.
https://www.algolia.com/doc/guides/managing-results/optimize-search-results/handling-natural-languages-nlp/in-depth/normalization/
Algolia会在分词前将文本标准化, 例如将繁体中文转换为"现代中文"(即"简体中文").
作为Redis模块(由C编写)构建的分布式全文搜索和聚合引擎.
索引和查询性能都远高于ElasticSearch.
由Rust编写的标识符索引引擎, 搜索引擎本身并不存储文档.
被视作Elasticsearch轻量级替代品和全文搜索领域的Redis, 可以在几RAM的机器上运行, 速度非常快.
被视作Elasticsearch轻量级替代品和全文搜索领域的Redis, 可以在几RAM的机器上运行, 速度非常快.
Sonic直接在文件系统上进行搜索(在后端使用RocksDB), SSD对Sonic来说是必须的.
Sonic创建出来的索引显著小于人们对全文搜索引擎的印象, 创建出的索引会比原始数据更小而不是更大.
使用结巴进行分词的PR已经被合并进主分支, 因此在之后的版本里理应能够使用结巴分词的索引.
旧版本也原生支持中文, 但旧版本的中文分词是基于逐字索引实现的.
可能与逐字索引或Sonic的实现原理有关, 并不能真正查询到所有符合条件的中文结果.
可能与逐字索引或Sonic的实现原理有关, 并不能真正查询到所有符合条件的中文结果.
作者曾在一个issue中说可以通过
在v1.3.0版本尝试过用此方式进行中文手动分词+逐词push, 获得的结果与不设定
lang: none
选项阻止在push和query时的语言特定行为,在v1.3.0版本尝试过用此方式进行中文手动分词+逐词push, 获得的结果与不设定
lang: none
时完全一样.collection 类似于数据库里的database
bucket 类似于数据库里的table
object 类似于数据库里的rowId
terms 类似于搜索关键字keyword
bucket 类似于数据库里的table
object 类似于数据库里的rowId
terms 类似于搜索关键字keyword
对大部分使用者的应用场景来说, 可能不需要bucket, 在这种情况下可以简单地使用default作为缺省名称.
- 无法实现相关性排名.
- 不支持一次查询多个索引.
因此, 每次只能查询一个关键字(term), 用户需要自行整合查询结果(即手动处理AND, OR). - 不支持批量插入数据.
Sonic只能通过编程方式逐个插入数据, 导入数据可能需要很长时间.
- 默认情况下的日志等级是用于开发的, 如果不修改或限制很快就会塞满磁盘.
注: 不要将它与Python的文档生成器Sphinx混淆.
据称有非常好的性能表现.
有自己的SQL方言SphinxQL, 模拟了MySQL服务器, 可以用MySQL客户端连接Sphinx.
提供HTTP API和多种语言的Native API.
Sphinx内置了连接到MySQL, MariaDB, PostgreSQL, ODBC提取数据的功能.
提供HTTP API和多种语言的Native API.
Sphinx内置了连接到MySQL, MariaDB, PostgreSQL, ODBC提取数据的功能.
遗憾的是, 最新版本Sphinx(Sphinx3, 于2017年末发布)的文档并不完善, 对于很多关键特性没有给出示例.
支持中文.
由JavaScript构建出的小型搜索引擎库, 用于在内存中搜索数据.
自称是速度最快的JavaScript全文搜索库, 在内存占用和搜索速度上都大幅领先于其他库.