我正在使用嵌入在java应用程序中的Neo4j社区版进行推荐。我做了一个自定义函数,其中包含比较两个实体的复杂逻辑,即产品和用户。这两个实体都作为图中的节点存在,并且每个实体都有20多个属性用于比较目的。例如。我以下列格式调用此函数:
match (e:User {user_id:"some-id"}) with e
match (f:Product {product_id:"some-id"}) with e,f
return e,f,findComparisonValue(e,f) as pref_value;
这个函数调用平均需要4-5毫秒才能运行。现在,为了向特定用户推荐最佳产品,我编写了一个cypher查询,它遍历所有产品,计算pref_value并对它们进行排名。我的cypher查询如下所示:
MATCH (source:User) WHERE id(source)={id} with source
MATCH (reco:Product) WHERE reco.is_active='t'
with reco, source, findComparisonValue(source, reco) as score_result
RETURN distinct reco, score_result.score as score, score_result.params as params, score_result.matched_keywords as matched_keywords
order by score desc
关于图结构的一些见解:
Total Number of nodes: 2 million
Total Number of relationships: 20 million
Total Number of Users: 0.2 million
Total Number of Products: 1.8 million
上面的cypher查询需要超过10秒的时间,因为它正在遍历所有产品。在这个cypher查询之上,我正在使用石墨器-reco模块来满足我的推荐需求(使用预计算、过滤、后处理等)。我想过并行化这个,但是社区版不支持集群。现在,随着系统中用户数量的日益增加,我需要考虑一个可扩展的解决方案。
有人能在这里帮助我,关于如何优化查询。
正如其他人评论的那样,在单个查询中进行数百万次的重大计算将会很慢,并且不会利用neo4j的优势。您应该研究修改您的数据模型和计算,以便您可以利用关系和/或索引。
与此同时,您的第二个问题有许多建议:
>
确保您已经为创建了一个索引:产品(is_active)
,这样就没有必要扫描所有产品了。(顺便说一句,如果该属性实际上应该是布尔值,那么考虑将其设为布尔值而不是字符串。)
CHOURN
子句不需要DISTINCT
运算符,因为所有结果行无论如何都应该是不同的。这是因为每个reco
值都已经是不同的。删除该关键字应该可以提高性能。