【问题标题】:Optimize Cypher Query for Movie Recommendation on Large Dataset优化大型数据集上电影推荐的 Cypher 查询
【发布时间】:2018-10-30 13:32:02
【问题描述】:

在阅读https://markorodriguez.com/2011/09/22/a-graph-based-movie-recommender-engine/ 之后,我目前正在使用 MovieLens 20m 数据集进行电影推荐。 Node Movie 通过关系 hasGenre 连接到 Genre,Node Movie 通过关系 hasRating 连接到 User。我正在尝试使用与玩具总动员共享所有类型的查询(例如玩具总动员)检索所有评分最高(共同评分 > 3.0)的电影。这是我的 Cypher 查询:

MATCH (inputMovie:Movie {movieId: 1})-[r:hasGenre]-(h:Genre)
WITH inputMovie, COLLECT (h) as inputGenres
MATCH (inputMovie)<-[r:hasRating]-(User)-[o:hasRating]->(movie)-[:hasGenre]->(genre) 
WITH  inputGenres,  r, o, movie, COLLECT(genre) AS genres 
WHERE ALL(h in inputGenres where h in genres) and (r.rating>3 and o.rating>3)  
RETURN movie.title,movie.movieId, count(*) 
ORDER BY count(*) DESC

但是,我的系统似乎无法处理它(使用 16GB RAM、Core i7 第 4 代和 SSD)。当我运行查询时,它会达到 97% 的 RAM 然后 Neo4j 意外关闭(可能是由于堆大小或 RAM 大小)。

  1. 我的查询是否正确?我是 Neo4j 的新手,所以我的查询可能不正确。
  2. 请建议如何优化此类查询?
  3. 如何优化 Neo4j,使其能够根据查询处理符合我系统规范的大型数据集?

提前致谢。

【问题讨论】:

    标签: performance neo4j cypher


    【解决方案1】:

    首先,您可以通过仅匹配我们需要的内容并在 WHERE 中处理其余部分来简化您的 Cypher 以进行更有效的规划(以便可以在匹配时进行过滤)

    MATCH (inputMovie:Movie {movieId: 1})-[r:hasGenre]->(h:Genre)
    WITH inputMovie, COLLECT (h) as inputGenres
    MATCH (inputMovie)<-[r:hasRating]-(User)-[o:hasRating]->(movie)
    WHERE (r.rating>3 and o.rating>3) AND ALL(genre in inputGenres WHERE (movie)-[:hasGenre]->(genre))
    RETURN movie.title,movie.movieId, count(*) 
    ORDER BY count(*) DESC
    

    现在,如果您不介意将数据添加到图表中以查找所需数据,您可以做的另一件事是将查询拆分为微小的位并“缓存”结果。比如

    // Cypher 1
    MATCH (inputMovie:Movie {movieId: 1})-[r:hasGenre]->(h:Genre)
    WITH inputMovie, COLLECT (h) as inputGenres
    MATCH (movie:Movie)
    WHERE ALL(genre in inputGenres WHERE (movie)-[:hasGenre]->(genre))
    // Merge so that multiple runs don't create extra copies
    MERGE (inputMovie)-[:isLike]->(movie)
    
    // Cypher 2
    MATCH (movie:Movie)<-[r:hasRating]-(user)
    WHERE r.rating>3
    // Merge so that multiple runs don't create extra copies
    MERGE (user)-[:reallyLikes]->(movie)
    
    // Cypher 3
    MATCH (inputMovie:Movie{movieId: 1})<-[:reallyLikes]-(user)-[:reallyLikes]->(movie:Movie)<-[:isLike]-(inputMovie)
    RETURN movie.title,movie.movieId, count(*) 
    ORDER BY count(*) DESC
    

    【讨论】:

    • 感谢@Tezra 的回复。你的第一个 Cypher 能够将时间减少到比我的 50% 并得到准确的结果。但是,使用您的第二个建议,我根本没有得到任何结果。 Cypher 1 创建关系。 Cypher 2,创建节点和关系。但是,Cypher 3 显示“(无更改,无记录)”。 Cypher 1+2+3 总时间比第一个 Cypher 慢 48%,可能是因为它需要物理创建节点和所有节点。如果可以的话,下一次运行可能会非常快。
    • @Adikara 使用 CREATE 而不是 MERGE 会更快(因为它不必先验证一个不存在),但多次运行会创建重复的链接。创建缓存链接/节点的想法是,它可以让您更便宜地执行这些类型的查询,但需要更多时间来设置。当您的原始查询因内存不足或某些部分花费太长时间而失败时,这很有用。 Cypher 2 不应该创建任何节点。 (变量有大小写错误,已修复)
    • 我明白了,避免内存不足是有道理的。但是在运行更新 Cypher 2 之后,我得到了“Neo.TransientError.General.OutOfMemoryError:没有足够的内存来执行当前任务。”,即使在尝试增加堆大小和页面缓存大小之后也是如此。经过几次更改堆大小的试验后,它仍然存在内存不足问题。
    猜你喜欢
    • 1970-01-01
    • 2018-03-07
    • 2017-09-18
    • 2017-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-31
    相关资源
    最近更新 更多