Skip to content

Commit d4091ca

Browse files
committed
Merge pull request #652 from bserdar/explain
Fix #231: expose explain plan
2 parents 959264f + 9148e77 commit d4091ca

File tree

15 files changed

+271
-46
lines changed

15 files changed

+271
-46
lines changed

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

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
import com.redhat.lightblue.assoc.ep.ResultDocument;
5050
import com.redhat.lightblue.assoc.ep.ExecutionContext;
5151

52+
import com.redhat.lightblue.util.JsonDoc;
53+
5254
/**
5355
* Finder for searches involving composite entities
5456
*
@@ -102,34 +104,51 @@ public void setParallelism(int n) {
102104
}
103105
}
104106

107+
private void initialize(OperationContext ctx,
108+
CRUDFindRequest req) {
109+
if(executionPlan==null) {
110+
// Composite find algorithm works like this:
111+
// 1) Find the minimal entity tree required to evaluate the request.query
112+
// 2) Find a query plan for that minimal entity tree
113+
// 3) If the query plan root is also the root entity, then expand that query
114+
// plan to include all nodes
115+
// 4) Execute search based on query plan
116+
// 5) If query plan node is not the entity root, find documents found for entity root,
117+
// and re-retrieve the documents
118+
// First: detemine minimal entity tree containing the nodes sufficient to
119+
// evaluate the query
120+
Set<CompositeMetadata> minimalTree = findMinimalSetOfQueryEntities(req.getQuery(),
121+
ctx.getTopLevelEntityMetadata());
122+
123+
selectQueryPlan(req.getQuery(), minimalTree);
124+
LOGGER.debug("Search query plan:{}, retrieval query plan:{}", searchQPlan, retrievalQPlan);
125+
126+
executionPlan = new ExecutionPlan(req.getProjection(),
127+
req.getSort(),
128+
req.getFrom(),
129+
req.getTo(),
130+
root,
131+
searchQPlan,
132+
retrievalQPlan);
133+
}
134+
}
135+
136+
@Override
137+
public void explain(OperationContext ctx,
138+
CRUDFindRequest req) {
139+
initialize(ctx,req);
140+
ExecutionContext executionContext = new ExecutionContext(ctx,null);
141+
JsonDoc doc=new JsonDoc(executionPlan.explain(executionContext));
142+
ctx.addDocument(doc);
143+
}
144+
105145
@Override
106146
public CRUDFindResponse find(OperationContext ctx,
107147
CRUDFindRequest req) {
108148
LOGGER.debug("Composite find: start");
149+
150+
initialize(ctx,req);
109151

110-
// Composite find algorithm works like this:
111-
// 1) Find the minimal entity tree required to evaluate the request.query
112-
// 2) Find a query plan for that minimal entity tree
113-
// 3) If the query plan root is also the root entity, then expand that query
114-
// plan to include all nodes
115-
// 4) Execute search based on query plan
116-
// 5) If query plan node is not the entity root, find documents found for entity root,
117-
// and re-retrieve the documents
118-
// First: detemine minimal entity tree containing the nodes sufficient to
119-
// evaluate the query
120-
Set<CompositeMetadata> minimalTree = findMinimalSetOfQueryEntities(req.getQuery(),
121-
ctx.getTopLevelEntityMetadata());
122-
123-
selectQueryPlan(req.getQuery(), minimalTree);
124-
LOGGER.debug("Search query plan:{}, retrieval query plan:{}", searchQPlan, retrievalQPlan);
125-
126-
executionPlan = new ExecutionPlan(req.getProjection(),
127-
req.getSort(),
128-
req.getFrom(),
129-
req.getTo(),
130-
root,
131-
searchQPlan,
132-
retrievalQPlan);
133152
ctx.setProperty(Mediator.CTX_QPLAN, searchQPlan == null ? retrievalQPlan : searchQPlan);
134153
LOGGER.debug("Execution plan:{}", executionPlan);
135154

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ public OperationContext search(ExecutionContext ctx, CRUDFindRequest req) {
100100
return search(block, ctx, req);
101101
}
102102

103+
103104
public OperationContext search(ExecutionBlock block, ExecutionContext ctx, CRUDFindRequest req) {
104105
OperationContext searchCtx = ctx.getOperationContext().
105106
getDerivedOperationContext(block.getMetadata().getName(), req);
@@ -152,4 +153,5 @@ public JsonNode toJson() {
152153
}
153154
return o;
154155
}
156+
155157
}

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public List<ResultDocument> getResultList(QueryExpression q, ExecutionContext ct
7373
}
7474

7575
@Override
76-
public StepResult<ResultDocument> getResults(ExecutionContext ctx) {
76+
public void initialize() {
7777
destinations = new HashMap<ExecutionBlock, Assemble>();
7878
for (ExecutionBlock x : destinationBlocks) {
7979
Assemble a = x.getStep(Assemble.class);
@@ -83,6 +83,10 @@ public StepResult<ResultDocument> getResults(ExecutionContext ctx) {
8383
throw new IllegalArgumentException("No assemble step in " + x);
8484
}
8585
}
86+
}
87+
88+
@Override
89+
public StepResult<ResultDocument> getResults(ExecutionContext ctx) {
8690
LOGGER.debug("getResults, source:{}, destinations={}", source, destinations);
8791
// Get the results from the source
8892
StepResult<ResultDocument> sourceResults = source.getStep().getResults(ctx);
@@ -194,8 +198,13 @@ public JsonNode toJson() {
194198
ArrayNode array = JsonNodeFactory.instance.arrayNode();
195199
a.set("right", array);
196200
for (ExecutionBlock b : destinationBlocks) {
197-
array.add(b.toJson());
201+
ObjectNode detail=JsonNodeFactory.instance.objectNode();
202+
AssociationQuery aq = b.getAssociationQueryForEdge(block);
203+
detail.set("associationQuery",aq.getQuery().toJson());
204+
detail.set("source",b.toJson());
205+
array.add(detail);
198206
}
199207
return o;
200208
}
209+
201210
}

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public ExecutionBlock(CompositeMetadata root, QueryPlanNode qpNode) {
8787
/**
8888
* This should be called after all execution blocks are built
8989
*/
90-
public void initialize() {
90+
public void linkBlocks() {
9191
// Build a list of all reference fields that need to be populated for the docs produces by this block
9292
// But to do that, we need the destination nodes of this block. We don't have that.
9393
// What we have is the sources. So, we populate the reference fields of our sources instead.
@@ -101,6 +101,10 @@ public void initialize() {
101101
}
102102
}
103103

104+
public void initializeSteps() {
105+
steps.stream().forEach(Step::initialize);
106+
}
107+
104108
/**
105109
* Returns the slots for the child documents of the documents of this block
106110
*/

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,10 @@ public ExecutionPlan(Projection requestProjection,
301301
}
302302

303303
for (ExecutionBlock block : qp2BlockMap.values()) {
304-
block.initialize();
304+
block.linkBlocks();
305+
}
306+
for (ExecutionBlock block : qp2BlockMap.values()) {
307+
block.initializeSteps();
305308
}
306309
}
307310

@@ -454,4 +457,8 @@ public JsonNode toJson() {
454457
public String toString() {
455458
return toJson().toString();
456459
}
460+
461+
public JsonNode explain(ExecutionContext ctx) {
462+
return resultStep.explain(ctx);
463+
}
457464
}

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,22 @@
2323

2424
import java.util.stream.Collectors;
2525

26+
import com.fasterxml.jackson.databind.JsonNode;
27+
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
28+
import com.fasterxml.jackson.databind.node.ArrayNode;
29+
import com.fasterxml.jackson.databind.node.ObjectNode;
30+
2631
import org.slf4j.Logger;
2732
import org.slf4j.LoggerFactory;
2833

2934
import com.redhat.lightblue.mediator.OperationContext;
35+
import com.redhat.lightblue.mediator.SimpleFindImpl;
3036

3137
import com.redhat.lightblue.crud.CRUDFindRequest;
3238
import com.redhat.lightblue.crud.DocCtx;
3339

40+
import com.redhat.lightblue.util.JsonDoc;
41+
3442
/**
3543
* Performs search
3644
*
@@ -79,4 +87,26 @@ protected List<ResultDocument> getSearchResults(ExecutionContext ctx) {
7987
}
8088
}
8189

90+
@Override
91+
public JsonNode explain(ExecutionContext ctx) {
92+
ObjectNode node=(ObjectNode)toJson();
93+
CRUDFindRequest req=buildFindRequest(ctx);
94+
OperationContext searchCtx = ctx.getOperationContext().
95+
getDerivedOperationContext(block.getMetadata().getName(), req);
96+
new SimpleFindImpl(block.getMetadata(), searchCtx.getFactory()).explain(searchCtx,req);
97+
List<JsonDoc> docs = searchCtx.getOutputDocumentsWithoutErrors();
98+
if(docs!=null&&!docs.isEmpty()) {
99+
if(docs.size()==1) {
100+
node.set("implementation",docs.get(0).getRoot());
101+
} else {
102+
ArrayNode arr=JsonNodeFactory.instance.arrayNode();
103+
for(JsonDoc doc:docs) {
104+
arr.add(doc.getRoot());
105+
}
106+
node.set("implementation",arr);
107+
}
108+
}
109+
return node;
110+
}
111+
82112
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ public Step(ExecutionBlock block) {
4545

4646
public abstract JsonNode toJson();
4747

48+
public void initialize() {
49+
}
50+
51+
public JsonNode explain(ExecutionContext ctx) {
52+
return toJson();
53+
}
54+
4855
/**
4956
* Returns the associated block of this step
5057
*/

crud/src/main/java/com/redhat/lightblue/crud/CRUDController.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ CRUDFindResponse find(CRUDOperationContext ctx,
113113
Long from,
114114
Long to);
115115

116+
116117
/**
117118
* Return an implementation of MetadataListener interface to receive
118119
* notifications about metadata operations. Returns null if this
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
Copyright 2013 Red Hat, Inc. and/or its affiliates.
3+
4+
This file is part of lightblue.
5+
6+
This program is free software: you can redistribute it and/or modify
7+
it under the terms of the GNU General Public License as published by
8+
the Free Software Foundation, either version 3 of the License, or
9+
(at your option) any later version.
10+
11+
This program is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
GNU General Public License for more details.
15+
16+
You should have received a copy of the GNU General Public License
17+
along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
package com.redhat.lightblue.crud;
20+
21+
import com.redhat.lightblue.query.Projection;
22+
import com.redhat.lightblue.query.QueryExpression;
23+
import com.redhat.lightblue.query.Sort;
24+
25+
import com.redhat.lightblue.util.JsonDoc;
26+
27+
/**
28+
* If the controller supports explain query, implement this interface
29+
*/
30+
public interface ExplainQuerySupport {
31+
32+
/**
33+
* Explains a query
34+
*
35+
* @param ctx Operation context
36+
* @param entity The entity to search for
37+
* @param query The query. Cannot be null
38+
* @param projection What fields to return. Cannot be null
39+
* @param sort Sort keys. Can be null
40+
* @param from starting index in the result set. Can be null. Meaninguful
41+
* only if a sort is given. Starts from 0.
42+
* @param to end index in the result set. Starts from 0, and inclusive. Can
43+
* be null.
44+
* @param destDoc This document is populated with the explained query
45+
*/
46+
void explain(CRUDOperationContext ctx,
47+
QueryExpression query,
48+
Projection projection,
49+
Sort sort,
50+
Long from,
51+
Long to,
52+
JsonDoc destDoc);
53+
54+
}

crud/src/main/java/com/redhat/lightblue/mediator/Finder.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,6 @@
2525
public interface Finder {
2626

2727
CRUDFindResponse find(OperationContext ctx, CRUDFindRequest req);
28+
29+
void explain(OperationContext ctx, CRUDFindRequest req);
2830
}

0 commit comments

Comments
 (0)