Dans mon application Neo4j/SDN 4, toutes mes requêtes Cypher sont basées sur des ID Neo4j internes.Neo4j SDN 4 Performance GraphId vs Index
Ceci est un problème car je ne peux pas compter sur ces ID dans mes URL d'application Web. Neo4j peut réutiliser ces identifiants, donc il y a de fortes chances pour qu'à l'avenir, sous le même ID, nous trouvions un autre nœud.
J'ai essayé de ré-implémenter cette logique basée sur la solution suivante: Using the graph to control unique id generation mais j'ai noté une dégradation des performances de requête.
D'un point de vue théorique, si une requête Cypher basée sur la propriété avec @Index(unique = true, primary = true
)
par exemple:
@Index(unique = true, primary = true)
private Long uid;
entity.uid = {someId}
travail avec les mêmes performances que une requête Cypher qui est basée sur interne Neo4j ID:
id(entity) = {someId}
MISE À JOUR
C'est :schema
sortie:
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
Comme vous pouvez le voir, j'ai un index sur :BaseEntity(uid)
BaseEntity
est une classe de base dans ma hiérarchie de l'entité, par exemple:
@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;
}
Will cet index uid
être utilisé quand je cherche par exemple pour (d:Decision) WHERE d.uid = {uid}
?
PROFIL resuls - ID interne vs propriété indexée
requête basée sur ID interne
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
sortie PROFIL:
Version Cypher: CYPHER 3.1, planificateur: COST, exécution: INTERPRETE . 350554 coups totaux db en 238 ms.
requête basée sur la propriété indexée 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
version Cypher: CYPHER 3.1, planificateur: COST, exécution: INTERPRETE. 671326 total des hits db en 426 ms.
Est-il possible d'améliorer la performance basée sur uid?
Merci pour votre réponse. En ce moment j'essaye de venir avec une approche comment refactoriser mon système afin d'éviter le problème avec la réutilisation d'identification et je vois le schéma suivant - dans mes URL de Web j'emploierai l'uid de substitution. Si id n'est pas nécessaire pour être placé dans l'url web, j'utiliserai l'identifiant interne Neo4j. Ainsi, l'uuid de substitution ne sera utilisé que dans les urls web sinon dans tous les autres endroits sur le client je vais utiliser l'identifiant interne Neo4j. Est-ce que ça fait du sens ? – alexanoid
Avoir 2 façons d'accéder aux entités par ID peut compliquer inutilement les choses. J'irais avec uuid personnalisé seulement. Comme je l'ai dit, l'index est * rapide *, la différence entre la recherche d'index interne et d'index sera inférieure d'un ordre de grandeur à la latence du réseau ou à l'overhead général OGM. –
J'ai une utilisation très intensive de différents identifiants dans une seule requête (https://stackoverflow.com/questions/43824894/neo4j-cypher-query-structure-and-performance-optimization) donc la dégradation des performances avec une approche basée sur un UID pur a été perceptible par l'oeil humain. – alexanoid