我有名为"选项"的节点。"用户"选择这些选项。我需要一个像这样工作的chpher查询:
检索选择了所有选项的用户,这些选项以列表形式提供。
MATCH (option:Option)<-[:CHOSE]-(user:User) WHERE option.Key IN ['1','2','2'] Return user
此查询为我提供了选择选项 (1)、选项 (2) 和选项(3) 的用户,也为我提供了仅选择 option (2) 的用户。
我需要的只是选择所有这些的用户 - 选项(1),选项(2)和选项(3)。
对于所有密码解决方案(不知道它是否比Chris的答案更好,您必须进行测试和比较),您可以收集每个用户的option.Key
并过滤掉列表中每个值没有option.Key
的用户
MATCH (u:User)-[:CHOSE]->(opt:Option)
WITH u, collect(opt.Key) as optKeys
WHERE ALL (v IN {values} WHERE v IN optKeys)
RETURN u
或者匹配其键在列表中的所有选项以及选择它们的用户,按用户收集这些选项,并将选项集合的大小与列表的大小进行比较(如果未在列表中提供重复项,则具有相同大小的选项集合的用户选择了所有选项)
MATCH (u:User)-[:CHOSE]->(opt:Option)
WHERE opt.Key IN {values}
WITH u, collect(opt) as opts
WHERE length(opts) = length({values}) // assuming {values} don't have duplicates
RETURN u
两者都应将结果限制为连接到在 {values} 中指定键值的所有选项的用户,并且您可以在不更改查询的情况下更改集合参数的长度。
如果选项数量有限,您可以执行以下操作:
MATCH
(user:User)-[:Chose]->(option1:Option),
(user)-[:Chose]->(option2:Option),
(user)-[:Chose]->(option3:Option)
WHERE
option1.Key = '1'
AND option2.Key = '2'
AND option3.Key = '3'
RETURN
user.Id
这只会返回具有所有 3 个选项的用户。
这有点垃圾,因为显然你最终有 3 行,你有 1 行,但我不知道如何使用 IN
关键字做你想做的事情。
如果你针对它进行编码,生成WHERE
和MATCH
子句非常简单,但仍然不理想。 :(
编辑 - 示例
事实证明,这里正在进行一些字符串操作(!),但您始终可以缓存位。重要的是 - 它使用 Params
这将允许 neo4j 缓存查询并为每次调用提供更快的响应。
public static IEnumerable<User> GetUser(IGraphClient gc)
{
var query = GenerateCypher(gc, new[] {"1", "2", "3"});
return query.Return(user => user.As<User>()).Results;
}
public static ICypherFluentQuery GenerateCypher(IGraphClient gc, string[] options)
{
ICypherFluentQuery query = new CypherFluentQuery(gc);
for(int i = 0; i < options.Length; i++)
query = query.Match(string.Format("(user:User)-[:CHOSE]->(option{0}:Option)", i));
for (int i = 0; i < options.Length; i++)
{
string paramName = string.Format("option{0}param", i);
string whereString = string.Format("option{0}.Key = {{{1}}}", i, paramName);
query = i == 0 ? query.Where(whereString) : query.AndWhere(whereString);
query = query.WithParam(paramName, options[i]);
}
return query;
}
MATCH (user:User)-[:CHOSE]->(option:Option)
WHERE option.key IN ['1', '2', '3']
WITH user, COUNT(*) AS num_options_chosen
WHERE num_options_chosen = LENGTH(['1', '2', '3'])
RETURN user.name
这将仅返回与数组中给定键的所有选项有关系的用户。这假设用户和选项之间没有多个 [:CHOSE] 关系。如果用户可能与单个选项具有多个 [:CHOSE] 关系,则必须根据需要添加一些条件。
我使用以下数据集测试了上述查询:
CREATE (User1:User {name:'User 1'}),
(User2:User {name:'User 2'}),
(User3:User {name:'User 3'}),
(Option1:Option {key:'1'}),
(Option2:Option {key:'2'}),
(Option3:Option {key:'3'}),
(Option4:Option {key:'4'}),
(User1)-[:CHOSE]->(Option1),
(User1)-[:CHOSE]->(Option4),
(User2)-[:CHOSE]->(Option2),
(User2)-[:CHOSE]->(Option3),
(User3)-[:CHOSE]->(Option1),
(User3)-[:CHOSE]->(Option2),
(User3)-[:CHOSE]->(Option3),
(User3)-[:CHOSE]->(Option4)
而且我只得到"用户 3"作为输出。
对于较短的列表,可以在 WHERE
子句中使用路径谓词:
MATCH (user:User)
WHERE (user)-[:CHOSE]->(:Option { Key: '1' })
AND (user)-[:CHOSE]->(:Option { Key: '2' })
AND (user)-[:CHOSE]->(:Option { Key: '3' })
RETURN user
优势:
- 清晰易读
- 易于生成动态长度列表
弊:
- 对于每个不同的长度,您将有一个不同的查询,必须由Cypher解析和缓存。太多的动态查询将看到缓存命中率下降,查询编译工作上升,查询性能下降。