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

com.redhat.lightblue.mediator.CompositeFindImpl Maven / Gradle / Ivy

The 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.mediator;

import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.HashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

import com.redhat.lightblue.OperationStatus;

import com.redhat.lightblue.query.QueryExpression;
import com.redhat.lightblue.query.FieldBinding;
import com.redhat.lightblue.query.NaryLogicalExpression;
import com.redhat.lightblue.query.NaryLogicalOperator;
import com.redhat.lightblue.query.QueryInContext;
import com.redhat.lightblue.query.FieldComparisonExpression;
import com.redhat.lightblue.query.FieldProjection;
import com.redhat.lightblue.query.RelativeRewriteIterator;

import com.redhat.lightblue.crud.CRUDFindRequest;
import com.redhat.lightblue.crud.CRUDFindResponse;
import com.redhat.lightblue.crud.Factory;
import com.redhat.lightblue.crud.DocCtx;

import com.redhat.lightblue.assoc.QueryPlan;
import com.redhat.lightblue.assoc.QueryPlanNode;
import com.redhat.lightblue.assoc.Conjunct;
import com.redhat.lightblue.assoc.QueryPlanData;
import com.redhat.lightblue.assoc.QueryPlanChooser;
import com.redhat.lightblue.assoc.QueryPlanDoc;

import com.redhat.lightblue.assoc.scorers.SimpleScorer;
import com.redhat.lightblue.assoc.iterators.First;

import com.redhat.lightblue.metadata.CompositeMetadata;
import com.redhat.lightblue.metadata.DocId;
import com.redhat.lightblue.metadata.DocIdExtractor;
import com.redhat.lightblue.metadata.PredefinedFields;
import com.redhat.lightblue.metadata.Type;

import com.redhat.lightblue.util.Path;
import com.redhat.lightblue.util.Error;
import com.redhat.lightblue.util.Tuples;
import com.redhat.lightblue.util.JsonDoc;

public class CompositeFindImpl implements Finder {

    private static final Logger LOGGER=LoggerFactory.getLogger(CompositeFindImpl.class);

    private final CompositeMetadata root;
    private final QueryPlan qplan;
    private final Factory factory;

    private final Map documentCache=new HashMap<>();
    
    private final List errors=new ArrayList<>();

    public CompositeFindImpl(CompositeMetadata md,
                             QueryPlan qplan,
                             Factory factory) {
        this.root=md;
        this.qplan=qplan;
        this.factory=factory;

        init(qplan,factory);
    }


    private void init(QueryPlan q,
                      Factory factory) {
        // We need two separate for loops below. First associates an
        // QueryPlanNodeExecutor to each query plan node. Second adds
        // the edges between those execution data. Setting up the
        // edges requires all execution information readily available.
        
        //  Setup execution data for each node
        for(QueryPlanNode x:q.getAllNodes())
            x.setProperty(QueryPlanNodeExecutor.class,new QueryPlanNodeExecutor(x,factory,root,documentCache));
        
        // setup edges between execution data
        for(QueryPlanNode x:q.getAllNodes()) {
            x.getProperty(QueryPlanNodeExecutor.class).init(q);
        }
    }

    /**
     * The operation starts by evaluating source nodes, and
     * moves on by going to the destination nodes.
     */
    @Override
    public CRUDFindResponse find(OperationContext ctx,
                                 CRUDFindRequest req) {
        LOGGER.debug("Composite find: start");
        // At this stage, we have Execution objects assigned to query plan nodes

        // Put the executions in order
        QueryPlanNode[] nodeOrdering=qplan.getBreadthFirstNodeOrdering();
        // Execute nodes. Only the nodes up to and including the root
        // node needs to be executed. The query choose makes sure that
        // any query plan descendands of the root node don't have any
        // queries associated with them, so they will be retrieved
        // only using edge conditions
        QueryPlanNode rootNode=null;
        for(QueryPlanNode node:nodeOrdering) {
            LOGGER.debug("Composite find: {}",node.getName());
            QueryPlanNodeExecutor exec=node.getProperty(QueryPlanNodeExecutor.class);
            if(node.getMetadata().getParent()==null) {
                if (req.getTo() != null && req.getFrom() != null) {
                    exec.setRange(req.getFrom(), req.getTo());
                }
                exec.execute(ctx,req.getSort());
                // Reached the root node. Terminate execution, and build documents
                rootNode=node;
                break;
            } else {
                exec.execute(ctx,null);
            }
        }

        LOGGER.debug("Composite find: retrieval of result set is complete, now building documents");

        if (rootNode == null) {
            // not likely, but just in case
            throw new IllegalStateException("No root QueryPlanNode selected to execute query against!");
        }
        
        CRUDFindResponse response=new CRUDFindResponse();
        List resultDocuments=retrieveDocuments(ctx,rootNode.getProperty(QueryPlanNodeExecutor.class));
        response.setSize(resultDocuments.size());
        ctx.setDocuments(resultDocuments);
        ctx.addErrors(errors);
        
        LOGGER.debug("Composite find: end");
        return response;
    }
    
    private List retrieveDocuments(OperationContext ctx,
                                           QueryPlanNodeExecutor rootNode) {
        LOGGER.debug("Retrieving {} documents",rootNode.getDocs().size());

        // Create a new query plan for retrieval. This one will have
        // the root document at the root.
        QueryPlanChooser chooser=new QueryPlanChooser(root,new First(),new SimpleScorer(),null);
        QueryPlan retrievalQPlan=chooser.choose();
        // This query plan has only one source
        QueryPlanNode retrievalPlanRoot=retrievalQPlan.getSources()[0];
        new CompositeFindImpl(root,retrievalQPlan,factory);
        // The root node documents are already known
        retrievalPlanRoot.getProperty(QueryPlanNodeExecutor.class).setDocs(rootNode.getDocs());

        // Now execute rest of the retrieval plan
        QueryPlanNode[] nodeOrdering=retrievalQPlan.getBreadthFirstNodeOrdering();
        
        for(int i=0;i ret=new ArrayList<>(rootNode.getDocs().size());
        for(QueryPlanDoc dgd:rootNode.getDocs()) {
            retrieveFragments(dgd,rootNode);
            PredefinedFields.updateArraySizes(factory.getNodeFactory(),dgd.getDoc());
            DocCtx dctx=new DocCtx(dgd.getDoc());
            dctx.setOutputDocument(dgd.getDoc());
            ret.add(dctx);
        }
        
        return ret;
    }

    private void retrieveFragments(QueryPlanDoc doc,
                                   QueryPlanNodeExecutor exec) {
        // We only process child nodes.
        QueryPlanNode[] destinations=exec.getNode().getDestinations();
        for(QueryPlanNode childNode:destinations) {
            QueryPlanNodeExecutor childExecutor=childNode.getProperty(QueryPlanNodeExecutor.class);
            CompositeMetadata childMd=childNode.getMetadata();
            Path insertInto=childMd.getEntityPath();
            JsonNode insertionNode=doc.getDoc().get(insertInto);
            if(insertionNode==null)
                doc.getDoc().modify(insertInto,insertionNode=factory.getNodeFactory().arrayNode(),true);
            List children=doc.getChildren(childNode);
            if(children!=null) {
                for(QueryPlanDoc childDoc:children) {
                    ((ArrayNode)insertionNode).add(childDoc.getDoc().getRoot());
                    retrieveFragments(doc,childExecutor);
                }
            }
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy