2016-11-05 63 views
0

這是基於從Neo4j的文檔樣本的Cypher:如何加速Neo4j Cypher中的協作過濾?

MATCH (user:User)-[:ORDERS]->(:Product)<-[:ORDERS]-(otherUser:User)-[:ORDERS]->(recommended:Product) 
WHERE NOT (user)-[:ORDERS]->(recommended) 
    AND user.id = 171 
RETURN distinct recommended.id, count(distinct otherUser.id) as frequency 
ORDER BY frequency DESC 
LIMIT 200 

而下面是我所作的改進:

MATCH (user:User)-[:ORDERS]->(p:Product) 
WHERE user.id = 171 
WITH DISTINCT p, user 
MATCH (p)<-[:ORDERS]-(otherUser:User) 
WITH DISTINCT otherUser, user 
MATCH (otherUser)-[:ORDERS]->(recommended:Product) 
WHERE NOT (user)-[:ORDERS]->(recommended) 
RETURN distinct recommended.id, count(distinct otherUser.id) as frequency 
ORDER BY frequency DESC 
LIMIT 200 

兩者返回相同的結果,但第二個運行快6倍。 (但仍然3我的Macbook花了幾秒鐘)

  1. 爲什麼第二個運行更快?
  2. 如何進一步提高速度?

回答

2

您的查詢獲得p產品(您不想推薦),但最終會丟棄它們。這些p節點可用於與recommended節點進行比較,避免處理WHERE NOT (user)-[:ORDERS]->(recommended)(每次必須重新掃描每個訂單的user)所需的額外數據庫命中數。這應該會顯着加快您的查詢速度。

試試這個:

MATCH (user:User)-[:ORDERS]->(p:Product)<-[:ORDERS]-(otherUser:User) 
WHERE user.id = 171 
WITH COLLECT(DISTINCT otherUser) AS others, COLLECT(DISTINCT p) AS sharedProds 
UNWIND others AS other 
MATCH (other)-[:ORDERS]->(recommended:Product) 
WHERE NOT recommended IN sharedProds 
RETURN DISTINCT recommended.id, count(DISTINCT other) as frequency 
ORDER BY frequency DESC 
LIMIT 200; 

另外,我認爲User節點具有獨特id值,所以我用count(DISTINCT otherUser)代替count(DISTINCT otherUser.id),這應該會更快。

+0

太好了,它跑得快得多!一點校正:'返回DISTINCT recommended.id,計數(DISTINCT其他)作爲頻率' –

+0

我想知道爲什麼你需要在那裏使用'UNWIND'?與以下有什麼不同? 'MATCH(user:User) - [:ORDERS] - >(p:Product)< - [:ORDERS] - (otherUser:User) WHERE user.id = 171 WITH distinct otherUser,COLLECT(DISTINCT p) AS sharedProds MATCH(otherUser) - [:訂單] - >(推薦:產品) WHERE不推薦sharedProds 具有鮮明的推薦,COUNT(DISTINCT otherUser)AS頻率 RETURN DISTINCT recommended.id,頻率 ORDER BY頻率DESC LIMIT 200' –

+0

如果你想以一個別名得到整個獨特的'products'集合,你必須也聚合'other'。爲了每行保留一個'otherUser',你只需執行'COLLECT'和'UNWIND',然後每行都有一個'otherUser'和'Products'的完整列表。但是,對於您的查詢,我相信這是不必要的,因此您的編輯也應該可以正常工作(但當然也可以查看其中哪一個更好) –

1

第一個查詢可能比需要的慢,因爲它適用於DISTINCT性質,而不是節點,這迫使該數據庫有可能下探之前來查找每一個節點的屬性。如果id屬性對於單個節點User是唯一的(對於Product節點也是如此),則效率會降低。這將是相同的查詢的速度更快的版本,如果你可以假設沒有User股的id與另一User,並沒有Product股的id另一個Product

MATCH (user:User)-[:ORDERS]->(:Product)<-[:ORDERS]-(otherUser:User)-[:ORDERS]->(recommended:Product) 
WHERE NOT (user)-[:ORDERS]->(recommended) 
    AND user.id = 171 
WITH recommended, COUNT(DISTINCT otherUser) AS frequency 
RETURN recommended.id, frequency 
ORDER BY frequency DESC 
LIMIT 200 

您的查詢在適用DISTINCT到節點每個步驟,所以它避免了這個屬性查找問題(這通常是查詢中最昂貴的部分,尤其是大型的)。但是,手動切斷這樣一條長路徑可能會減慢查詢速度,因爲規劃人員可能無法看到它可能對整個路徑執行的優化。幾乎在每個實例中,單個長路徑比多個短路徑更可取。關於如何走得更快,請記住,圖表可能是資源密集型的,所以如果您在筆記本電腦或小型雲實例上並行運行它,也許可以查看performance tuning以查看是否你可能會窒息你的數據庫。

編輯:如果你有一個密集的連接圖(大量:User s之間共享:Product節點),那麼你可能會產生第一個查詢不必要的結果行。如果PROFILE顯示在您的查詢的中間大量結果行的,試試這個替代方案來代替:

MATCH (user:User {id:171})-[:ORDERS]->(:Product)<-[:ORDERS]-(otherUser:User) 
WITH DISTINCT user, otherUser 
MATCH (otherUser)-[:ORDERS]->(recommended:Product) 
WHERE NOT (user)-[:ORDERS]->(recommended) 
WITH recommended, COUNT(DISTINCT otherUser) AS frequency 
RETURN recommended.id, frequency 
ORDER BY frequency DESC 
LIMIT 200 

這確保每個otherUser節點僅建議檢查一次,而第一次查詢將處理每個otherUser:Product一次,他們與用戶分享。這是關於你的圖表的一種信息,PROFILE可以幫助你出現;找到一個結果行數爆炸的步驟,看看是否有辦法將其分解以減少總行數。

+0

太棒了!它比第一個快2到3倍!但仍是第二個的兩倍。 –

+0

我不得不在你的設置中看到一個查詢的「PROFILE」,以便進一步說明,在這種情況下可能會有實際的影響。 –