Skip to content

QueryExpression rewriter #820

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

Merged
merged 5 commits into from
Sep 10, 2020
Merged

QueryExpression rewriter #820

merged 5 commits into from
Sep 10, 2020

Conversation

bart-degreed
Copy link
Contributor

This PR adds a building block for rewriting a QueryExpression tree by recursively walking it and propagating changes up to the root of the tree. To minimize memory pressure, existing subtrees are reused where possible.

Aside from emitting a new tree, the rewriter can also be used to just recursively walk the tree, for example if you want to add extra validations on subterms.

While the rewriter could be implemented outside of this repository, it being correct highly depends on:

  • Proper Equals implementation on all terminal nodes (so we update them when properties are added)
  • Visiting into all expression properties that declare a (collection of) QueryExpressions

It is much more likely these are kept in sync within this repository, compared to spreading it out, then breaking subtly on version upgrades.

namespace JsonApiDotNetCore.Queries.Expressions
{
/// <summary>
/// Building block for rewriting <see cref="QueryExpression" /> trees. It walks nested expressions and updates parent on changes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It walks through nested expressions?

I am a little confused: I don't see any nested traversing happening in this class. Do you mean it is a building block for such traversal which is performed by some encapsulating object?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The visitor pattern is recursive by nature (through double-dispatch). For example, for an expression like and(not(or(...equals...))) you'd override VisitComparison and rewrite the equals subterm, passing in the source text.

Copy link
Member

@maurei maurei Sep 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if to walk nested expressions is grammatically correct, or if it should be to walk through nested expressions

Copy link
Member

@maurei maurei Sep 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still confused with the use of the word recursion here, because there is no code calling itself which is usually what recursion means in practise. ("Recursion solves such recursive problems by using functions that call themselves from within their own code.")

Or would the VisitComparison override be calling itself in your example?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't understand, familiarize yourself with the visitor pattern I linked to. Recursion does not mean a recursive function here.

I'll add 'through'

Copy link
Member

@maurei maurei Sep 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While the rewriter could be implemented outside of this repository, it being correct highly depends on:

I am also not sure if I fully understand your motivation for including this class in the repository rather than having it in our private repo

  • Proper Equals implementation on all terminal nodes (so we update them when properties are added)

I understand that the rewriter requires proper Equals implementation. This PR introduces this, which is a valuable contribution by itself, even if a rewriter would live in another repository. Since this component isn't used or referenced anywhere in this repository, I wonder if it would make sense to include it here rather than in our own? Or conversely: can we work out a solid use-case for a dev, include it in the docs and have more reasons to have it in this repo?

…re nested `return null`s, so I rewrote them for single entry/exit.
@bart-degreed bart-degreed merged commit 938985f into json-api-dotnet:master Sep 10, 2020
@bart-degreed bart-degreed deleted the expr-equals branch September 10, 2020 12:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants