
com.redhat.lightblue.assoc.QueryPlan Maven / Gradle / Ivy
/*
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 .
*/
package com.redhat.lightblue.assoc;
import java.io.Serializable;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.Map;
import java.util.HashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.redhat.lightblue.metadata.CompositeMetadata;
import com.redhat.lightblue.util.Path;
/**
* Represents a query plan. A query plan is constructed from composite
* metadata. Starting from the root entity, a QueryPlanNode is created
* in the query plan for each entity in the composite metadata. Query
* plan is initially constructed using the same tree structure as the
* metadata, but query plan offers flip
operation to
* change the direction of an edge in the tree. During query plan
* determination, many query plans are evaluated, and an efficient one
* is selected, so this implementation of QueryPlan is optimized for
* quickly evaluating flip operations, and quickly
* creating a clone of the query plan.
*
* The internal representation is a connection matrix between the
* nodes. These are the internal fields:
*
* - nodes: Query plan nodes in an array. Each node contains a
* nodeIndex such that nodes[i].nodeIndex=i
* - connMx: Connection matrix. If connMx[from][to]=true, then there
* is an edge from nodes[from] to nodes[to]
* - fromN: fromN[i]=number of edges emanating from nodes[i]
* - toN: toN[i]=number of edges entering into nodes[i]
*
*/
public class QueryPlan implements Serializable {
private static final long serialVersionUID=1l;
private static final Logger LOGGER=LoggerFactory.getLogger(QueryPlan.class);
private final QueryPlanNodeImpl[] nodes;
private final ConnMx mx;
private final List unassignedClauses=new ArrayList<>();
private final Map edgeData=new HashMap<>();
private final QueryPlanScorer qdf;
private class QueryPlanNodeImpl extends QueryPlanNode {
private final int nodeIndex;
private String name;
public QueryPlanNodeImpl(CompositeMetadata md,QueryPlanData data,int index) {
super(md,data);
this.nodeIndex=index;
}
public QueryPlanNodeImpl(QueryPlanNodeImpl source) {
super(source);
nodeIndex=source.nodeIndex;
}
public QueryPlanNode[] getSources() {
return map(mx.getSources(nodeIndex));
}
public QueryPlanNode[] getDestinations() {
return map(mx.getDestinations(nodeIndex));
}
public String getName() {
if(name==null) {
name = md.getName() + '_' + nodeIndex;
}
return name;
}
public String toString() {
return getName();
}
}
private static final class Edge {
private final int from;
private final int to;
public Edge(int from,int to) {
this.from=from;
this.to=to;
}
@Override
public String toString() {
return from+"->"+to;
}
}
private Integer getEdgeId(int ix1,int ix2) {
if(ix1 filter) {
this.qdf=qdf;
LOGGER.debug("Constructing query plan for {}",root.getName());
List md=new ArrayList<>(16);
List edges=new ArrayList<>(16);
traverseInit(md,root,edges,filter);
LOGGER.debug("edges:{}",edges);
nodes=new QueryPlanNodeImpl[filter==null?md.size():filter.size()];
int i=0;
for(CompositeMetadata m:md) {
nodes[i]=new QueryPlanNodeImpl(m,qdf.newDataInstance(),i);
i++;
}
mx=new ConnMx(nodes.length);
for(Edge x:edges)
mx.connect(x.from,x.to);
LOGGER.debug("constructed plan:{}",this);
}
/**
* Returns the size (number of nodes) of the query plan
*/
public int getSize() {
return nodes.length;
}
private void traverseInit(List md,
CompositeMetadata root,
List edges,
Set filter) {
LOGGER.debug("Traverse {}",root.getName());
int from=md.size();
if(filter==null||filter.contains(root)) {
md.add(root);
Set children=root.getChildPaths();
LOGGER.debug("Children:{}",children);
for(Path p:children) {
CompositeMetadata child=root.getChildMetadata(p);
if(filter==null||filter.contains(child)) {
edges.add(new Edge(from,md.size()));
traverseInit(md,child,edges,filter);
}
}
}
}
/**
* Copy constructor.
*
* @param source
*/
private QueryPlan(QueryPlan source) {
qdf=source.qdf;
mx=new ConnMx(source.mx);
nodes=new QueryPlanNodeImpl[source.nodes.length];
for(int i=0;i entry:source.edgeData.entrySet()) {
QueryPlanData data;
if(entry.getValue()!=null) {
data=entry.getValue().newInstance();
data.copyFrom(entry.getValue());
} else
data=null;
edgeData.put(entry.getKey(),data);
}
unassignedClauses.addAll(source.unassignedClauses);
}
/**
* Creates a new instance of QueryPlanData
*/
public QueryPlanData newData() {
return qdf.newDataInstance();
}
/**
* Returns an array of source nodes, nodes with no incoming
* edges. This can never return null, or an empty array. Worst
* case, it will return the root entity.
*/
public QueryPlanNode[] getSources() {
return map(mx.getSources());
}
/**
* Returns the list containing clauses that cannot be associated
* with a node or an edge (i.e. clauses refer to more than two
* nodes).
*/
public List getUnassignedClauses() {
return unassignedClauses;
}
/**
* Returns the list of conjuncts associated with the undirected edge between the two nodes
*/
public QueryPlanData getEdgeData(QueryPlanNode x,
QueryPlanNode y) {
if(isOwned(x)&&isOwned(y))
return getEdgeData(((QueryPlanNodeImpl)x).nodeIndex,
((QueryPlanNodeImpl)y).nodeIndex);
else
throw new IllegalArgumentException();
}
/**
* Sets the list of conjuncts associated with the undirected edge between the two nodes
*/
public void setEdgeData(QueryPlanNode x,
QueryPlanNode y,
QueryPlanData d) {
if(isOwned(x)&&isOwned(y))
setEdgeData( ((QueryPlanNodeImpl)x).nodeIndex,
((QueryPlanNodeImpl)y).nodeIndex,
d);
else
throw new IllegalArgumentException();
}
/**
* Returns a deep copy of the query plan.
*/
public QueryPlan deepCopy() {
return new QueryPlan(this);
}
/**
* Flips the direction of a node.
*/
public void flip(QueryPlanNode x,
QueryPlanNode y) {
if(isOwned(x)&&isOwned(y)) {
mx.flip( ((QueryPlanNodeImpl)x).nodeIndex,((QueryPlanNodeImpl)y).nodeIndex);
} else
throw new IllegalArgumentException();
}
/**
* Connects two nodes
*/
public void connect(QueryPlanNode from,
QueryPlanNode to) {
if(isOwned(from)&&isOwned(to)) {
mx.connect( ((QueryPlanNodeImpl)from).nodeIndex,
((QueryPlanNodeImpl)to).nodeIndex);
} else
throw new IllegalArgumentException();
}
/**
* Returns all nodes
*/
public QueryPlanNode[] getAllNodes() {
return nodes;
}
/**
* Returns if there exists a directed edge between the nodes,
* directed from from
to to
*/
public boolean isDirectedConnected(QueryPlanNode from,
QueryPlanNode to) {
if(isOwned(from)&&isOwned(to)) {
return mx.isDirectedConnected( ((QueryPlanNodeImpl)from).nodeIndex, ((QueryPlanNodeImpl)to).nodeIndex );
}
return false;
}
/**
* Returns if there exists an edge between the two nodes, pointing either way
*/
public boolean isUndirectedConnected(QueryPlanNode from,
QueryPlanNode to) {
if(isOwned(from)&&isOwned(to)) {
return mx.isUndirectedConnected( ((QueryPlanNodeImpl)from).nodeIndex,((QueryPlanNodeImpl)to).nodeIndex);
}
return false;
}
public QueryPlanNode[] getBreadthFirstNodeOrdering() {
QueryPlanNode[] ret=new QueryPlanNode[nodes.length];
int k=0;
for(QueryPlanNode x:getSources())
ret[k++]=x;
while(k=0;i--) {
QueryPlanNode[] dests=ret[i].getDestinations();
for(QueryPlanNode x:dests)
k=addBreadthFirstNode(ret,k,x);
}
}
return ret;
}
private int addBreadthFirstNode(QueryPlanNode[] arr,int n,QueryPlanNode node) {
// Already in array?
for(int i=0;i ").
append(node.getName()).append('\n');
treeToString(node,bld);
}
}
private boolean isOwned(QueryPlanNode node) {
for(QueryPlanNodeImpl x:nodes)
if(node==x)
return true;
return false;
}
private QueryPlanNode[] map(int[] nodeIx) {
QueryPlanNode[] ret=new QueryPlanNode[nodeIx.length];
for(int i=0;i
© 2015 - 2025 Weber Informatics LLC | Privacy Policy