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

Support @normalize directive for subqueries #4042

Merged
merged 26 commits into from
Oct 11, 2019

Conversation

animesh2049
Copy link
Contributor

@animesh2049 animesh2049 commented Sep 23, 2019

@normalize for subqueries.

resolves #1755


This change is Reviewable

Copy link

@pullrequest pullrequest bot left a comment

Choose a reason for hiding this comment

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

✅ A review job has been created and sent to the PullRequest network.


@animesh2049 you can click here to see the review status or cancel the code review job.

query/outputnode.go Outdated Show resolved Hide resolved
Copy link
Contributor

@mangalaman93 mangalaman93 left a comment

Choose a reason for hiding this comment

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

Reviewable status: 0 of 3 files reviewed, 3 unresolved discussions (waiting on @animesh2049)


query/outputnode.go, line 64 at r1 (raw file):

	New(attr string) outputNode
	SetUID(uid uint64, attr string)
	SetFlatten(flatten bool)

Can we call it SetNormalize instead? or something equivalent. Doesn't make sense to use two different names for the same thing


query/outputnode.go, line 815 at r1 (raw file):

}

func createAttrs(oNode outputNode) error {

This function is not needed, implementation can be moved in the caller itself

Copy link
Contributor Author

@animesh2049 animesh2049 left a comment

Choose a reason for hiding this comment

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

Reviewable status: 0 of 3 files reviewed, 3 unresolved discussions (waiting on @golangcibot and @mangalaman93)


query/outputnode.go, line 64 at r1 (raw file):

Previously, mangalaman93 (Aman Mangal) wrote…

Can we call it SetNormalize instead? or something equivalent. Doesn't make sense to use two different names for the same thing

Done.


query/outputnode.go, line 815 at r1 (raw file):

Previously, mangalaman93 (Aman Mangal) wrote…

This function is not needed, implementation can be moved in the caller itself

Done.


query/outputnode.go, line 841 at r1 (raw file):

Previously, golangcibot (Bot from GolangCI) wrote…

Error return value of flattenResult is not checked (from errcheck)

Done.

Copy link

@pullrequest pullrequest bot left a comment

Choose a reason for hiding this comment

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

I've left a single inline comment regarding struct initialization and accessing its pointer value.


Reviewed with ❤️ by PullRequest

if err != nil {
return err
}

Copy link

Choose a reason for hiding this comment

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

This is not a blocker but a readability recommendation, prefer being explicit that 2 operations are happening.

jn := fastJsonNode{attr: node.attr, attrs: attrList[0]}
parent.attrs[childIdx] = &jn

normalized, err := n1.(*fastJsonNode).normalize()
if err != nil {
fj.AddListChild(sg.Params.Alias, n1)
if err := flattenResult(n1.(*fastJsonNode), fj, len(fj.attrs)-1); err != nil {
Copy link

Choose a reason for hiding this comment

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

Is it possible that fj.attrs returns 0? Is the resulting -1 a valid argument to flattenResult?

@animesh2049 animesh2049 force-pushed the animesh2049/normalize_subqueries branch from 9dd6cda to f390c8b Compare September 24, 2019 12:36
Copy link

@pullrequest pullrequest bot left a comment

Choose a reason for hiding this comment

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

⚠️ Warning

PullRequest detected a force-push on this branch. This may have caused some information to be lost, and additional time may be required to complete review of the code. Read More

Copy link
Contributor

@mangalaman93 mangalaman93 left a comment

Choose a reason for hiding this comment

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

Reviewable status: 0 of 6 files reviewed, 7 unresolved discussions (waiting on @animesh2049 and @golangcibot)


query/outputnode.go, line 482 at r3 (raw file):

	}

	if err := normalizeResult(fj, nil, 0); err != nil {

Why are we passing 0 here? add a comment


query/outputnode.go, line 828 at r3 (raw file):

		for _, attrs := range attrList[1:] {
			parent.attrs = append(parent.attrs,
				&fastJsonNode{attr: node.attr, attrs: attrs})

isChild copy?


query/query2_test.go, line 1559 at r3 (raw file):

func TestNormalizeDirectiveSubQueryLevel1(t *testing.T) {

newline


query/query2_test.go, line 1628 at r3 (raw file):

	js := processQueryNoErr(t, query)
	require.JSONEq(t, `{"data":{"me":[{"fn":"Michonne","mn":"Michonne","n":"Rick Grimes","sn":"Andre"},{"fn":"Michonne","mn":"Michonne","n":"Rick Grimes","sn":"Helmut"},{"mn":"Michonne","n":"Glenn Rhee","sn":"Andre"},{"mn":"Michonne","n":"Glenn Rhee","sn":"Helmut"},{"mn":"Michonne","n":"Daryl Dixon","sn":"Andre"},{"mn":"Michonne","n":"Daryl Dixon","sn":"Helmut"},{"fn":"Glenn Rhee","mn":"Michonne","n":"Andrea","sn":"Andre"},{"fn":"Glenn Rhee","mn":"Michonne","n":"Andrea","sn":"Helmut"}]}}`, js)

format

Copy link
Contributor Author

@animesh2049 animesh2049 left a comment

Choose a reason for hiding this comment

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

Reviewable status: 0 of 6 files reviewed, 7 unresolved discussions (waiting on @animesh2049, @golangcibot, @mangalaman93, and @pullrequest[bot])


query/outputnode.go, line 480 at r2 (raw file):

Previously, pullrequest[bot] wrote…

Is it possible that fj.attrs returns 0? Is the resulting -1 a valid argument to flattenResult?

Done.


query/outputnode.go, line 482 at r3 (raw file):

Previously, mangalaman93 (Aman Mangal) wrote…

Why are we passing 0 here? add a comment

Done.


query/outputnode.go, line 828 at r3 (raw file):

Previously, mangalaman93 (Aman Mangal) wrote…

isChild copy?

Done.


query/query2_test.go, line 1559 at r3 (raw file):

Previously, mangalaman93 (Aman Mangal) wrote…

newline

Done.


query/query2_test.go, line 1628 at r3 (raw file):

Previously, mangalaman93 (Aman Mangal) wrote…

format

Done.

@animesh2049 animesh2049 marked this pull request as ready for review September 24, 2019 14:51
Copy link
Contributor

@mangalaman93 mangalaman93 left a comment

Choose a reason for hiding this comment

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

Reviewable status: 0 of 6 files reviewed, 5 unresolved discussions (waiting on @animesh2049, @danielmai, @golangcibot, @manishrjain, @MichaelJCompton, @pawanrawal, and @pullrequest[bot])

a discussion (no related file):
:lgtm:



query/outputnode.go, line 834 at r4 (raw file):

		for _, attrs := range attrList[1:] {
			parent.attrs = append(parent.attrs,
				&fastJsonNode{attr: node.attr, attrs: attrs, isChild: node.isChild})

nit: may be define the node first into a variable and then perform append

Copy link
Contributor

@pawanrawal pawanrawal left a comment

Choose a reason for hiding this comment

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

Mostly looks good. I'd still want you to modify the tests to start with multiple ids at root and please see other comments as well. I'll have another look after you have replied to them.

Reviewable status: 0 of 6 files reviewed, 13 unresolved discussions (waiting on @animesh2049, @danielmai, @golangcibot, @manishrjain, @MichaelJCompton, @pawanrawal, and @pullrequest[bot])


query/outputnode.go, line 64 at r4 (raw file):

	New(attr string) outputNode
	SetUID(uid uint64, attr string)
	SetNormalize(isNormalized bool)

Do you need to pass in parameter here? Or could you just set the value to true whenever this function is called on fastJsonNode.


query/outputnode.go, line 88 at r4 (raw file):

	attrs        []*fastJsonNode
	list         bool
	isNormalized bool

Please add a comment about when is this set to true. For e.g. is it only set for a node on which the directive was applied or for all its children as well?


query/outputnode.go, line 829 at r4 (raw file):

		parent.attrs[childIdx] = &fastJsonNode{ // A small optimization. Instead of removing
			attr:    node.attr,    // element from slice, we are replacing it
			attrs:   attrList[0],  // with one of the node that we intend to

Please move these comments to above the code. Inline comments are ok if they are only on a single line.


query/outputnode.go, line 836 at r4 (raw file):

				&fastJsonNode{attr: node.attr, attrs: attrs, isChild: node.isChild})
		}
	} else {

You can return before this line and then you don't need else can out indent the block below.


query/query1_test.go, line 1067 at r4 (raw file):

	}`
	js := processQueryNoErr(t, query)
	require.JSONEq(t, `{"data":{"me":[{"Friend":"Rick Grimes","Me":"Michonne"},{"Cofriend":"Glenn Rhee","Friend":"Michonne","Me":"Rick Grimes"},{"Me":"Daryl Dixon"},{"Friend":"Glenn Rhee","Me":"Michonne"},{"Friend":"Daryl Dixon","Me":"Michonne"},{"Cofriend":"Glenn Rhee","Friend":"Andrea","Me":"Michonne"},{"Cofriend":"Daryl Dixon","Friend":"Michonne","Me":"Rick Grimes"},{"Cofriend":"Andrea","Friend":"Michonne","Me":"Rick Grimes"}]}}`, js)

Why did the response change here? Was the previous result wrong?


query/query2_test.go, line 1616 at r4 (raw file):

	query := `
		{
			me(func: uid(0x01)) {

Ideally, I'd prefer we start with multiple uids always in tests. Or if you want you can have tests both for single and multiple ids. In the past I have encountered bugs where things worked with single ids but not multiple.


query/query2_test.go, line 1681 at r4 (raw file):

					dob
					friend @normalize { # Results of this subquery will be normalized
						fn : name

Can you add another field without an alias to this test and also possibly another level of nesting here? Right now the result of the query seems to be same as what it would be without the directive.


query/query2_test.go, line 1740 at r4 (raw file):

		}`, js)
}

Can you please add one more test where two sibling nodes with children have @normalize? Something like the example below, fill in values for some_predicate and its children.

		{
			me(func: uid(0x01, 0x02, 0x03))  {
				mn: name
				gender
				friend @normalize {
					n: name
					dob
					friend {
						fn : name
					}
				}
				some_predicate @normalize {
                                       child {
                                          n: name
                                       }
                                }
			}
		}

Copy link
Contributor Author

@animesh2049 animesh2049 left a comment

Choose a reason for hiding this comment

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

Reviewable status: 0 of 6 files reviewed, 13 unresolved discussions (waiting on @animesh2049, @danielmai, @golangcibot, @manishrjain, @MichaelJCompton, @pawanrawal, and @pullrequest[bot])


query/outputnode.go, line 64 at r4 (raw file):

dst.SetNormalize(sg.Params.Normalize)

I will have to check if sg.Params.Normalize is true and then call SetNormalize. Will that be better ?


query/outputnode.go, line 88 at r4 (raw file):
It will be true for children as well.

Normalize: gchild.Normalize || sg.Params.Normalize,

I am recursively setting Normalized to true for all the children of node where @normalize has been applied. Later in pretraverse setting isNormalized when Normalized is true.
I also added a comment.


query/outputnode.go, line 829 at r4 (raw file):

Previously, pawanrawal (Pawan Rawal) wrote…

Please move these comments to above the code. Inline comments are ok if they are only on a single line.

Done.


query/outputnode.go, line 836 at r4 (raw file):

Previously, pawanrawal (Pawan Rawal) wrote…

You can return before this line and then you don't need else can out indent the block below.

Done.


query/query1_test.go, line 1067 at r4 (raw file):

Previously, pawanrawal (Pawan Rawal) wrote…

Why did the response change here? Was the previous result wrong?

Only the order of output in the list has changed. Test was failing because of that.


query/query2_test.go, line 1616 at r4 (raw file):

Previously, pawanrawal (Pawan Rawal) wrote…

Ideally, I'd prefer we start with multiple uids always in tests. Or if you want you can have tests both for single and multiple ids. In the past I have encountered bugs where things worked with single ids but not multiple.

Done.


query/query2_test.go, line 1681 at r4 (raw file):

Previously, pawanrawal (Pawan Rawal) wrote…

Can you add another field without an alias to this test and also possibly another level of nesting here? Right now the result of the query seems to be same as what it would be without the directive.

Done


query/query2_test.go, line 1740 at r4 (raw file):

Previously, pawanrawal (Pawan Rawal) wrote…

Can you please add one more test where two sibling nodes with children have @normalize? Something like the example below, fill in values for some_predicate and its children.

		{
			me(func: uid(0x01, 0x02, 0x03))  {
				mn: name
				gender
				friend @normalize {
					n: name
					dob
					friend {
						fn : name
					}
				}
				some_predicate @normalize {
                                       child {
                                          n: name
                                       }
                                }
			}
		}

Done.

Copy link

@pullrequest pullrequest bot left a comment

Choose a reason for hiding this comment

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

1 Message
⚠️ Due to its size, this pull request will likely have a little longer turnaround time and will probably require multiple passes from our reviewers.

Copy link
Contributor

@campoy campoy left a comment

Choose a reason for hiding this comment

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

Reviewable status: 0 of 6 files reviewed, 14 unresolved discussions (waiting on @animesh2049, @danielmai, @golangcibot, @manishrjain, @MichaelJCompton, @pawanrawal, and @pullrequest[bot])


wiki/content/query-language/index.md, line 1899 at r5 (raw file):

{{< /runnable >}}

You can also apply `@normalize` on nested query blocks. It will work similarly but only flatten the result of nested query block where `@normalize` has been applied.

the result of the nested query block ...

Copy link
Contributor

@pawanrawal pawanrawal left a comment

Choose a reason for hiding this comment

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

Reviewable status: 0 of 6 files reviewed, 7 unresolved discussions (waiting on @animesh2049, @danielmai, @golangcibot, @mangalaman93, @manishrjain, @MichaelJCompton, and @pullrequest[bot])

a discussion (no related file):

Previously, mangalaman93 (Aman Mangal) wrote…

:lgtm:

:lgtm:



query/outputnode.go, line 64 at r4 (raw file):

Previously, animesh2049 (Animesh Chandra Pathak) wrote…

dst.SetNormalize(sg.Params.Normalize)

I will have to check if sg.Params.Normalize is true and then call SetNormalize. Will that be better ?

that's right. No this is ok in that case.

Copy link
Contributor

@manishrjain manishrjain left a comment

Choose a reason for hiding this comment

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

Reviewable status: 0 of 6 files reviewed, 8 unresolved discussions (waiting on @animesh2049, @danielmai, @golangcibot, @mangalaman93, @manishrjain, @MichaelJCompton, and @pullrequest[bot])

a discussion (no related file):

Previously, pawanrawal (Pawan Rawal) wrote…

:lgtm:

Is this doing more work than before? Because of the extra recursive iteration this is doing?

If this PR isn't affecting performance, then :lgtm:



query/query2_test.go, line 1767 at r5 (raw file):

					n: name
					dob
					friend @normalize { # It would appear as if @normalize isn't here.

"This would be ignored."

Copy link
Contributor Author

@animesh2049 animesh2049 left a comment

Choose a reason for hiding this comment

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

Reviewable status: 0 of 7 files reviewed, 8 unresolved discussions (waiting on @animesh2049, @campoy, @danielmai, @golangcibot, @mangalaman93, @manishrjain, @MichaelJCompton, and @pullrequest[bot])


query/query2_test.go, line 1767 at r5 (raw file):

Previously, manishrjain (Manish R Jain) wrote…

"This would be ignored."

Done.


wiki/content/query-language/index.md, line 1899 at r5 (raw file):

Previously, campoy (Francesc Campoy) wrote…

the result of the nested query block ...

Done.

Copy link
Contributor

@pawanrawal pawanrawal left a comment

Choose a reason for hiding this comment

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

Reviewable status: 0 of 6 files reviewed, 9 unresolved discussions (waiting on @animesh2049, @campoy, @danielmai, @golangcibot, @mangalaman93, @manishrjain, @MichaelJCompton, and @pullrequest[bot])

a discussion (no related file):

Previously, manishrjain (Manish R Jain) wrote…

Is this doing more work than before? Because of the extra recursive iteration this is doing?

If this PR isn't affecting performance, then :lgtm:

Looks much simpler now.

Can you also check with a query which has an intermediate node of type uid and not [uid]? Also are you going to check in the benchmarks that you did before and after this change?



query/outputnode_test.go, line 98 at r6 (raw file):

}

func TestNormalizeJSONUid1(t *testing.T) {

Why remove these?

Copy link
Contributor Author

@animesh2049 animesh2049 left a comment

Choose a reason for hiding this comment

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

Reviewable status: 0 of 6 files reviewed, 9 unresolved discussions (waiting on @animesh2049, @campoy, @danielmai, @golangcibot, @mangalaman93, @manishrjain, @MichaelJCompton, @pawanrawal, and @pullrequest[bot])


query/outputnode_test.go, line 98 at r6 (raw file):

Previously, pawanrawal (Pawan Rawal) wrote…

Why remove these?

I have changed normalize function. Now instead of recursing it just flattens it's children. It expects all it's children have already called normalize flattened their children.

Copy link
Contributor Author

@animesh2049 animesh2049 left a comment

Choose a reason for hiding this comment

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

Reviewable status: 0 of 6 files reviewed, 9 unresolved discussions (waiting on @animesh2049, @campoy, @danielmai, @golangcibot, @mangalaman93, @manishrjain, @MichaelJCompton, @pawanrawal, and @pullrequest[bot])

a discussion (no related file):

Previously, pawanrawal (Pawan Rawal) wrote…

Looks much simpler now.

Can you also check with a query which has an intermediate node of type uid and not [uid]? Also are you going to check in the benchmarks that you did before and after this change?

Benchmark that I wrote earlier are no more valid because of the recent changes. However I tried running normalize query from docs on 21million dataset. I don't see significant latency changes.


@animesh2049 animesh2049 force-pushed the animesh2049/normalize_subqueries branch from c148603 to 7b623d8 Compare October 9, 2019 18:30
Copy link
Contributor Author

@animesh2049 animesh2049 left a comment

Choose a reason for hiding this comment

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

Reviewable status: 2 of 7 files reviewed, 13 unresolved discussions (waiting on @animesh2049, @campoy, @danielmai, @golangcibot, @mangalaman93, @manishrjain, @MichaelJCompton, @pawanrawal, and @pullrequest[bot])


query/outputnode.go, line 681 at r9 (raw file):

Previously, pawanrawal (Pawan Rawal) wrote…

Please add a comment that addAsList is only used for Normalize queries and not for others.

Removed it and added everything as list when for @normalize

Copy link
Contributor

@campoy campoy left a comment

Choose a reason for hiding this comment

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

Reviewable status: 2 of 7 files reviewed, 20 unresolved discussions (waiting on @animesh2049, @campoy, @danielmai, @golangcibot, @mangalaman93, @manishrjain, @MichaelJCompton, @pawanrawal, and @pullrequest[bot])


query/outputnode.go, line 302 at r10 (raw file):

	cnt := 0
	for _, a := range fj.attrs {
		// Here we are counting all non scalar attributes of fj. If there are any such

all non-scalar attributes in fj.attrs


query/outputnode.go, line 304 at r10 (raw file):

		// Here we are counting all non scalar attributes of fj. If there are any such
		// attributes, we will flatten it, otherwise we will return all attributes.

what is it here? fj?


query/outputnode.go, line 305 at r10 (raw file):

		// attributes, we will flatten it, otherwise we will return all attributes.

		// When we call addMapChild it tries to find whether there is already a attribute

already an attribute


query/outputnode.go, line 309 at r10 (raw file):

		// such attribute, it creates an attribute with isChild = false. In those cases
		// sometimes cnt remains zero  and normalize returns attributes without flattening.
		// So we are using len(a.attrs) > 0 instead of a.isChild

Try to write this again in a much more clear way.
Think about someone that doesn't understand your code yet and how this would help them.


query/outputnode.go, line 327 at r10 (raw file):

	for _, a := range fj.attrs {
		// Check comment at previous occurrence of len(a.attrs) > 0
		if len(a.attrs) == 0 {

I would instead write a quick function, document it, and use it in all these places.
It will be inlined anyway and it will make your code more obvious.


query/outputnode.go, line 723 at r10 (raw file):

						// calling normalize after pretraverse.
						// Now normalize() only flattens one level,
						// the expectation is that it's children have

its children


query/outputnode_test.go, line 98 at r10 (raw file):

}

func TestNormalizeJSONUid1(t *testing.T) {

Why are you removing these tests?

Copy link
Contributor Author

@animesh2049 animesh2049 left a comment

Choose a reason for hiding this comment

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

Reviewable status: 2 of 7 files reviewed, 20 unresolved discussions (waiting on @animesh2049, @campoy, @danielmai, @golangcibot, @mangalaman93, @manishrjain, @MichaelJCompton, @pawanrawal, and @pullrequest[bot])


query/outputnode.go, line 302 at r10 (raw file):

Previously, campoy (Francesc Campoy) wrote…

all non-scalar attributes in fj.attrs

Done.


query/outputnode.go, line 304 at r10 (raw file):

Previously, campoy (Francesc Campoy) wrote…

what is it here? fj?

Yes


query/outputnode.go, line 305 at r10 (raw file):

Previously, campoy (Francesc Campoy) wrote…

already an attribute

Done.


query/outputnode.go, line 309 at r10 (raw file):

Previously, campoy (Francesc Campoy) wrote…

Try to write this again in a much more clear way.
Think about someone that doesn't understand your code yet and how this would help them.

This is not exactly the explanation of what code is doing, rather I am trying to explain why I am using len(a.ttrs), so I guess reader must have some context about code.


query/outputnode.go, line 327 at r10 (raw file):

Previously, campoy (Francesc Campoy) wrote…

I would instead write a quick function, document it, and use it in all these places.
It will be inlined anyway and it will make your code more obvious.

I am using two different variations len(node.attrs)>0 and len(node.attrs) == 0. I will probably have to write 2 different functions. Would that be better ?


query/outputnode.go, line 723 at r10 (raw file):

Previously, campoy (Francesc Campoy) wrote…

its children

Done.


query/outputnode_test.go, line 98 at r10 (raw file):

Previously, campoy (Francesc Campoy) wrote…

Why are you removing these tests?

Behavior of normalize function has changed, so tests aren't valid anymore.

Copy link
Contributor

@manishrjain manishrjain left a comment

Choose a reason for hiding this comment

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

Ok. Good to go. Assuming that we're not doing any more work than before, and the query latency is not going to be affected.

Reviewable status: 2 of 7 files reviewed, 20 unresolved discussions (waiting on @animesh2049, @campoy, @danielmai, @golangcibot, @mangalaman93, @manishrjain, @MichaelJCompton, @pawanrawal, and @pullrequest[bot])

Copy link
Contributor Author

@animesh2049 animesh2049 left a comment

Choose a reason for hiding this comment

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

Dismissed @pawanrawal and @pullrequest[bot] from 2 discussions.
Reviewable status: 1 of 7 files reviewed, 18 unresolved discussions (waiting on @campoy, @danielmai, @golangcibot, @mangalaman93, @manishrjain, @MichaelJCompton, @pawanrawal, and @pullrequest[bot])

@animesh2049 animesh2049 merged commit 3933ad1 into master Oct 11, 2019
@animesh2049 animesh2049 deleted the animesh2049/normalize_subqueries branch October 11, 2019 13:15
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.

Support for @cascade and @normalize directives for subqueries
7 participants