Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple request for projection #2437

Closed
Andy2003 opened this issue Nov 9, 2021 · 3 comments
Closed

Multiple request for projection #2437

Andy2003 opened this issue Nov 9, 2021 · 3 comments
Assignees
Labels
type: enhancement A general enhancement

Comments

@Andy2003
Copy link
Contributor

Andy2003 commented Nov 9, 2021

Given

@Data
@Setter(AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@SuperBuilder(toBuilder = true)
abstract public class BaseNodeEntity {

	@Id
	@GeneratedValue(UUIDStringGenerator.class)
	@EqualsAndHashCode.Include
	private String nodeId;

	@Relationship(type = "CHILD_OF", direction = OUTGOING)
	private NodeEntity parent;

	@Relationship(type = "HAS_TYPE", direction = OUTGOING)
	private NodeType nodeType;
}
@Node
@Data
@Setter(AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(callSuper = true, onlyExplicitlyIncluded = true)
@SuperBuilder(toBuilder = true)
public class MeasurementMeta extends BaseNodeEntity {

	@Relationship(type = "WEIGHTS", direction = OUTGOING)
	private MeasurementMeta baseMeasurement;
}
@Node
@Data
@Setter(AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(callSuper = true, onlyExplicitlyIncluded = true)
@SuperBuilder(toBuilder = true)
public class NodeEntity extends BaseNodeEntity {
}
@Node
@Value
@AllArgsConstructor
@EqualsAndHashCode
@Immutable
public class NodeType {
	@Id
	String nodeTypeId;
}
public interface BaseNodeFieldsProjection {
	String getNodeId();
}
public interface ApiMeasurementMetaProjection extends BaseNodeFieldsProjection {
	BaseNodeFieldsProjection getParent();
	NodeType getNodeType();
	BaseNodeFieldsProjection getBaseMeasurement();
}
interface MeasurementMetaRepository extends Repository<MeasurementMeta, String> {
	<R> Optional<R> findByNodeId(String nodeId, Class<R> clazz);
}
CREATE (m1:MeasurementMeta{nodeId: 'm1'}) 
CREATE (m2:MeasurementMeta{nodeId: 'm2'}) 
CREATE (nt2:NodeType{nodeTypeId: 'nt2'}) 
CREATE (m1)-[:HAS_TYPE]->(nt2)
CREATE (m2)-[:HAS_TYPE]->(nt2)
CREATE (m1)-[:WEIGHTS]->(m2)

The following invocation:

repository.findByNodeId("m1", ApiMeasurementMetaProjection.class);

Will run multiple queries against the db:

[main] 2021-11-09 18:20:32,864 DEBUG    org.springframework.data.neo4j.cypher: 313 - Executing:
MATCH (measurementMeta:`MeasurementMeta`) WHERE measurementMeta.nodeId = $nodeId WITH collect(id(measurementMeta)) AS __sn__ RETURN __sn__
[main] 2021-11-09 18:20:32,864 TRACE    org.springframework.data.neo4j.cypher: 334 - with parameters:
:params {nodeId: "m1"}
[main] 2021-11-09 18:20:32,969 DEBUG    org.springframework.data.neo4j.cypher: 313 - Executing:
MATCH (measurementMeta:`MeasurementMeta`) WHERE measurementMeta.nodeId = $nodeId OPTIONAL MATCH (measurementMeta)-[__sr__:`HAS_TYPE`]->(__srn__:`NodeType`) WITH collect(id(measurementMeta)) AS __sn__, collect(id(__srn__)) AS __srn__, collect(id(__sr__)) AS __sr__ RETURN __sn__, __srn__, __sr__
[main] 2021-11-09 18:20:32,969 TRACE    org.springframework.data.neo4j.cypher: 334 - with parameters:
:params {nodeId: "m1"}
[main] 2021-11-09 18:20:33,094 DEBUG    org.springframework.data.neo4j.cypher: 313 - Executing:
MATCH (measurementMeta:`MeasurementMeta`) WHERE measurementMeta.nodeId = $nodeId OPTIONAL MATCH (measurementMeta)-[__sr__:`CHILD_OF`]->(__srn__:`NodeEntity`) WITH collect(id(measurementMeta)) AS __sn__, collect(id(__srn__)) AS __srn__, collect(id(__sr__)) AS __sr__ RETURN __sn__, __srn__, __sr__
[main] 2021-11-09 18:20:33,094 TRACE    org.springframework.data.neo4j.cypher: 334 - with parameters:
:params {nodeId: "m1"}
[main] 2021-11-09 18:20:33,147 DEBUG    org.springframework.data.neo4j.cypher: 313 - Executing:
MATCH (measurementMeta:`MeasurementMeta`) WHERE measurementMeta.nodeId = $nodeId OPTIONAL MATCH (measurementMeta)-[__sr__:`WEIGHTS`]->(__srn__:`MeasurementMeta`) WITH collect(id(measurementMeta)) AS __sn__, collect(id(__srn__)) AS __srn__, collect(id(__sr__)) AS __sr__ RETURN __sn__, __srn__, __sr__
[main] 2021-11-09 18:20:33,147 TRACE    org.springframework.data.neo4j.cypher: 334 - with parameters:
:params {nodeId: "m1"}
[main] 2021-11-09 18:20:33,196 DEBUG    org.springframework.data.neo4j.cypher: 313 - Executing:
MATCH (rootNodeIds) WHERE id(rootNodeIds) IN $rootNodeIds WITH collect(rootNodeIds) AS n OPTIONAL MATCH ()-[relationshipIds]-() WHERE id(relationshipIds) IN $relationshipIds WITH n, collect(DISTINCT relationshipIds) AS __sr__ OPTIONAL MATCH (relatedNodeIds) WHERE id(relatedNodeIds) IN $relatedNodeIds WITH n, __sr__ AS __sr__, collect(DISTINCT relatedNodeIds) AS __srn__ UNWIND n AS rootNodeIds WITH rootNodeIds AS measurementMeta, __sr__, __srn__ RETURN measurementMeta AS __sn__, __sr__, __srn__
[main] 2021-11-09 18:20:33,196 TRACE    org.springframework.data.neo4j.cypher: 334 - with parameters:
:params {rootNodeIds: [22], relatedNodeIds: [23, 24], relationshipIds: [0, 2]}

In my opinion it is not necessary to query all relations separately for the given projection. One query should be sufficient here.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Nov 9, 2021
@Andy2003
Copy link
Contributor Author

Andy2003 commented Nov 9, 2021

As soon as I remove ApiMeasurementMetaProjection::getBaseMeasurement() from the projection, the data is queried by a single call:

MATCH (measurementMeta:`MeasurementMeta`)
  WHERE measurementMeta.nodeId = $nodeId
RETURN measurementMeta{
    .nodeId,
    __nodeLabels__:labels(measurementMeta),
    __internalNeo4jId__:id(measurementMeta),
    MeasurementMeta_CHILD_OF_NodeEntity:[(measurementMeta)-[:`CHILD_OF`]->(measurementMeta_parent:`NodeEntity`) | measurementMeta_parent{
        .nodeId,
        __nodeLabels__:labels(measurementMeta_parent),
        __internalNeo4jId__:id(measurementMeta_parent),
        NodeEntity_HAS_TYPE_NodeType:[(measurementMeta_parent)-[:`HAS_TYPE`]->(measurementMeta_parent_nodeType:`NodeType`) | measurementMeta_parent_nodeType{
            .nodeTypeId,
            __nodeLabels__:labels(measurementMeta_parent_nodeType),
            __internalNeo4jId__:id(measurementMeta_parent_nodeType)}]
    }],
    MeasurementMeta_HAS_TYPE_NodeType:[(measurementMeta)-[:`HAS_TYPE`]->(measurementMeta_nodeType:`NodeType`) | measurementMeta_nodeType{
        .nodeTypeId,
        __nodeLabels__:labels(measurementMeta_nodeType), 
        __internalNeo4jId__:id(measurementMeta_nodeType)
    }]
}

Andy2003 added a commit to Andy2003/spring-data-neo4j that referenced this issue Nov 9, 2021
@Andy2003 Andy2003 changed the title Multiple request for for projection Multiple request for projection Nov 9, 2021
@meistermeier meistermeier self-assigned this Nov 10, 2021
@meistermeier meistermeier added type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged labels Nov 10, 2021
@meistermeier
Copy link
Collaborator

I leave the card as a long-term enhancement open. At the moment it is not possible to determine at the moment of the circle detection if we are inspecting a projection or entity.
In the entity-world SDN only sees this:

public class MeasurementMeta extends BaseNodeEntity {

	@Relationship(type = "WEIGHTS", direction = OUTGOING)
	private MeasurementMeta baseMeasurement;
}

and interprets it as a same-type relationship -what it is- and falls back to the broader data-driver discovery with cascading queries.

@meistermeier
Copy link
Collaborator

I know that this ticket is old and a few things happened during this: The multilevel projection much improved and for the case above ^^ should be solved, .... but this leaves

public interface ApiMeasurementMetaProjection extends BaseNodeFieldsProjection {
	BaseNodeFieldsProjection getParent();
	//....
}

as a cycle because it could be the very same.
Good news are that there is now #2840 created which hopefully allows users to specify that the modelled domain cycle should not be seen as a data cycle when querying.

Closing this issue now because I cannot add anything further here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

3 participants