Skip to content

Commit

Permalink
Query plan iterator
Browse files Browse the repository at this point in the history
  • Loading branch information
Burak Serdar committed Sep 11, 2014
1 parent 75a3eaa commit 89767b9
Show file tree
Hide file tree
Showing 3 changed files with 253 additions and 5 deletions.
21 changes: 16 additions & 5 deletions crud/src/main/java/com/redhat/lightblue/assoc/QueryPlan.java
Expand Up @@ -156,6 +156,13 @@ public QueryPlan(CompositeMetadata root) {
connect(x.from,x.to);
}

/**
* Returns the size (number of nodes) of the query plan
*/
public int getSize() {
return nodes.length;
}

private void traverseInit(List<CompositeMetadata> md,CompositeMetadata root,List<Edge> edges) {
LOGGER.debug("Traverse {}",root.getName());
int from=md.size();
Expand Down Expand Up @@ -217,11 +224,15 @@ public QueryPlan deepCopy() {
/**
* Flips the direction of a node.
*/
public void flip(QueryPlanNode from,
QueryPlanNode to) {
if(isOwned(from)&&isOwned(to)) {
flip( ((QueryPlanNodeImpl)from).nodeIndex,
((QueryPlanNodeImpl)to).nodeIndex);
public void flip(QueryPlanNode x,
QueryPlanNode y) {
if(isOwned(x)&&isOwned(y)) {
int ix1=((QueryPlanNodeImpl)x).nodeIndex;
int ix2=((QueryPlanNodeImpl)y).nodeIndex;
if(connMx[ix1][ix2])
flip(ix1,ix2);
else
flip(ix2,ix1);
} else
throw new IllegalArgumentException();
}
Expand Down
113 changes: 113 additions & 0 deletions crud/src/main/java/com/redhat/lightblue/assoc/QueryPlanIterator.java
@@ -0,0 +1,113 @@
/*
Copyright 2013 Red Hat, Inc. and/or its affiliates.
This file is part of lightblue.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.redhat.lightblue.assoc;

import java.io.Serializable;

import java.util.List;
import java.util.ArrayList;

/**
* Iterates over possible query plans by rearranging the query plan graph.
*
* The given query plan is taken as an initial state. Every successive
* call to <code>next</code> will modify the query plan into a unique
* tree. Once all possible query plans are iterated, <code>next</code>
* returns false.
*/
public class QueryPlanIterator implements Serializable {

private static final long serialVersionUID=1l;

private final QueryPlan qp;

private final Edge[] edges;

private final class Edge {
private final QueryPlanNode n1;
private final QueryPlanNode n2;
private boolean v;

public Edge(QueryPlanNode n1,
QueryPlanNode n2) {
this.n1=n1;
this.n2=n2;
}

public void flip() {
v=!v;
qp.flip(n1,n2);
}
}

/**
* Construct a query plan iterator that operates on the given
* query plan
*/
public QueryPlanIterator(QueryPlan qp) {
this.qp=qp;
List<Edge> edgeList=new ArrayList<>(16);
QueryPlanNode[] sources=qp.getSources();
for(QueryPlanNode x:sources)
findEdges(edgeList,x);
edges=edgeList.toArray(new Edge[edgeList.size()]);
}

private void findEdges(List<Edge> l,QueryPlanNode from) {
QueryPlanNode[] dests=from.getDestinations();
for(QueryPlanNode to:dests) {
l.add(new Edge(from,to));
findEdges(l,to);
}
}

/**
* Returns the query plan
*/
public QueryPlan getQueryPlan() {
return qp;
}

/**
* Modifies the query plan into a unique tree.
*
* @return If true, tne query plan is configured into a unique
* tree. If false, query plan is now returned back to its original
* state during iterator construction, and the iteration is
* expected to stop.
*/
public boolean next() {
int i;
for(i=edges.length-1;i>=0;i--) {
edges[i].flip();
if(edges[i].v)
break;
}
return i>=0;
}

public String toString() {
StringBuilder bld=new StringBuilder();
for(Edge e:edges)
bld.append(e.v?'0':'1');
return bld.toString();
}

}

@@ -0,0 +1,124 @@
/*
Copyright 2013 Red Hat, Inc. and/or its affiliates.
This file is part of lightblue.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.redhat.lightblue.assoc;

import org.junit.Test;
import org.junit.Assert;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;

import com.redhat.lightblue.util.Path;
import com.redhat.lightblue.util.JsonUtils;
import com.redhat.lightblue.util.test.AbstractJsonNodeTest;
import com.redhat.lightblue.metadata.CompositeMetadata;
import com.redhat.lightblue.metadata.AbstractGetMetadata;
import com.redhat.lightblue.metadata.EntityMetadata;
import com.redhat.lightblue.metadata.TypeResolver;
import com.redhat.lightblue.metadata.types.DefaultTypes;
import com.redhat.lightblue.metadata.parser.Extensions;
import com.redhat.lightblue.metadata.parser.JSONMetadataParser;
import com.redhat.lightblue.query.QueryExpression;
import com.redhat.lightblue.query.Projection;
import com.redhat.lightblue.TestDataStoreParser;

import java.io.IOException;

public class QueryPlanIteratorTest extends AbstractJsonNodeTest {

private static final JsonNodeFactory factory = JsonNodeFactory.withExactBigDecimals(true);

private static JsonNode json(String q) {
try {
return JsonUtils.json(q.replace('\'', '\"'));
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private EntityMetadata getMd(String fname) throws Exception {
JsonNode node = loadJsonNode(fname);
Extensions<JsonNode> extensions = new Extensions<>();
extensions.addDefaultExtensions();
extensions.registerDataStoreParser("mongo", new TestDataStoreParser<JsonNode>());
TypeResolver resolver = new DefaultTypes();
JSONMetadataParser parser = new JSONMetadataParser(extensions, resolver, factory);
return parser.parseEntityMetadata(node);
}

private QueryExpression query(String s) throws Exception {
return QueryExpression.fromJson(JsonUtils.json(s.replace('\'', '\"')));
}

private Projection projection(String s) throws Exception {
return Projection.fromJson(JsonUtils.json(s.replace('\'', '\"')));
}

private class GMD extends AbstractGetMetadata {
public GMD(Projection p,QueryExpression q) {
super(p,q);
}

@Override
protected EntityMetadata retrieveMetadata(Path injectionField,
String entityName,
String version) {
try {
return getMd("composite/"+entityName+".json");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

@Test
public void simpleTest() throws Exception {
GMD gmd=new GMD(projection("{'field':'obj1.c','include':1}"),null);
CompositeMetadata md=CompositeMetadata.buildCompositeMetadata(getMd("composite/A.json"),gmd);
QueryPlan qp=new QueryPlan(md);
QueryPlanIterator itr=new QueryPlanIterator(qp);

// There is only one edge, two iterations are possible
System.out.println(qp.treeToString());
Assert.assertEquals("A",qp.getSources()[0].getMetadata().getName());

Assert.assertTrue(itr.next());
System.out.println(qp.treeToString());
Assert.assertEquals("C",qp.getSources()[0].getMetadata().getName());

Assert.assertFalse(itr.next());
}

@Test
public void two_level_test() throws Exception {
GMD gmd=new GMD(projection("[{'field':'r.*.r.*','include':1},{'field':'b.*.','include':1}]"),null);
CompositeMetadata md=CompositeMetadata.buildCompositeMetadata(getMd("composite/R.json"),gmd);
QueryPlan qp=new QueryPlan(md);
QueryPlanIterator itr=new QueryPlanIterator(qp);

// 3 edges, 8 query plans
System.out.println(qp.treeToString());
for(int i=0;i<7;i++) {
Assert.assertTrue(itr.next());
System.out.println(qp.treeToString());
}
Assert.assertFalse(itr.next());
}
}

0 comments on commit 89767b9

Please sign in to comment.