我有以下结构。
CREATE
(`0` :Sentence {`{text`:'This is a sentence'}}) ,
(`1` :Word {`{ text`:'This' }}) ,
(`2` :Word {`{text`:'is'}}) ,
(`3` :Sentence {`{'text'`:'Sam is a dog'}}) ,
(`0`)-[:`RELATED_TO`]->(`1`),
(`0`)-[:`RELATED_TO`]->(`2`),
(`3`)-[:`RELATED_TO`]->(`2`)
架构示例
所以我的问题是这个。我有一堆句子,我已经分解成单词对象。这些单词对象都是唯一的,因此将指向不同的句子。如果我搜索一个单词,很容易找出与该单词相关的所有句子。如何构建查询以找出两个单词而不是一个单词的相同信息。
我想提交两个或更多单词,并找到一条包含所有提交的单词的路径,选择所有感兴趣的句子。
我只记得一种可能效果更好的替代方法。将此查询的配置文件与其他查询的配置文件进行比较,看看它是否更适合您。
WITH {myListOfWords} as wordList
WITH wordList, size(wordList) as wordCnt
MATCH (s)-[:RELATED_TO]->(w:Word)
WHERE w.text in wordList
WITH s, wordCnt, count(DISTINCT w) as cnt
WHERE wordCnt = cnt
RETURN s
不幸的是,这不是一个非常漂亮的方法,它基本上归结为收集 :Word 节点并使用 ALL() 谓词来确保您想要的模式适用于集合的所有元素。
MATCH (w:Word)
WHERE w.text in {myListOfWords}
WITH collect(w) as words
MATCH (s:Sentence)
WHERE ALL(word in words WHERE (s)-[:RELATED_TO]->(word))
RETURN s
丑陋的是,规划者现在还不够聪明,无法推断出当你MATCH (s:Sentence) WHERE ALL(word in words ...
说s
的初始匹配应该来自你words
集合中第一个w
的匹配时,所以它首先从所有 :Sentence 节点开始,这是一个重大的性能打击。
因此,为了解决这个问题,我们必须从words
集合的第一个集合中显式匹配,然后对其余集合使用WHERE ALL()
。
MATCH (w:Word)
WHERE w.text in {myListOfWords}
WITH w, size(()-[:RELATED_TO]->(w)) as rels
WITH w ORDER BY rels ASC
WITH collect(w) as words
WITH head(words) as head, tail(words) as words
MATCH (s)-[:RELATED_TO]->(head)
WHERE ALL(word in words WHERE (s)-[:RELATED_TO]->(word))
RETURN s
编辑:
添加了一项优化,以按传入的 :RELATED_TO 关系的程度对w
节点进行排序(这是对极少数节点的度数查找),因为这意味着与 :Sentence 节点的初始匹配是过滤其余单词关系之前的最小起始集。
作为替代方法,您可以考虑使用手动索引(也称为"旧版索引"),而不是使用Word
节点和RELATED_TO
关系。手动索引支持使用 lucene 进行"全文"搜索。
有许多 apoc 程序可以帮助您解决这个问题。
下面是一个可能适合您的示例。在此示例中,我假设不区分大小写的比较是可以的,您保留Sentence
节点(及其text
属性),并且您希望将所有Sentence
节点的text
属性自动添加到手动索引中。
-
如果您使用的是 neo4j 3.2+,则必须将此设置添加到
neo4j.conf
文件中,以使一些昂贵的apoc.index
过程(如apoc.index.addAllNodes
)可用:dbms.security.procedures.unrestricted=apoc.*
-
执行此 Cypher 代码以使用所有现有
Sentence
节点中的text
文本初始化名为"WordIndex"的手动索引,并从该点开始启用自动索引:CALL apoc.index.addAllNodes('WordIndex', {Sentence: ['text']}, {autoUpdate: true}) YIELD label, property, nodeCount RETURN *;
-
要查找(不区分大小写)包含集合中所有单词(通过
$words
参数传递)的Sentence
节点,您需要执行如下所示的 Cypher 语句。WITH
子句为您构建lucene
查询字符串(例如,"foo AND bar")。警告:由于 lucene 的特殊布尔术语(如"AND"和"OR")总是大写的,你应该确保你传入的单词是小写的(或根据需要修改下面的WITH
子句以使用 TOLOWER()' 函数)。WITH REDUCE(s = $words[0], x IN $words[1..] | s + ' AND ' + x) AS q CALL apoc.index.search('WordIndex', q) YIELD node RETURN node;