com.redhat.lightblue.assoc.ResultDoc.java Source code

Java tutorial

Introduction

Here is the source code for com.redhat.lightblue.assoc.ResultDoc.java

Source

/*
 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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.Collection;

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

import com.redhat.lightblue.metadata.DocId;
import com.redhat.lightblue.metadata.CompositeMetadata;
import com.redhat.lightblue.metadata.ResolvedReferenceField;

import com.redhat.lightblue.util.JsonDoc;
import com.redhat.lightblue.util.KeyValueCursor;
import com.redhat.lightblue.util.Path;
import com.redhat.lightblue.util.MutablePath;

/**
 * A result document of a particular entity type. This contains the
 * document, the unique id, the query plan node corresponding to this
 * document, and the references to other documents. During
 * construction, all reference instances are collected, and a
 * DocReference list si constructed using those references. Subsequent
 * stages of execution attach new documents to those references.
 */
public class ResultDoc implements Serializable {

    private static final long serialVersionUID = 1l;

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

    private final JsonDoc doc;
    private final DocId id;
    private final QueryPlanNode node;

    private final Map<QueryPlanNode, List<ChildDocReference>> children = new HashMap<>();
    private final Map<QueryPlanNode, ChildDocReference> parents = new HashMap<>();

    public ResultDoc(JsonDoc doc, DocId id, QueryPlanNode node) {
        this.node = node;
        this.doc = doc;
        this.id = id;
        gatherChildren();
    }

    public DocId getId() {
        return id;
    }

    public JsonDoc getDoc() {
        return doc;
    }

    /**
     * Returns the query plan node for this document
     */
    public QueryPlanNode getQueryPlanNode() {
        return node;
    }

    /**
     * Returns the metadata for this document
     */
    public CompositeMetadata getMetadata() {
        return node.getMetadata();
    }

    public List<ChildDocReference> getChildren(QueryPlanNode dest) {
        return children.get(dest);
    }

    public Map<QueryPlanNode, List<ChildDocReference>> getChildren() {
        return children;
    }

    /**
     * Returns all child references to a given node for all the docs in the list
     */
    public static List<ChildDocReference> getChildren(List<ResultDoc> docs, QueryPlanNode dest) {
        List<ChildDocReference> ret = new ArrayList<>();
        for (ResultDoc x : docs) {
            List<ChildDocReference> l = x.getChildren(dest);
            if (l != null)
                ret.addAll(l);
        }
        return ret;
    }

    public Map<QueryPlanNode, ChildDocReference> getParentDocs() {
        return parents;
    }

    /**
     * Sets the parent document reference of this document that is coming from parentNode
     */
    public void setParentDoc(QueryPlanNode parentNode, ChildDocReference ref) {
        parents.put(parentNode, ref);
    }

    /**
     * Iterates through all destination nodes in query plan, finds all
     * references corresponding to those destination nodes, and
     * initializes a reference for each
     */
    private void gatherChildren() {
        QueryPlanNode[] destinations = node.getDestinations();
        CompositeMetadata md = node.getMetadata();
        if (destinations.length > 0) {
            for (QueryPlanNode destNode : destinations) {
                CompositeMetadata destMd = destNode.getMetadata();
                List<ChildDocReference> refList = new ArrayList<>();
                // Find the entity reference for destMd. It can be a child of this entity, or the parent
                if (destMd == node.getMetadata().getParent()) {
                    // This happens when entity graph is A->B, but retrieval graph is B->A
                    // This works only if A -> B link is not an array
                    if (md.getEntityPath().nAnys() > 0)
                        throw new IllegalArgumentException("Unsupported association");

                    refList.add(new ChildDocReference(this, Path.EMPTY));
                    children.put(destNode, refList);
                } else {
                    // Get the resolved reference for this field
                    ResolvedReferenceField ref = md.getChildReference(destMd.getEntityPath());
                    // We cut the last two segments, they are "ref.*"
                    Path field = md.getEntityRelativeFieldName(ref).prefix(-2);
                    LOGGER.debug("Getting instances of {}", field);
                    Path p = destMd.getEntityPath().prefix(-2);
                    // p points to the object containing the reference field
                    // It could be empty, if the reference is at the root level
                    if (p.isEmpty()) {
                        refList.add(new ChildDocReference(this, new Path(p, new Path(ref.getName()))));
                    } else {
                        KeyValueCursor<Path, JsonNode> cursor = doc.getAllNodes(p);
                        while (cursor.hasNext()) {
                            cursor.next();
                            JsonNode nodeWithRef = cursor.getCurrentValue();
                            if (nodeWithRef instanceof ArrayNode) {
                                int size = ((ArrayNode) nodeWithRef).size();
                                MutablePath elem = cursor.getCurrentKey().mutableCopy();
                                int ix = elem.numSegments();
                                elem.push(0);
                                elem.push(ref.getName());
                                for (int i = 0; i < size; i++) {
                                    elem.set(ix, i);
                                    refList.add(new ChildDocReference(this, elem.immutableCopy()));
                                }
                            } else {
                                refList.add(new ChildDocReference(this,
                                        new Path(cursor.getCurrentKey(), new Path(ref.getName()))));
                            }
                        }
                    }
                    children.put(destNode, refList);
                }
            }
        }
        LOGGER.debug("children for {}:{}", id, children);
    }

    public String toString() {
        return "Doc:" + id + " children:" + children;
    }
}