Skip to content

Commit e2dbad8

Browse files
authored
Merge pull request #666 from bserdar/assembler-inner-join
Fix #665: If there are query clauses for non-root nodes, we have to f…
2 parents f7d490d + a2489e9 commit e2dbad8

File tree

7 files changed

+158
-22
lines changed

7 files changed

+158
-22
lines changed

crud/src/main/java/com/redhat/lightblue/assoc/CompositeFindImpl.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@ private void initialize(OperationContext ctx,
123123
selectQueryPlan(req.getQuery(), minimalTree);
124124
LOGGER.debug("Search query plan:{}, retrieval query plan:{}", searchQPlan, retrievalQPlan);
125125

126-
executionPlan = new ExecutionPlan(req.getProjection(),
126+
executionPlan = new ExecutionPlan(req.getQuery(),
127+
req.getProjection(),
127128
req.getSort(),
128129
req.getFrom(),
129130
req.getTo(),

crud/src/main/java/com/redhat/lightblue/assoc/ep/ExecutionPlan.java

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ public class ExecutionPlan {
8383
/**
8484
* Creates an execution plan
8585
*
86+
* @param requestQuery query requested by the client
8687
* @param requestProjection projection requested by the client
8788
* @param requestSort sort requested by the client.
8889
* @param from request.from
@@ -95,7 +96,8 @@ public class ExecutionPlan {
9596
* documents found by that search. If searchQueryPlan is null, this plan
9697
* performs the search and retrieval.
9798
*/
98-
public ExecutionPlan(Projection requestProjection,
99+
public ExecutionPlan(QueryExpression requestQuery,
100+
Projection requestProjection,
99101
Sort requestSort,
100102
Long from,
101103
Long to,
@@ -229,6 +231,14 @@ public ExecutionPlan(Projection requestProjection,
229231
// Done with the search plan. Now we build the execution plan for retrieval
230232
LOGGER.debug("Building execution plan from retrieval query plan:{}", retrievalQueryPlan);
231233
List<Conjunct> unassigned = retrievalQueryPlan.getUnassignedClauses();
234+
boolean queries_in_non_root_nodes=false;
235+
for(QueryPlanNode node:retrievalQueryPlan.getAllNodes()) {
236+
if(node.getSources().length!=0&& // non-root
237+
!node.getData().getConjuncts().isEmpty()) {
238+
queries_in_non_root_nodes=true;
239+
break;
240+
}
241+
}
232242
List<QueryFieldInfo> qfi = getAllQueryFieldInfo(retrievalQueryPlan);
233243
for (QueryPlanNode node : retrievalQueryPlan.getAllNodes()) {
234244
ExecutionBlock block = qp2BlockMap.get(node);
@@ -242,6 +252,7 @@ public ExecutionPlan(Projection requestProjection,
242252
// If there is a search plan, then we already have the root documents, so simply stream
243253
// those docs from the search plan. If there is not a search plan, we search the documents
244254
// here
255+
boolean needsFilter=false;
245256
Source<ResultDocument> last;
246257
AbstractSearchStep search;
247258
if (queryRoot != null) {
@@ -252,22 +263,15 @@ public ExecutionPlan(Projection requestProjection,
252263
//There is not a search plan. We'll search documents here
253264
search = new Search(block);
254265
last = new Source<>(search);
255-
if (unassigned.isEmpty()) {
256-
// There are no unassigned clauses
266+
if (unassigned.isEmpty()&&!queries_in_non_root_nodes) {
267+
// There are no unassigned clauses or queries in non-root nodes
257268
// We can sort/limit here
258269
search.setLimit(from, to);
259270
search.setSort(requestSort);
260271
} else {
261272
// There are unassigned clauses. We can sort during search, but we have to filter and limit
262273
search.setSort(requestSort);
263-
Filter f = new Filter(block, new Source<>(search), getFilterQuery(unassigned));
264-
last = new Source<>(f);
265-
if (from != null) {
266-
last = new Source<>(new Skip(block, from.intValue(), last));
267-
}
268-
if (to != null) {
269-
last = new Source<>(new Limit(block, to.intValue() - from.intValue() + 1, last));
270-
}
274+
needsFilter=true;
271275
}
272276
}
273277
search.recordResultSetSize(true);
@@ -277,6 +281,15 @@ public ExecutionPlan(Projection requestProjection,
277281
search.setProjection(writeProjection(fields));
278282
search.setQueries(node.getData().getConjuncts());
279283
resultStep = new Assemble(block, last, destinationBlocks);
284+
if(needsFilter) {
285+
resultStep = new Filter(block, new Source<>(resultStep), requestQuery);
286+
if (from != null) {
287+
resultStep = new Skip(block, from.intValue(), new Source<>(resultStep));
288+
}
289+
if (to != null) {
290+
resultStep = new Limit(block, to.intValue() - from.intValue() + 1, new Source<>(resultStep));
291+
}
292+
}
280293
resultStep = new Project(block, new Source<>(resultStep), requestProjection);
281294
block.setResultStep(resultStep);
282295
} else {
@@ -447,11 +460,6 @@ public static Set<Path> getIncludedFieldsOfEntityForSort(ExecutionBlock block) {
447460
return ret;
448461
}
449462

450-
private QueryExpression getFilterQuery(List<Conjunct> list) {
451-
List<QueryExpression> l = list.stream().map(c -> c.getClause()).collect(Collectors.toList());
452-
return Searches.and(l);
453-
}
454-
455463
public JsonNode toJson() {
456464
return resultStep.toJson();
457465
}

crud/src/test/java/com/redhat/lightblue/assoc/ep/ExecutionPlanTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public void retrieveAandBonly() throws Exception {
9494
new IndexedFieldScorer(),
9595
q,
9696
minimalTree).choose();
97-
ExecutionPlan ep = new ExecutionPlan(p, null, null, null, md, null, searchQP);
97+
ExecutionPlan ep = new ExecutionPlan(q,p, null, null, null, md, null, searchQP);
9898
ObjectNode j = (ObjectNode) ep.toJson();
9999

100100
System.out.println("retrieveAandBonly");
@@ -129,7 +129,7 @@ public void retrieveABC() throws Exception {
129129
q,
130130
minimalTree).choose();
131131
QueryPlan retrievalQP = new QueryPlanChooser(md, new First(), new SimpleScorer(), null, minimalTree).choose();
132-
ExecutionPlan ep = new ExecutionPlan(p, null, null, null, md, searchQP, retrievalQP);
132+
ExecutionPlan ep = new ExecutionPlan(q,p, null, null, null, md, searchQP, retrievalQP);
133133
ObjectNode j = (ObjectNode) ep.toJson();
134134

135135
System.out.println("retrieveABC");
@@ -170,7 +170,7 @@ public void retrieveAandConly_CFirst() throws Exception {
170170
q,
171171
minimalTree).choose();
172172

173-
ExecutionPlan ep = new ExecutionPlan(p, null, null, null, md, searchQP, retrievalQP);
173+
ExecutionPlan ep = new ExecutionPlan(q,p, null, null, null, md, searchQP, retrievalQP);
174174
ObjectNode j = (ObjectNode) ep.toJson();
175175

176176
System.out.println("retrieveAandConly_CFirst");
@@ -224,7 +224,7 @@ public void rev_search_with_arraycond() throws Exception {
224224
q,
225225
minimalTree).choose();
226226

227-
ExecutionPlan ep = new ExecutionPlan(p, null, null, null, md, searchQP, retrievalQP);
227+
ExecutionPlan ep = new ExecutionPlan(q,p, null, null, null, md, searchQP, retrievalQP);
228228
ObjectNode j = (ObjectNode) ep.toJson();
229229

230230
System.out.println("rev_search_with_arraycond");

crud/src/test/java/com/redhat/lightblue/mediator/CompositeFinderTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,4 +659,16 @@ public void assocQWithNull2() throws Exception {
659659
Assert.assertEquals(1, response.getEntityData().size());
660660
}
661661

662+
@Test
663+
public void dont_do_outer_joins() throws Exception {
664+
// We need to use a_with_index in this test, so the execution plan becomes A->B instead of B-> A
665+
FindRequest fr = new FindRequest();
666+
fr.setQuery(query("{'$and': [ {'field':'_id','op':'$in','values':['A99','ADEEP']}, {'field':'level1.arr1.*.ref.*.field1','op':'=','rvalue':'bdeep1'} ] }"));
667+
fr.setProjection(projection("[{'field':'*','recursive':1},{'field':'level1.arr1.*.ref','recursive':1}]"));
668+
fr.setEntityVersion(new EntityVersion("A_with_index", "1.0.0"));
669+
Response response = mediator.find(fr);
670+
System.out.println(response.getEntityData());
671+
Assert.assertEquals(1, response.getEntityData().size());
672+
}
673+
662674
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
{
2+
"entityInfo" : {
3+
"name": "A_with_index",
4+
"datastore": {
5+
"backend":"mongo",
6+
"datasource": "mongodata",
7+
"collection": "user"
8+
},
9+
"indexes":[
10+
{
11+
"fields":[ {"field":"_id","dir":"$asc"} ]
12+
}
13+
]
14+
},
15+
"schema" : {
16+
"name" : "A_with_index",
17+
"version": {
18+
"value": "1.0.0",
19+
"changelog": "Test"
20+
},
21+
"status": {
22+
"value": "active"
23+
},
24+
"access" : {
25+
"insert": ["anyone"],
26+
"find":["anyone"],
27+
"update":["anyone"],
28+
"delete":["anyone"]
29+
},
30+
"fields": {
31+
"_id": {"type": "string", "constraints":{ "identity":1 } },
32+
"objectType": {"type": "string"},
33+
"field1": { "type": "string" },
34+
"obj1": {
35+
"type":"object",
36+
"fields": {
37+
"field1": { "type":"string" },
38+
"c_ref":{"type":"string"},
39+
"c": {
40+
"type":"reference",
41+
"entity":"C",
42+
"versionValue":"1.0.0",
43+
"query":{"field":"_id","op":"$eq","rfield":"$parent.c_ref"}
44+
}
45+
}
46+
},
47+
"b_ref": { "type": "string" },
48+
"b" : {
49+
"type":"reference",
50+
"entity":"B",
51+
"versionValue":"1.0.0",
52+
"query":{ "field":"_id","op":"$eq","rfield":"$parent.b_ref"}
53+
},
54+
"nonid_b_ref":{"type":"string"},
55+
"nonid_b" : {
56+
"type":"reference",
57+
"entity":"B",
58+
"versionValue":"1.0.0",
59+
"query":{"field":"field1","op":"$eq","rfield":"$parent.nonid_b_ref"}
60+
},
61+
"level1": {
62+
"type":"object",
63+
"fields": {
64+
"arr1": {
65+
"type":"array",
66+
"items": {
67+
"type":"object",
68+
"fields": {
69+
"b_ref":{"type":"string"},
70+
"ref": {
71+
"type":"reference",
72+
"entity":"B",
73+
"versionValue":"1.0.0",
74+
"query":{"field":"_id","op":"=","rfield":"$parent.b_ref"}
75+
}
76+
}
77+
}
78+
}
79+
}
80+
}
81+
}
82+
}
83+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
[{
2+
"_id":"A99",
3+
"objectType":"A",
4+
"field1":"dwzedQr4hw5HdHwWhvE7SkvZB22",
5+
"obj1":{
6+
"field1":"d6gdfa8wh7F6l6Iq.KV8",
7+
"c_ref":"C99"
8+
},
9+
"b_ref":"B99"
10+
},{
11+
"_id":"ADEEP",
12+
"objectType":"A",
13+
"level1": {
14+
"arr1": [
15+
{
16+
"b_ref":"BDEEP1"
17+
},
18+
{
19+
"b_ref":"BDEEP2"
20+
}
21+
]
22+
}
23+
},{
24+
"_id":"MANYB1",
25+
"objectType":"A",
26+
"nonid_b_ref":"MANYB1"
27+
},{
28+
"_id":"MANYB2",
29+
"objectType":"A",
30+
"nonid_b_ref":"MANYB2"
31+
}
32+
]
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[ { "_id":1,"status":"active","authentications":[ { "uid":"1","principal":"a","providerName":"p" } ],"legalEntities":[ {"legalEntityId":1,"title":"s","emails":[{"address":"[email protected]"}] } ],"objectType":"U" },
22
{ "_id":2,"status":"active","authentications":[ { "uid":"1","principal":"a" } ],"legalEntities":[ {"legalEntityId":1,"title":"s" } ],"objectType":"U" },
3-
{ "_id":3,"status":"active","authentications":[ { "uid":"1","principal":"b" } ],"legalEntities":[ {"legalEntityId":2,"title":"s" } ],"objectType":"U" }
3+
{ "_id":3,"status":"active","authentications":[ { "uid":"1","principal":"b" } ],"legalEntities":[ {"legalEntityId":2,"title":"s" } ],"objectType":"U" }
44
]
55

0 commit comments

Comments
 (0)