/* ---- This file is part of SECONDO. Copyright (C) 2012, University in Hagen Faculty of Mathematic and Computer Science, Database Systems for New Applications. SECONDO 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 2 of the License, or (at your option) any later version. SECONDO 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 SECONDO; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ---- */ #include "QueryGraph.h" #include "NestedList.h" #include "ListUtils.h" #include "Utils.h" #include "PropertyGraphQueryProcessor.h" #include using namespace std; namespace pgraph2 { //---------------------------------------------------------------------------- QueryGraph::QueryGraph(RelationRegistry *relreg) { RelRegistry=relreg; } //---------------------------------------------------------------------------- QueryGraph::~QueryGraph() { } //---------------------------------------------------------------------------- void QueryGraph::DumpGraph() { cout << "GRAPH:"<Name << " = " << p->Value<< endl;; } cout << " EDGES:" <Alias << " -> " << e.ToNode->Alias << "[:"<< e.TypeName <<"]" << endl; for (auto&& p : e.Filters) cout << " " << p->Name << " = " << p->Value<< endl;; } cout << "." <:"< CARD: "<"; list :: iterator itf; for(auto&& item : n.Filters) { data << ((item->Indexed)?"(INDEXED) ":""); data << item->Name <<" = " << item->Value << "
\n"; } data<<">]\n"; } // for (auto&& e : Edges) { data << "n"<_uid<<"->n"<_uid<<"\n"; data << " [label=<"; if (e.Alias!="") data<AVG: "<"; data<<">]\n"; } data<<"}\n"; std::ofstream outfile; outfile.open(fn, std::ios_base::trunc); outfile << data.str(); LOGOP(20,"/QueryGraph::DumpGraphDot"); } //---------------------------------------------------------------------------- QueryGraphNode::QueryGraphNode() { static int treenodeids=100; _uid=treenodeids++; } //---------------------------------------------------------------------------- QueryGraphNode* QueryGraph::addNode(string alias, string typename_, ListExpr *props) { LOGOP(30,"ADDNODE ",alias,":",typename_, (props==NULL?"":" PROPS") ); QueryGraphNode n; n.ID=NextNodeID; n.Alias=alias; n.TypeName=typename_; AliasList.AddNode(n.Alias, n.TypeName); if (props!=NULL) readFilters(n.Filters, *props); Nodes.push_back(n); NextNodeID++; return &Nodes.back(); } //---------------------------------------------------------------------------- void QueryGraph::addEdge(string edgelias, string typename_,string alias, string alias2, ListExpr *props) { LOGOP(30, "ADDEDGE ", alias, " -[",edgelias,":", typename_, "]-> ", alias2, (props==NULL?"":" PROPS") ); QueryGraphEdge e; e.ID=NextEdgeID; e.Alias=edgelias; e.TypeName=typename_; for(auto&& n:Nodes) if (n.Alias==alias) e.FromNode=&n; for(auto&& n:Nodes) if (n.Alias==alias2) e.ToNode=&n; AliasList.AddEdge(e.Alias, e.TypeName); RelationInfo *relinfo = RelRegistry->GetRelationInfo(e.TypeName); if (relinfo!=NULL) { e.CostFw=relinfo->statistics->avgcardForward; e.CostBw=relinfo->statistics->avgcardBackward;; } // create missing nodes if (e.FromNode==NULL) e.FromNode=addNode(alias,"", NULL); if (e.ToNode==NULL) e.ToNode=addNode(alias2,"", NULL); if (props!=NULL) readFilters(e.Filters, *props); Edges.push_back(e); if ((e.ToNode==NULL)||(e.FromNode==NULL)) throw PGraph2Exception("edge #"+to_string(NextEdgeID)+ " could not be connected"); NextEdgeID++; } //---------------------------------------------------------------------------- void dfs(vector adj[], bool *vis, int x) { vis[x] = true; for (auto i : adj[x]) { if (!vis[i]) dfs(adj,vis,i); } } //---------------------------------------------------------------------------- void QueryGraph::Validate() { // check if types are existing for (auto&& n : Nodes) { if (n.TypeName!="") { RelationInfo *relinfo = RelRegistry->GetRelationInfo(n.TypeName); if (relinfo==NULL) throw PGraph2Exception("undefined node type: "+ n.TypeName ); } } for (auto&& e : Edges) { if (e.TypeName!="") { RelationInfo *relinfo = RelRegistry->GetRelationInfo(e.TypeName); if (relinfo==NULL) throw PGraph2Exception("undefined edge type: "+ e.TypeName ); } } CompleteTypes(); if (!IsConnected()) throw PGraph2Exception("query graph is not connected and cycle free"); for (auto&& n : Nodes) { if (n.TypeName=="") throw PGraph2Exception("missing node type"+ (n.Alias!=""?" for alias "+n.Alias:"")); RelationInfo *relinfo = RelRegistry->GetRelationInfo(n.TypeName); // mark filters as indexed for(auto&& fi:n.Filters) { if (RelRegistry->IsIndexed(relinfo->Name, fi->Name)) fi->Indexed=true; } // get statistics if (relinfo!=NULL) { n.Cost=relinfo->statistics->cardinality; } } for (auto&& e : Edges) { if (e.TypeName=="") throw PGraph2Exception("missing edge type"+ (e.Alias!=""?" for alias "+e.Alias:"")); RelationInfo *relinfo = RelRegistry->GetRelationInfo(e.TypeName); if (relinfo==NULL) throw PGraph2Exception("undefined edge type: "+e.TypeName ); } } //---------------------------------------------------------------------------- bool QueryGraph::IsConnected() { // prepare adjacency-lists (double sided!) vector grFw[Nodes.size()], grBw[Nodes.size()]; for (auto&& e : Edges) { grFw[e.FromNode->ID].push_back(e.ToNode->ID); grFw[e.ToNode->ID].push_back(e.FromNode->ID); grBw[e.ToNode->ID].push_back(e.FromNode->ID); grBw[e.FromNode->ID].push_back(e.ToNode->ID); } bool vis1[Nodes.size()], vis2[Nodes.size()]; for (uint ni = 0; ni < Nodes.size(); ni++) { // go forward memset(vis1, false, sizeof vis1); dfs( grFw, vis1, ni); // go backwards memset(vis2, false, sizeof vis2); dfs( grBw, vis2, ni); // unconnected? for (uint i = 0; i < Nodes.size(); i++) { if (!vis1[i] and !vis2[i]) return false; } } return true; } //---------------------------------------------------------------------------- QueryTree *QueryGraph::CreateOptimalQueryTree(string forcealias) { //TODO if (Nodes.size()==0) throw PGraph2Exception("no QueryGraph!"); QueryTree *bestSoFar=NULL; QueryTree *forced=NULL; double minCost=0; for (auto&& n : Nodes) { if (n.TypeName=="") continue; QueryTree *t = CreateQueryTree(&n); double cost=t->CalcCost(); LOGOP(10, "QueryGraph::CreateOptimalQueryTree", " COST FROM ",n.Alias, ":",n.TypeName," :", int(cost)); if (forcealias!="" && (forcealias==n.Alias)) forced=t; if (bestSoFar==NULL) { bestSoFar=t; minCost=cost; } else { if (costRoot ->Alias,":",forced->Root->TypeName); return forced; } LOGOP(10, "QueryGraph::CreateOptimalQueryTree", " taking ", bestSoFar->Root ->Alias,":",bestSoFar->Root->TypeName); return bestSoFar; } //---------------------------------------------------------------------------- void CreateQueryTree_rec(set *visited, QueryGraph *qg, QueryTree *tree, QueryTreeNode *parent, QueryGraphNode *n, bool reverse, QueryGraphEdge *qe) { if (n==NULL) return; visited->insert(n); QueryTreeNode *tn = new QueryTreeNode(); tn->Alias=n->Alias; tn->TypeName=n->TypeName; tn->Cost=n->Cost; // take filters int idx=0; for(auto&& f: n->Filters) { tn->Filters.push_back(f->Clone() ); if (f->Indexed) idx++; } // use cost = 1 for indexed properties if (n->Filters.size()==1 && idx==1) tn->Cost=1; if (tree->Root==NULL) { tree->Root=tn; } else { // add to parent QueryTreeEdge *e=new QueryTreeEdge(); e->FromNode=parent; e->ToNode=tn; e->Alias=qe->Alias; e->TypeName=qe->TypeName; e->Reverse=reverse; e->Cost=(!reverse)?qe->CostFw:qe->CostBw; for(auto&& f: qe->Filters) e->Filters.push_back(f->Clone() ); parent->Edges.push_back(e); } for(auto&& e : qg->Edges) { if (e.FromNode==n){ if (visited->find(e.ToNode)==visited->end()) CreateQueryTree_rec(visited, qg, tree, tn, e.ToNode, false, &e ); } if (e.ToNode==n) { if (visited->find(e.FromNode)==visited->end()) CreateQueryTree_rec(visited, qg, tree, tn, e.FromNode, true, &e ); } } } QueryTree *QueryGraph::CreateQueryTree(QueryGraphNode *n) { // set visited; QueryTree *tree=new QueryTree(); CreateQueryTree_rec(&visited, this, tree, NULL, n, true, NULL); return tree; } //---------------------------------------------------------------------------- void QueryGraph::readFilters(list &filters, ListExpr list) { while(!nl->IsEmpty(list)) { QueryFilter *f=new QueryFilter(); filters.push_back(f); f->Name = nl->ToString(nl->First(nl->First(list))); f->Value = nl->ToString(nl->Second(nl->First(list))); ReplaceStringInPlace(f->Value, "\"",""); list=nl->Rest(list); } } //---------------------------------------------------------------------------- void QueryGraph::ReadQueryGraph(string slist) { ListExpr alist=0; nl->ReadFromString(slist, alist); try { ReadQueryGraph(alist); } catch(...) { nl->Destroy(alist); throw; } nl->Destroy(alist); } //---------------------------------------------------------------------------- void checkOrcompleteType(QueryAliasList *aliaslist, string typename_, QueryGraphNode *n) { if (n->TypeName=="") { LOGOP(20,"QueryGraph::CompleteTypes","completed:"+n->Alias+":"+typename_); n->TypeName=typename_; aliaslist->Update(n->Alias, n->TypeName); } else { if (n->TypeName!=typename_) throw PGraph2Exception("Type conflict: "+n->TypeName+" != "+typename_); } } void QueryGraph::CompleteTypes() { // derive typenames from edge relations, if any for(auto&& e:Edges) { LOGOP(20,"checking",e.Alias+":"+e.TypeName); RelationInfo *ri = NULL; if (e.TypeName!="") ri=RelRegistry->GetRelationInfo(e.TypeName); for(auto&& n : Nodes) { if (e.FromNode==&n) if (ri!=NULL) checkOrcompleteType(&AliasList, ri->FromName, e.FromNode); if (e.ToNode==&n) if (ri!=NULL) checkOrcompleteType(&AliasList, ri->ToName, e.ToNode); } for(auto&& e : Edges) { if (e.TypeName=="") { // search if any edge relation is matching for(auto&& ri: RelRegistry->RelationInfos ) { if (ri->roleType==RoleEdge) { if ((e.FromNode->TypeName==ri->FromName) && (e.ToNode->TypeName==ri->ToName)) { e.TypeName=ri->Name; AliasList.Update(e.Alias, e.TypeName); } } } } } } } //---------------------------------------------------------------------------- void QueryGraph::ReadQueryGraph(ListExpr alist) { try { if (nl->ListLength(alist)==0) throw PGraph2Exception("At least a list with at least one " " node is required"); for(int pass=1; pass<=2;pass++) { ListExpr list=alist; while(!nl->IsEmpty(list)) { // split item ListExpr item=nl->First(list); list=nl->Rest(list); // detect propertylist ListExpr proplist; bool hasPropList=false; int count=nl->ListLength(item); proplist=nl->Nth( nl->ListLength(item), item); if (nl->ListLength( proplist)>0) { hasPropList=true; count--; } string s1="", s2="", s3="", s4=""; if (count>0) s1=nl->ToString(nl->First(item)); if (count>1) s2=nl->ToString(nl->Second(item)); if (count>2) s3=nl->ToString(nl->Third(item)); if (count>3) s4=nl->ToString(nl->Fourth(item)); if ( (count==1) || (count==2 && FirstUpper(s2)) ) { // node if (pass==1) addNode( s1, s2, hasPropList?&proplist:NULL); } else { // or edge if (pass==2) { if (count==2) { addEdge( "", "", s1, s2 , hasPropList?&proplist:NULL); } if (count==3) { addEdge( "", s2, s1, s3 , hasPropList?&proplist:NULL); } if (count==4) { addEdge( s2, s3, s1, s4 , hasPropList?&proplist:NULL); } } } } } } catch(PGraph2Exception &e) { throw; } } } // namespace