neo4j CypherDSL、ReactiveCypherdslStatementExecutor.findOne()无法正常工作

a64a0gku  于 2023-03-29  发布在  React
关注(0)|答案(1)|浏览(170)

我使用CypherDSL从Neo4j检索特定节点;但返回的实体缺少其@Relationship属性。该实体类似于:

@Node
    @Builder
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @EqualsAndHashCode(exclude = {"children", "visibleEntities"})
    @ToString(exclude = {"children", "visibleEntities"})
    public class VisibilityGroup {
    
        public static final String CAN_SEE_REL_TYPE ="CAN_SEE";
        public static final String CONTAINS_REL_TYPE ="CONTAINS";
    
        public static final String ENTITY_ID_PROPERTY= "entityId";
    
    
        @DynamicLabels
        @Singular(ignoreNullCollections = true)
        private List<String> labels;
    
        @Property("name")
        private String nodeName;
    
        @Property(ENTITY_ID_PROPERTY)
        private UUID entityId;
    
        @Id
        @GeneratedValue(generatorClass = UUIDStringGenerator.class)
        private String id;
    
        @Relationship(type = CONTAINS_REL_TYPE, direction = Relationship.Direction.OUTGOING)
        @Builder.Default
        private Set<VisibilityGroupRelationship> children = new HashSet<>();
    
        @Relationship(type = CAN_SEE_REL_TYPE, direction = Relationship.Direction.OUTGOING)
        @Builder.Default
        private Set<VisibilityGroupRelationship> visibleEntities = new HashSet<>();
    
        ........
    }

现在:如果我使用ReactiveCrudRepository.findById(ID id),则对象具有正确初始化的子对象和visibleEntities,但如果我使用CypherDSL获取它,则它们都为null。
下面是我的repo代码:

default Mono<VisibilityGroup> findEntity(UUID originEntityId, String originType,
                                             UUID targetEntityId, String targetType,
                                             boolean useContains){
 
  
            Node targetNode = Cypher.node(targetType)
                .named("target")
                .withProperties(VisibilityGroup.ENTITY_ID_PROPERTY,
                    Cypher.literalOf(targetEntityId.toString()));
            Node originNode = Cypher.node(originType)
                .named("origin")
                .withProperties(VisibilityGroup.ENTITY_ID_PROPERTY,
                    Cypher.literalOf(originEntityId.toString()));
            Relationship relationship = useContains ?
                originNode.relationshipTo(targetNode, VisibilityGroup.CAN_SEE_REL_TYPE,
                    VisibilityGroup.CONTAINS_REL_TYPE).unbounded() :
                originNode.relationshipTo(targetNode, VisibilityGroup.CAN_SEE_REL_TYPE).unbounded();
            SymbolicName permissionVariable = Cypher.name("p");
            ResultStatement statement = Cypher.match(relationship)
                .where(....)
                .returningDistinct(targetNode).build();
            return findOne(statement);
        }

我知道,我可以很容易地修复它,将最后一行替换为:

return findOne(statement).flatMap(visibilityGroup -> findById(visibilityGroup.getId()));

但我想知道这是否正常,或者我是否滥用了CypherDSL。提前谢谢你。
更新
我添加了模型和预期结果的文本描述。所有节点都用“CAN_SEE”或“CONTAINS”关系绑定在一起,定义了一个可见性图。只有管理员可以通过CONTAINS关系“看到”;所有用户都可以通过“CAN_SEE”关系进行查看。所提出的存储库方法发现源节点(通常是用户)CAN_SEE*(或CAN_SEE|CONTAINS*)目标节点,这很好用。返回的节点是请求的目标节点,如果可见的话。我需要的是返回的目标节点包含ITS邻居作为属性:
1.在visibleEntities属性中,哪些节点直接(1跳)绑定到具有CAN_SEE关系的目标节点;
1.在children属性中,哪些节点通过CONTAINS关系直接(1跳)绑定到他。

mec1mxoz

mec1mxoz1#

请在退货条款中使用以下内容:

ResultStatement statement = Cypher.match(relationship)
    //  .where(....)
    .returningDistinct(originNode.getRequiredSymbolicName(), Functions.collect(relationship).as("r"), Functions.collect(targetNode).as("t"))
        .build();

基本上,当使用自定义查询时,你必须做的是将返回的子图整形为每个实体一条记录(这是SDN通常对生成的查询所做的),这里描述:每个根节点获取一条记录,作为Cypher-DSL示例,在使用复杂、动态的自定义查询但仍返回域类型下
我是Cypher-DSL的作者和SDN的共同维护者,所以感谢您使用两者:)

  • 评论后更新 *

请注意,我在与您的模型相同的方向上查询关系,从目标节点的PoV(它们从其视图传出)

Node targetNode = Cypher.node(targetType)
    .named("target")
    .withProperties(VisibilityGroup.ENTITY_ID_PROPERTY,
            Cypher.literalOf(targetEntityId.toString()));
Node originNode = Cypher.node(originType)
    .named("origin")
    .withProperties(VisibilityGroup.ENTITY_ID_PROPERTY,
            Cypher.literalOf(originEntityId.toString()));
Relationship relationship = useContains ?
    targetNode.relationshipTo(originNode, VisibilityGroup.CAN_SEE_REL_TYPE, VisibilityGroup.CONTAINS_REL_TYPE).unbounded() :
    targetNode.relationshipTo(originNode, VisibilityGroup.CAN_SEE_REL_TYPE).unbounded();

SymbolicName permissionVariable = Cypher.name("p");

// Needed individually for collection

Condition condition = Conditions.noCondition();
// Build your where clause…
condition = condition.and(permissionVariable.isTrue());

ResultStatement statement;
if(useContains) {
    NamedPath p = Cypher.path("path").definedBy(relationship);
    statement = Cypher.match(targetNode, p)
            .where(condition)
            .returning(targetNode.getRequiredSymbolicName(), Functions.relationships(p).as("r"), Functions.nodes(p).as("o"))
            .build();
} else {
    Relationship canSee = targetNode.relationshipTo(originNode, VisibilityGroup.CAN_SEE_REL_TYPE).named("canSee");
    Relationship contains = targetNode.relationshipTo(originNode, VisibilityGroup.CONTAINS_REL_TYPE).named("contains");
    statement = Cypher.match(relationship)
            .where(condition)
            .with(originNode, targetNode)
            .match(canSee, contains)
            .returningDistinct(targetNode.getRequiredSymbolicName(), Functions.collect(canSee).add(Functions.collect(contains)).as("r"), Functions.collect(originNode).as("o"))
            .build();
}

这个条件当然会导致无效的密码,因为p是未定义的,但我想这个想法是明确的。
当使用CONTAINS关系时,这将生成如下内容

MATCH (target:target {entityId: 'dcfb30c2-8a09-41d2-ba35-d1199f25ed69'}), path = (target)-[:CAN_SEE|CONTAINS*]->(origin:origin {entityId: 'e8a41c34-a2b0-479b-8b34-40bc1b6d0656'})
WHERE p = true
RETURN target, relationships(path) AS r, nodes(path) AS o

你可以利用你已经匹配了所有类型的事实
或者不用的时候,像这样

MATCH (target:target {entityId: '7cb5cd5e-c8b1-48f7-8e5f-aad70ab19d15'})-[:CAN_SEE*]->(origin:origin {entityId: '66530792-5704-4732-98be-aea6b64b5fb6'})
WHERE p = true
WITH origin, target
MATCH (target)-[canSee:CAN_SEE]->(origin), (target)-[contains:CONTAINS]->(origin)
RETURN DISTINCT target, (collect(canSee) + collect(contains)) AS r, collect(origin) AS o

请测试优化,我不确定是否总是包含所有的关系时,使用|或运算符从我的头顶部。

相关问题