全文索引

全文索引用于根据 STRING(字符串)属性对节点和关系进行索引。与仅能执行有限 STRING 匹配(精确、前缀、子字符串或后缀匹配)的范围索引文本索引不同,全文索引会存储给定 STRING 属性中的各个单词。这意味着全文索引可用于匹配 STRING 属性的内容。全文索引还会返回给定查询字符串与数据库中存储的 STRING 值之间的相似度得分,从而能够对数据进行语义解释。

全文索引由 Apache Lucene 索引和搜索库提供支持。

示例图

以下图表用于下方的示例

要重新创建它,请在空的 Neo4j 数据库中运行以下查询

CREATE (nilsE:Employee {name: "Nils-Erik Karlsson", position: "Engineer", team: "Kernel", peerReviews: ['Nils-Erik is difficult to work with.', 'Nils-Erik is often late for work.']}),
(lisa:Manager {name: "Lisa Danielsson", position: "Engineering manager"}),
(nils:Employee {name: "Nils Johansson", position: "Engineer", team: "Operations"}),
(maya:Employee {name: "Maya Tanaka", position: "Senior Engineer", team:"Operations"}),
(lisa)-[:REVIEWED {message: "Nils-Erik is reportedly difficult to work with."}]->(nilsE),
(maya)-[:EMAILED {message: "I have booked a team meeting tomorrow."}]->(nils)

创建全文索引

全文索引通过 CREATE FULLTEXT INDEX [index_name] 命令创建。建议在创建索引时为其指定名称。如果创建时未提供名称,系统将为该全文索引分配一个随机名称。

CREATE FULLTEXT INDEX 命令是可选幂等的。这意味着它的默认行为是:如果尝试创建两次相同的索引,则会抛出错误。如果在命令末尾添加 IF NOT EXISTS,则不会抛出错误,如果已存在同名索引或相同模式上的全文索引,也不会执行任何操作。相反,系统会返回一条信息性通知,显示阻止创建的现有索引。索引名称也可以作为参数给出,例如 CREATE FULLTEXT INDEX $name FOR …​

创建全文索引需要 CREATE INDEX 权限

创建全文索引时,需要指定其适用的标签/关系类型和属性名称。

此语句为标签为 EmployeeManager 的节点上的 nameteam 属性创建一个名为 namesAndTeams 的全文索引。

在节点标签和属性组合上创建全文索引
CREATE FULLTEXT INDEX namesAndTeams FOR (n:Employee|Manager) ON EACH [n.name, n.team]

此查询强调了全文索引与搜索性能索引之间的两个关键区别:

  • 全文索引可以应用于多个节点标签。

  • 全文索引可以应用于多个属性,但与复合搜索性能索引不同,全文索引存储的是至少具有一个已索引标签或关系类型,且至少具有一个已索引属性的实体。

同样,虽然一个关系只能有一种类型,但全文索引可以存储多种关系类型。在这种情况下,所有匹配至少一个关系类型且匹配至少一个已索引属性的关系都将被包含在内。

此语句为关系类型 REVIEWEDEMAILEDmessage 属性创建一个名为 communications 的全文索引。

在关系类型和属性组合上创建全文索引
CREATE FULLTEXT INDEX communications FOR ()-[r:REVIEWED|EMAILED]-() ON EACH [r.message]

分词与分析器

全文索引将 STRING 属性中的各个单词存储起来。这是通过分词器 (tokenizer) 实现的,它将字符流分解为单个标记(通常是单个单词)。STRING 如何被分词取决于全文索引配置的分析器 (analyzer)。默认分析器 (standard-no-stop-words) 会同时分析已索引的值和查询字符串。

停用词 (Stop words) 是语言中的常见词汇,由于它们在确定字符串含义时用处不大,因此可以在信息检索任务中被过滤掉。这些词通常很短,且在各种语境中频繁使用。

例如,Lucene 的 english 分析器中包含了以下停用词:“a”、“an”、“and”、“are”、“as”、“at”、“be”、“but”、“by”、“for”、“if”、“in”、“into”、“is”、“it”、“no”、“not”、“of”、“on”、“or”、“such”、“that”、“the”、“their”、“then”、“there”、“these”、“they”、“this”、“to”、“was”、“will” 以及 “with”。

移除停用词有助于减少存储数据的大小,从而提高数据检索的效率。

在某些情况下,对已索引值和查询字符串使用不同的分析器更为合适。例如,如果处理的是瑞典语编写的 STRING 值,选择 swedish 分析器可能更有利,因为它知道如何对瑞典语单词进行分词,并将避免索引瑞典语停用词。

db.index.fulltext.listAvailableAnalyzers() 存储过程显示所有可用的分析器。

Neo4j 还支持使用自定义分析器。有关详细信息,请参阅 Java 参考手册 → 全文索引分析器提供程序

配置设置

CREATE FULLTEXT INDEX 命令接受可选的 OPTIONS 子句,可在其中指定 indexConfig。以下语句使用参数为标签为 EmployeeManager 的节点创建全文索引。

参数
{
  "name": "peerReviews"
}
使用 OPTIONS 创建全文索引
CREATE FULLTEXT INDEX $name FOR (n:Employee|Manager) ON EACH [n.peerReviews]
OPTIONS {
  indexConfig: {
    `fulltext.analyzer`: 'english', (1)
    `fulltext.eventually_consistent`: true (2)
  }
}
1 fulltext.analyzer 设置可用于配置索引特定的分析器。在本例中,它被设置为 english 分析器。fulltext.analyzer 设置的可能值可以通过 db.index.fulltext.listAvailableAnalyzers() 存储过程列出。
2 如果将 fulltext.eventually_consistent 设置为 true,则会将索引置于最终一致性 (eventually consistent) 更新模式。这意味着更新将在后台线程中“尽快”应用,而不是像其他索引那样在事务提交期间应用。

有关如何配置全文索引的更多信息,请参阅 操作手册 → 支持全文搜索的索引

查询全文索引

搜索性能索引不同,全文索引不会被 Cypher® 查询规划器自动使用。要查询全文索引,请使用 db.index.fulltext.queryNodes()db.index.fulltext.queryRelationships() 存储过程。

当索引处于 POPULATING(填充中)状态时(创建后立即进入此状态),无法使用该索引。要检查全文索引的 state(是 ONLINE(可用)还是 POPULATING(仍在构建中;populationPercent 列显示创建进度)),请运行以下命令:SHOW FULLTEXT INDEXES

此查询使用 db.index.fulltext.queryNodes() 存储过程在之前创建的 namesAndTeams 全文索引中查找 nils

查询节点属性的全文索引
CALL db.index.fulltext.queryNodes("namesAndTeams", "nils") YIELD node, score
RETURN node.name, score
结果
node.name score

"Nils Johansson"

0.3300700783729553

"Nils-Erik Karlsson"

0.27725890278816223

行:2

许多全文索引分析器(包括 Neo4j 的默认分析器)会将标记标准化为小写。因此,当在 Neo4j 上使用时,全文索引默认是不区分大小写的。

score(得分)列代表索引认为条目与给定查询字符串的匹配程度。因此,除了精确匹配外,全文索引还会返回与给定查询字符串的近似匹配项。这是因为已索引的属性值和对索引的查询都会经过分析器处理,使得索引可以找到与所提供的 STRING 不完全匹配的数据实体。

score 结果始终以得分降序返回,匹配度最高的结果条目排在首位。

此查询使用 db.index.fulltext.queryRelationships() 存储过程查询之前创建的 communications 全文索引,以查找包含 "meeting" 的任何 message

查询关系属性的全文索引
CALL db.index.fulltext.queryRelationships("communications", "meeting") YIELD relationship, score
RETURN type(relationship), relationship.message, score
结果
type(relationship) relationship.message score

"EMAILED"

"I have booked a team meeting tomorrow."

0.3239005506038666

行:1

若要仅获取精确匹配,请为您搜索的 STRING 添加引号。

查询全文索引以获取精确匹配
CALL db.index.fulltext.queryNodes("namesAndTeams", '"Nils-Erik"') YIELD node, score
RETURN node.name, score
结果
node.name score

"Nils-Erik Karlsson"

0.7588480710983276

行:1

查询字符串还支持使用 Lucene 布尔运算符 (AND, OR, NOT, +, -)。

使用逻辑运算符查询全文索引
CALL db.index.fulltext.queryNodes("namesAndTeams", 'nils AND kernel') YIELD node, score
RETURN node.name, node.team, score
结果
node.name node.team score

"Nils-Erik Karlsson"

"Kernel"

0.723090410232544

行:1

可以通过在查询字符串前加上 <propertyName>: 来限制搜索特定的属性。

查询特定属性的全文索引
CALL db.index.fulltext.queryNodes("namesAndTeams", 'team:"Operations"') YIELD node, score
RETURN node.name, node.team, score
结果
node.name node.team score

"Nils Johansson"

"Operations"

0.21363800764083862

"Maya Tanaka"

"Operations"

0.21363800764083862

行:2

Lucene 查询语法的完整说明可以在 Lucene 文档中找到。

STRING 值列表

如果被索引的属性包含 STRING 值列表,则每个条目都会被独立分析,产生的所有标记都与同一个属性名称相关联。这意味着在查询此类已索引的节点或关系时,只要列表中的任何元素匹配查询字符串,即视为匹配。在评分方面,全文索引将其视为单个属性值,得分将代表查询与整个列表的匹配程度。

查询 STRING 属性列表中存在的内容的全文索引
CALL db.index.fulltext.queryNodes('peerReviews', 'late') YIELD node, score
RETURN node.name, node.peerReviews, score
结果
node.name node.peerReviews score

"Nils-Erik Karlsson"

["Nils-Erik is difficult to work with.", "Nils-Erik is often late for work."]

0.13076457381248474

显示全文索引

要列出数据库中的所有全文索引,请使用 SHOW FULLTEXT INDEXES 命令。

显示数据库中的所有全文索引
SHOW FULLTEXT INDEXES
结果
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| id | name             | state    | populationPercent | type       | entityType     | labelsOrTypes           | properties       | indexProvider  | owningConstraint | lastRead                 | readCount |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 4  | "communications" | "ONLINE" | 100.0             | "FULLTEXT" | "RELATIONSHIP" | ["REVIEWED", "EMAILED"] | ["message"]      | "fulltext-2.0" | NULL             | 2023-10-31T15:06:10.270Z | 2         |
| 3  | "namesAndTeams"  | "ONLINE" | 100.0             | "FULLTEXT" | "NODE"         | ["Employee", "Manager"] | ["name", "team"] | "fulltext-2.0" | NULL             | 2023-10-31T15:07:48.874Z | 5         |
| 6  | "peerReviews"    | "ONLINE" | 100.0             | "FULLTEXT" | "NODE"         | ["Employee", "Manager"] | ["peerReviews"]  | "fulltext-2.0" | NULL             | 2023-10-31T15:09:05.391Z | 3         |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

与搜索性能索引类似,SHOW 命令可以针对特定列进行过滤。

使用过滤显示全文索引
SHOW FULLTEXT INDEXES WHERE name CONTAINS "Team"
结果
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| id | name            | state    | populationPercent | type       | entityType | labelsOrTypes           | properties       | indexProvider  | owningConstraint | lastRead | readCount |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 5  | "namesAndTeams" | "ONLINE" | 100.0             | "FULLTEXT" | "NODE"     | ["Employee", "Manager"] | ["name", "team"] | "fulltext-2.0" | NULL             | NULL     | 0         |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

要返回完整的索引详细信息,请使用 YIELD 子句。例如:

显示所有全文索引和所有返回列
SHOW FULLTEXT INDEXES YIELD *
结果
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| id | name             | state    | populationPercent | type       | entityType     | labelsOrTypes           | properties       | indexProvider  | owningConstraint | lastRead | readCount | trackedSince             | options                                                                                                 | failureMessage | createStatement                                                                                                                                                                                                   |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 4  | "communications" | "ONLINE" | 100.0             | "FULLTEXT" | "RELATIONSHIP" | ["REVIEWED", "EMAILED"] | ["message"]      | "fulltext-2.0" | NULL             | NULL     | 0         | 2023-11-01T09:27:57.024Z | {indexConfig: {`fulltext.analyzer`: "standard-no-stop-words", `fulltext.eventually_consistent`: FALSE}} | ""             | "CREATE FULLTEXT INDEX `communications` FOR ()-[r:`REVIEWED`|`EMAILED`]-() ON EACH [r.`message`] OPTIONS {indexConfig: {`fulltext.analyzer`: 'standard-no-stop-words',`fulltext.eventually_consistent`: false}}"  |
| 5  | "namesAndTeams"  | "ONLINE" | 100.0             | "FULLTEXT" | "NODE"         | ["Employee", "Manager"] | ["name", "team"] | "fulltext-2.0" | NULL             | NULL     | 0         | 2023-11-01T12:24:48.002Z | {indexConfig: {`fulltext.analyzer`: "standard-no-stop-words", `fulltext.eventually_consistent`: FALSE}} | ""             | "CREATE FULLTEXT INDEX `namesAndTeams` FOR (n:`Employee`|`Manager`) ON EACH [n.`name`, n.`team`] OPTIONS {indexConfig: {`fulltext.analyzer`: 'standard-no-stop-words',`fulltext.eventually_consistent`: false}}"  |
| 6  | "peerReviews"    | "ONLINE" | 100.0             | "FULLTEXT" | "NODE"         | ["Employee", "Manager"] | ["peerReviews"]  | "fulltext-2.0" | NULL             | NULL     | 0         | 2023-11-01T12:25:41.495Z | {indexConfig: {`fulltext.analyzer`: "english", `fulltext.eventually_consistent`: TRUE}}                 | ""             | "CREATE FULLTEXT INDEX `peerReviews` FOR (n:`Employee`|`Manager`) ON EACH [n.`peerReviews`] OPTIONS {indexConfig: {`fulltext.analyzer`: 'english',`fulltext.eventually_consistent`: true}}"                       |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

有关所有返回列的完整说明,请参阅 搜索性能索引 → 列出索引 → 列出索引的结果列

删除全文索引

删除全文节点索引使用与其他索引相同的命令,即 DROP INDEX

在以下示例中,之前创建的 communications 全文索引将从数据库中删除。

删除全文索引
DROP INDEX communications

删除索引时,索引名称也可以作为参数给出:DROP INDEX $name

全文索引存储过程列表

全文索引的存储过程列于下表中。

用法 存储过程/命令 描述

最终一致性索引。

db.index.fulltext.awaitEventuallyConsistentIndexRefresh()

等待最近提交的事务的更新被应用到任何最终一致的全文索引中。

列出可用分析器。

db.index.fulltext.listAvailableAnalyzers()

列出全文索引可以配置的可用分析器。

使用全文节点索引。

db.index.fulltext.queryNodes()

查询给定的全文索引。返回匹配的节点及其 Lucene 查询得分,按得分排序。

使用全文关系索引。

db.index.fulltext.queryRelationships()

查询给定的全文索引。返回匹配的关系及其 Lucene 查询得分,按得分排序。

总结

  • 全文索引支持节点和关系的索引。

  • 全文索引仅包含 STRINGLIST<STRING> 类型的属性值。

  • 全文索引通过 Cypher 存储过程进行访问。

  • 全文索引为查询的每个结果返回得分 (score)

  • 全文索引支持配置自定义分析器,包括 Lucene 本身未包含的分析器。

  • 全文索引可以使用 Lucene 查询语言进行查询。

  • 随着节点和关系的添加、删除和修改,全文索引会自动保持更新。

  • 全文索引会自动使用存储中的现有数据填充新创建的索引。

  • 全文索引可以由一致性检查器进行检查,如果出现问题,可以重建它们。

  • 新创建的全文索引会自动填充数据库中的现有数据。

  • 全文索引可以在单个索引中支持任意数量的属性。

  • 全文索引是在事务中创建、删除和更新的,并会在整个集群中自动复制。

  • 全文索引可以配置为最终一致性,此时索引更新将从提交路径转移到后台线程。使用此功能,可以绕过性能关键的提交过程中缓慢的 Lucene 写入,从而消除 Neo4j 写入性能的主要瓶颈。