All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.redhat.lightblue.assoc.QueryPlan Maven / Gradle / Ivy

There is a newer version: 2.18.0
Show newest version
/*
 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