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

Quantified Path Patterns #290

Open
Tracked by #162
angrykoala opened this issue Feb 1, 2024 · 9 comments · May be fixed by #354
Open
Tracked by #162

Quantified Path Patterns #290

angrykoala opened this issue Feb 1, 2024 · 9 comments · May be fixed by #354

Comments

@angrykoala
Copy link
Member

angrykoala commented Feb 1, 2024

Relevant docs:

@angrykoala
Copy link
Member Author

This task is closely related to #292

@tomhayford
Copy link

@angrykoala I'm planning to use the new quantified graph patterns and quantified relationships from 5.9 pretty extensively. I was going to try to see if I could make this update to cypher-builder myself, but it seems it might require some breaking changes to how patterns/partial patterns work.... Did you already have any ideas for how you saw this new syntax getting implemented?

@angrykoala
Copy link
Member Author

angrykoala commented Jun 28, 2024

Hi @tomhayford
I have a few ideas, but we haven't really needed quantified patterns so far, so not much though have been put into it

It would be really helpful if you could share some examples of the type of queries you want to achieve and maybe any though/preference of how the Cypher Builder API would look like?

Ideally we should try to avoid breaking changes for this. But we were already planning on releasing CypherBuilder 2.0.0 soonish so it is not the end of the world if we decide to make this for that version, although that would mean waiting a bit more for this to be ready

@angrykoala
Copy link
Member Author

A very initial draft on the api could be to add a method to Pattern class to add the quantifiers:

new Cypher.Pattern(...).quantified({min: 1, max: 5})

Translating to

(()-[]-()){1,5}

@tomhayford
Copy link

It would be really helpful if you could share some examples of the type of queries you want to achieve and maybe any though/preference of how the Cypher Builder API would look like?

My queries are pretty similar in structure the ones on the documentation page.
A set of 3 paths (simple, quantified, simple), taking advantage of how quantified paths can union with simple paths.

MATCH ()-[]-() 
      ( ()-[]-() ) {1,5}
      ()

I was thinking maybe creating an explicit Path container ("Path" right now would conflict with the Path reference... but using it just for illustrative purposes). This could make it clear where the quantifier applies. And could make logic checks easier for the union of quantified paths with other quantified paths or simple paths (need to check for minimum multiplicity and no nested quantified paths/relationships).

new Cypher.Path( new Cypher.Pattern().related().to() , {min: 1,max:5})

new Cypher.Path( new Cypher.Pattern().related().to())
    .path( new Cypher.Pattern().related().to() , {min: 1, max: 5})
    .path( new Cypher.Pattern() )

@angrykoala
Copy link
Member Author

I'm not sure Path is the correct naming for this, but I need to take a deeper look into Quantified patterns to make sure what the correct API would be for this

@angrykoala
Copy link
Member Author

angrykoala commented Jul 3, 2024

After giving some though to Quantified Patterns. I have the following proposal:

  • A QuantifiedPattern class, that accept a Pattern which at least 1 Relationship and a quantifier (min, max)
    • Optionally, this could be created directly from the Pattern: new Cypher.Pattern().related().to().quantified({min, max})
  • A QuantifiedPath class that can be created that accepts multiple Pattern and QuantifiedPattern
  • All the clauses that support quantified patterns, will accept a QuantifiedPath, as an alternative to a Pattern

For example:

const quantifiedPath = new QuantifiedPath(
    new Cypher.Pattern({labels: ["Movie"]}),
    new Cypher.Pattern({labels: ["Movie"]}).related({type: "ACTED_IN"}).to({labels:["Person"]}).quantified({min:1, max:4}),
    new Cypher.Pattern({variable: node, labels: "Movie"}) 
)

This would translate to the following pattern in Cypher

(:Movie) ((:Movie)-[:ACTED_IN]-(:Person)){1,4} (m:Movie)

It is not too dissimilar to your proposal @tomhayford, just some renames to avoid collisions. This one shouldn't be breaking either. Any thoughts?

Technically, QuantifiedPath is not an accurate name in Cypher, as these refer to a set of node pattern pairs

@tomhayford
Copy link

Yea, I think that the QuantifiedPattern and QuantifiedPath classes could do the job.

How would you see that working with Quantified Relationships? I image we'd want a similar implementation using .quantified(...). Though doing so would imply that we would also need a PartialQuantifiedPattern.

new Cypher.Pattern().related().quantified("+").to() 
new Cypher.Pattern().related().quantified({min:2}).to()
()-[]->+()
()-[]->{2,}()

Also just thinking out loud with observations on some implementation details (and I'm not sure how much syntax enforcement cypher-builder is intended to provide...) but there is some interesting complexity with the syntax rules that come with Quantified Paths & Quantified Relationships:

  • Enforce minimum number of elements in the Quantified Path is greater than zero. link
    Not Allowed: ((n)-[r]->(m)){0,10} | Allowed (:A)-[:R]->{0,10}(:B)

  • No nested Quantified Paths (Which includes Quantified Relationships since those are just an abbreviated Quantified Path Pattern)

  • Enforce Node Pattern Pair Rules , i.e. no pairs of simple path patterns (For this rule i think Variable Length Relationships count as simple paths)

  • Group Variables . One side effect is that we can't do equijoins with variables in quantified path patterns: example

    Variables that are declared inside quantified path patterns are known as group variables. They are so called because, when referred outside of the quantified path pattern, they are lists of the nodes or relationships they are bound to in the match.

  • Can't allow a relationship to be both Quantified and Variable length. Variable Length Relationships follow slightly different rules than Quantified Relationships: link

@angrykoala
Copy link
Member Author

For QuantifiedRelationships.

The syntax could be what you have there, alternatively, it could be part of the options for the relationship (like length) so:

new Cypher.Pattern().related().quantified("+").to() 

vs.

new Cypher.Pattern().related({quantified: "+"}).to() 

another third option, a bit different, but would help on correctness would be to have a separate method instead of related:

new Cypher.Pattern().relatedQuantified({quantifier: "+"}).to() 

Personally, I hate the naming of this option (ideas welcome), but it would ensure that variable length and quantifier are never in the same relationship

Regarding the extra class. We can also take the shortcut on keeping the quantifier within the PartialPattern, as really, it is not different to a normal partial pattern other than that. But I'm not sure if that would have problems in the future


In general for syntax enforcement, while we try to have a syntax and types that promote correct usage, Cypher Builder does not provide any guarantee of Cypher correctness. In general, we try to add any check that can be done through typings (as these will provide helpful warnings in the IDE), but runtime checks are out of scope for now.

From the considerations that you raised, I would say that we should pay special attention to the second, third and fifth as these could potentially be enforced through types (although we may not be able to do so without breaking changes)

I don't see how we could enforce the first and fourth just with types so I would leave them out of scope for now

On top of these considerations, there is another limitation which is making sure that a QuantifiedPattern is composed at least one relationship pattern, not a single node, it could be enforced but we would need a way of differentiating a single node pattern and a relationship pattern with types, which I think would be breaking

@angrykoala angrykoala linked a pull request Jul 5, 2024 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants