全文索引

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

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

示例图

以下图用于下面的示例

full text graph

要重新创建它,请对空的 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命令创建。建议在创建索引时为其命名。如果创建时未指定名称,则会为全文索引分配随机名称。

CREATE FULLTEXT INDEX命令可以选择性地是幂等的。这意味着其默认行为是在尝试两次创建相同索引时抛出错误。如果将IF NOT EXISTS附加到命令,则如果已存在具有相同名称的索引或在相同模式上的全文索引,则不会抛出错误,并且不会发生任何事情。从 Neo4j 5.17 开始,改为返回信息通知,显示阻止创建的现有索引。从 Neo4j 5.16 开始,索引名称也可以作为参数给出,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属性中的单个单词。这是通过标记器实现的,标记器将字符流分解成单个标记(通常是单个单词)。STRING如何标记化取决于全文索引配置的分析器。默认分析器(standard-no-stop-words)分析已索引的值和查询字符串。

停用词是在信息检索任务期间可以过滤掉的语言中的常用词,因为在确定字符串的含义时,它们被认为几乎没有用处。这些词通常很短,并且在各种上下文中经常使用。

例如,Lucene 的英语分析器中包含以下停用词:“a”、“an”、“and”、“are”、“as”、“at”、“be”、“but”等。

删除停用词可以帮助减少存储数据的规模,从而提高数据检索效率。

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

所有可用分析器的完整列表包含在db.index.fulltext.listAvailableAnalyzers过程的结果中。

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

配置设置

CREATE FULLTEXT INDEX命令采用可选的OPTIONS子句,其中可以指定indexConfig。以下语句使用参数为标签为EmployeeManager的节点创建全文索引。在 Neo4j 5.16 中引入了使用参数创建和删除索引的功能。

参数
{
  "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分析器。可以使用db.index.fulltext.listAvailableAnalyzers过程列出fulltext.analyzer设置的可能值。
2 如果将fulltext.eventually_consistent 设置为 true,则会将索引置于最终一致性更新模式。这意味着更新将在后台线程中“尽快”应用,而不是在事务提交期间应用,而其他索引则是在事务提交期间应用。

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

查询全文索引

要查询全文索引,请使用db.index.fulltext.queryNodesdb.index.fulltext.queryRelationships过程。

与其他搜索性能索引不同,全文索引不会被Cypher®查询计划器自动使用。要访问全文索引,必须使用上述过程显式调用它们。

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

查询节点属性的全文索引
CALL db.index.fulltext.queryNodes("namesAndTeams", "nils") YIELD node, score
RETURN node.name, score
表 1. 结果
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
表 2. 结果
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
表 3. 结果
node.name score

"Nils-Erik Karlsson"

0.7588480710983276

行数:1

查询字符串也支持使用Lucene 布尔运算符ANDORNOT+-

使用逻辑运算符查询全文索引
CALL db.index.fulltext.queryNodes("namesAndTeams", 'nils AND kernel') YIELD node, score
RETURN node.name, node.team, score
表 4. 结果
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
表 5. 结果
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
表 6. 结果
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-1.0" | NULL             | 2023-10-31T15:06:10.270Z | 2         |
| 3  | "namesAndTeams"  | "ONLINE" | 100.0             | "FULLTEXT" | "NODE"         | ["Employee", "Manager"] | ["name", "team"] | "fulltext-1.0" | NULL             | 2023-10-31T15:07:48.874Z | 5         |
| 6  | "peerReviews"    | "ONLINE" | 100.0             | "FULLTEXT" | "NODE"         | ["Employee", "Manager"] | ["peerReviews"]  | "fulltext-1.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-1.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-1.0" | NULL             | NULL     | 0         | 2023-11-01T09:27:57.024Z | {indexConfig: {`fulltext.analyzer`: "standard-no-stop-words", `fulltext.eventually_consistent`: FALSE}, indexProvider: "fulltext-1.0"} | ""             | "CREATE FULLTEXT INDEX `communications` FOR ()-[r:`REVIEWED`|`EMAILED`]-() ON EACH [r.`message`] OPTIONS {indexConfig: {`fulltext.analyzer`: 'standard-no-stop-words',`fulltext.eventually_consistent`: false}, indexProvider: 'fulltext-1.0'}" |
| 5  | "namesAndTeams"  | "ONLINE" | 100.0             | "FULLTEXT" | "NODE"         | ["Employee", "Manager"] | ["name", "team"] | "fulltext-1.0" | NULL             | NULL     | 0         | 2023-11-01T12:24:48.002Z | {indexConfig: {`fulltext.analyzer`: "standard-no-stop-words", `fulltext.eventually_consistent`: FALSE}, indexProvider: "fulltext-1.0"} | ""             | "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}, indexProvider: 'fulltext-1.0'}" |
| 6  | "peerReviews"    | "ONLINE" | 100.0             | "FULLTEXT" | "NODE"         | ["Employee", "Manager"] | ["peerReviews"]  | "fulltext-1.0" | NULL             | NULL     | 0         | 2023-11-01T12:25:41.495Z | {indexConfig: {`fulltext.analyzer`: "english", `fulltext.eventually_consistent`: TRUE}, indexProvider: "fulltext-1.0"}                 | ""             | "CREATE FULLTEXT INDEX `peerReviews` FOR (n:`Employee`|`Manager`) ON EACH [n.`peerReviews`] OPTIONS {indexConfig: {`fulltext.analyzer`: 'english',`fulltext.eventually_consistent`: true}, indexProvider: 'fulltext-1.0'}"                      |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

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

删除全文索引

全文节点索引通过使用与其他索引相同的命令DROP INDEX来删除。

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

删除全文索引
DROP INDEX communications

从 Neo4j 5.16 开始,在删除索引时也可以将索引名称作为参数提供:DROP INDEX $name

全文索引过程列表

下表列出了全文索引的过程

用法 过程/命令 描述

最终一致性索引。

db.index.fulltext.awaitEventuallyConsistentIndexRefresh

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

列出可用的分析器。

db.index.fulltext.listAvailableAnalyzers

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

使用全文节点索引。

db.index.fulltext.queryNodes

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

使用全文关系索引。

db.index.fulltext.queryRelationships

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

总结

  • 全文索引支持对节点和关系进行索引。

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

  • 通过 Cypher 过程访问全文索引。

  • 全文索引返回查询每个结果的分数

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

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

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

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

  • 全文索引可以通过一致性检查器进行检查,如果存在问题,可以重建它们。

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

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

  • 全文索引以事务方式创建、删除和更新,并自动复制到整个集群中。

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