2017-05-26 220 views
2

在我的Neo4j/SDN 4應用程序中,我所有的Cypher查詢都基於內部Neo4j ID。Neo4j SDN 4 GraphId性能與指數

這是一個問題,因爲我無法在我的Web應用程序URL上依賴這些ID。 Neo4j可以重用這些ID,所以很有可能在未來的某個時間在相同的ID下我們可以找到絕對的另一個節點。

我試圖根據以下解決方案重新實現此邏輯:Using the graph to control unique id generation但注意到查詢性能下降。

從理論的角度來看,應在Cypher支架查詢基於與@Index(unique = true, primary = true屬性)

例如:

@Index(unique = true, primary = true) 
private Long uid; 

entity.uid = {someId} 

具有同樣性能的Cypher支架查詢其基於內部工作Neo4j的ID:

id(entity) = {someId} 

修訂

這是:schema輸出:

Indexes 
    ON :BaseEntity(uid) ONLINE 
    ON :Characteristic(lowerName) ONLINE 
    ON :CharacteristicGroup(lowerName) ONLINE 
    ON :Criterion(lowerName) ONLINE 
    ON :CriterionGroup(lowerName) ONLINE 
    ON :Decision(lowerName) ONLINE 
    ON :FlagType(name) ONLINE (for uniqueness constraint) 
    ON :HAS_VALUE_ON(value) ONLINE 
    ON :HistoryValue(originalValue) ONLINE 
    ON :Permission(code) ONLINE (for uniqueness constraint) 
    ON :Role(name) ONLINE (for uniqueness constraint) 
    ON :User(email) ONLINE (for uniqueness constraint) 
    ON :User(username) ONLINE (for uniqueness constraint) 
    ON :Value(value) ONLINE 

Constraints 
    ON (flagtype:FlagType) ASSERT flagtype.name IS UNIQUE 
    ON (permission:Permission) ASSERT permission.code IS UNIQUE 
    ON (role:Role) ASSERT role.name IS UNIQUE 
    ON (user:User) ASSERT user.email IS UNIQUE 
    ON (user:User) ASSERT user.username IS UNIQUE 

正如你可以看到我有:BaseEntity(uid)

BaseEntity的指數是在我的實體層次結構中的基類,例如:

@NodeEntity 
public abstract class BaseEntity { 

    @GraphId 
    private Long id; 

    @Index(unique = false) 
    private Long uid; 

    private Date createDate; 

    private Date updateDate; 

... 

} 

@NodeEntity 
public class Commentable extends BaseEntity { 
... 
} 

@NodeEntity 
public class Decision extends Commentable { 

    private String name; 

} 

威爾當我在查找(d:Decision) WHERE d.uid = {uid}的示例時,將使用uid索引?

PROFILE resuls - 內部ID VS索引屬性

查詢基於內部ID

PROFILE MATCH (parentD)-[:CONTAINS]->(childD:Decision) 
WHERE id(parentD) = 1474333 
MATCH (childD)-[relationshipValueRel1475199:HAS_VALUE_ON]-(filterCharacteristic1475199) 
WHERE id(filterCharacteristic1475199) = 1475199 
WITH relationshipValueRel1475199, childD 
WHERE ([1, 19][0] <= relationshipValueRel1475199.value <= [1, 19][1]) 
WITH childD 
MATCH (childD)-[relationshipValueRel1474358:HAS_VALUE_ON]-(filterCharacteristic1474358) 
WHERE id(filterCharacteristic1474358) = 1474358 
WITH relationshipValueRel1474358, childD 
WHERE (ANY (id IN ['Compact'] WHERE id IN relationshipValueRel1474358.value)) 
WITH childD 
MATCH (childD)-[relationshipValueRel1475193:HAS_VALUE_ON]-(filterCharacteristic1475193) 
WHERE id(filterCharacteristic1475193) = 1475193 
WITH relationshipValueRel1475193, childD 
WHERE (ANY (id IN ['16:9', '3:2', '4:3', '1:1'] 
WHERE id IN relationshipValueRel1475193.value)) 
WITH childD 
OPTIONAL MATCH (childD)-[vg:HAS_VOTE_ON]->(c) 
WHERE id(c) IN [1474342, 1474343, 1474340, 1474339, 1474336, 1474352, 1474353, 1474350, 1474351, 1474348, 1474346, 1474344] 
WITH childD, vg.avgVotesWeight as weight, vg.totalVotes as totalVotes 
WITH * MATCH (childD)-[ru:CREATED_BY]->(u:User) 
WITH ru, u, childD , toFloat(sum(weight)) as weight, toInt(sum(totalVotes)) as totalVotes 
ORDER BY weight DESC 
SKIP 0 LIMIT 10 
RETURN ru, u, childD AS decision, weight, totalVotes, 
[ (parentD)<-[:DEFINED_BY]-(entity)<-[:COMMENTED_ON]-(comg:CommentGroup)-[:COMMENTED_FOR]->(childD) | {entityId: id(entity), types: labels(entity), totalComments: toInt(comg.totalComments)} ] AS commentGroups, 
[ (parentD)<-[:DEFINED_BY]-(c1)<-[vg1:HAS_VOTE_ON]-(childD) | {criterionId: id(c1), weight: vg1.avgVotesWeight, totalVotes: toInt(vg1.totalVotes)} ] AS weightedCriteria, 
[ (parentD)<-[:DEFINED_BY]-(ch1:Characteristic)<-[v1:HAS_VALUE_ON]-(childD) WHERE NOT ((ch1)<-[:DEPENDS_ON]-()) | {characteristicId: id(ch1), value: v1.value, totalHistoryValues: toInt(v1.totalHistoryValues), description: v1.description, valueType: ch1.valueType, visualMode: ch1.visualMode} ] AS valuedCharacteristics 

PROFILE輸出:

Cypher支架版本:CYPHER 3.1,計劃者:成本,運行時:INTERPRETED 。 350554總分貝命中238毫秒。 CYPHER 3.1,規劃師:成本,運行時間:解釋爲基於索引的屬性UID

PROFILE MATCH (parentD)-[:CONTAINS]->(childD:Decision) 
WHERE parentD.uid = 61 
MATCH (childD)-[relationshipValueRel1475199:HAS_VALUE_ON]-(filterCharacteristic1475199) 
WHERE filterCharacteristic1475199.uid = 15 
WITH relationshipValueRel1475199, childD 
WHERE ([1, 19][0] <= relationshipValueRel1475199.value <= [1, 19][1]) 
WITH childD 
MATCH (childD)-[relationshipValueRel1474358:HAS_VALUE_ON]-(filterCharacteristic1474358) 
WHERE filterCharacteristic1474358.uid = 10 
WITH relationshipValueRel1474358, childD 
WHERE (ANY (id IN ['Compact'] WHERE id IN relationshipValueRel1474358.value)) 
WITH childD 
MATCH (childD)-[relationshipValueRel1475193:HAS_VALUE_ON]-(filterCharacteristic1475193) 
WHERE filterCharacteristic1475193.uid = 14 
WITH relationshipValueRel1475193, childD 
WHERE (ANY (id IN ['16:9', '3:2', '4:3', '1:1'] 
WHERE id IN relationshipValueRel1475193.value)) 
WITH childD 
OPTIONAL MATCH (childD)-[vg:HAS_VOTE_ON]->(c) 
WHERE c.uid IN [26, 27, 24, 23, 20, 36, 37, 34, 35, 32, 30, 28] 
WITH childD, vg.avgVotesWeight as weight, vg.totalVotes as totalVotes 
WITH * MATCH (childD)-[ru:CREATED_BY]->(u:User) 
WITH ru, u, childD , toFloat(sum(weight)) as weight, toInt(sum(totalVotes)) as totalVotes 
ORDER BY weight DESC 
SKIP 0 LIMIT 10 
RETURN ru, u, childD AS decision, weight, totalVotes, 
[ (parentD)<-[:DEFINED_BY]-(entity)<-[:COMMENTED_ON]-(comg:CommentGroup)-[:COMMENTED_FOR]->(childD) | {entityId: id(entity), types: labels(entity), totalComments: toInt(comg.totalComments)} ] AS commentGroups, 
[ (parentD)<-[:DEFINED_BY]-(c1)<-[vg1:HAS_VOTE_ON]-(childD) | {criterionId: id(c1), weight: vg1.avgVotesWeight, totalVotes: toInt(vg1.totalVotes)} ] AS weightedCriteria, 
[ (parentD)<-[:DEFINED_BY]-(ch1:Characteristic)<-[v1:HAS_VALUE_ON]-(childD) WHERE NOT ((ch1)<-[:DEPENDS_ON]-()) | {characteristicId: id(ch1), value: v1.value, totalHistoryValues: toInt(v1.totalHistoryValues), description: v1.description, valueType: ch1.valueType, visualMode: ch1.visualMode} ] AS valuedCharacteristics 

暗號版本

enter image description here

查詢。 671326總分貝命中426毫秒。

enter image description here

是否有任何改善基於UID的表現機會呢?

回答

5

你是對的,不要在網址上使用Neo4j內部id,因爲節點被刪除後可以重複使用它們。

從性能的角度來看,內部id的速度可以達到 - 它實際上是一個帶有節點/關係記錄的文件中的偏移量(您可能已經注意到這些是2個獨立的id序列,您可以使用id = z和相同的id = x的關係)。

任何索引的使用都要慢一些,因爲數據庫首先索引查找,獲取內部id,然後讀取節點記錄。

但是,對於絕大多數應用性能差異可以忽略 - 可能遠遠小於網絡延遲或一般OGM開銷。

如果你看到

  • 明顯的差異驗證指標存在於數據庫中(例如,在Neo4j的瀏覽器:schema
  • 打開日誌記錄和驗證查詢標籤正確(設置org.neo4j.ogminfo水平)
  • 如果索引存在,並且查詢包含正確的標籤,然後使用PROFILE檢查查詢計劃

修訂

是,指數將被用於類似查詢:應該由

session.load(Decision.class, uid) 

獲取生成,如果你的指數是主要或findByUidDecisionRepository

MATCH (d:Decision) WHERE d.uid = {uid} ... 

要注意的是,當where子句出現在查詢中間的指標可以不使用:

... 
WITH x 
MATCH (x)-[...]-(d) WHERE d.uid = {uid} ... 

這取決於查詢計劃,你應該使用PROFILE進行調查。

+0

感謝您的回答。現在,我試圖採用一種方法來重構我的系統,以避免ID重用的問題,並且我看到了以下模式 - 在我的網站上我將使用代理uid。如果id不需要放在web url中,我會使用內部的Neo4j id.So這個代理uuid將只用在web urls中,否則在客戶端的所有其他地方我都會使用內部的Neo4j ID。是否有意義 ? – alexanoid

+0

有兩種方式通過ID訪問實體可能會使事情不必要地複雜化。我只會去用自定義的uuid。正如我所說索引是*快*,內部id和索引查找之間的差異將小於網絡延遲或一般OGM開銷的數量級。 –

+0

我在單個查詢中使用了不同的ID(https://stackoverflow.com/questions/43824894/neo4j-cypher-query-structure-and-performance-optimization),所以使用基於一個純粹的UIDs被人眼所注意到。 – alexanoid