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

com.redhat.lightblue.assoc.ResolvedFieldBinding 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.assoc;

import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;

import java.io.Serializable;

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

import com.fasterxml.jackson.databind.JsonNode;

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

import com.redhat.lightblue.metadata.Type;
import com.redhat.lightblue.metadata.CompositeMetadata;
import com.redhat.lightblue.metadata.FieldTreeNode;

import com.redhat.lightblue.util.Path;

/**
 * This class represents a field binding interpreted based on the
 * metadata. It contains the original field binding information, the
 * field type information, and the field name relative to an entity in
 * a query plan that denotes the field name containing the value that
 * is bound in the field binding.
 *
 * For example:
 * 
 *    query={'field':'field1','op':'=','rfield':'$parent.referencedField'}
 * 
* * Assume referencedField is bound to a value in a * different entity. Then, binding contains the binding * information for referencedField, type * contains the type of referencedField, and valueField * contains referencedField, the field relative to the * entity it is contained in. */ public class ResolvedFieldBinding implements Serializable { private static final long serialVersionUID=1l; private static final Logger LOGGER=LoggerFactory.getLogger(ResolvedFieldBinding.class); private final FieldBinding binding; private final Type type; private final Path valueField; private final CompositeMetadata entity; /** * This class is used to return binding results. It contains the * bindings, and the query rewritten relative to the node the * query is bound, */ public static final class BindResult { private final List bindings; private final QueryExpression runExpression; private BindResult(List bindings, QueryExpression runExpression) { this.bindings=bindings; this.runExpression=runExpression; } public List getBindings() { return bindings; } public QueryExpression getRelativeQuery() { return runExpression; } } /** * Construct a resolved field binding using the given binding and the root composite metadata */ public ResolvedFieldBinding(FieldBinding b, CompositeMetadata root) { this.binding=b; FieldTreeNode field=root.resolve(b.getField()); if(field==null) throw new IllegalArgumentException("Cannot resolve "+b.getField()); this.type=field.getType(); this.entity=root.getEntityOfField(field); this.valueField=root.getEntityRelativeFieldName(field); } /** * Binds a list of clauses for execution at a particular query plan node * * @param clauses The query conjuncts that will be bound * @param atNode The query plan node at which the query will be run * * @return Returns the binding result containing the bindings, and * the query expression rewritten to be run at the given query * plan node. Returns null if there are no bindings or query. */ public static BindResult bind(List clauses, QueryPlanNode atNode, CompositeMetadata root) { BindResult ret = null; if(clauses!=null&&!clauses.isEmpty()) { QueryExpression query; if(clauses.size()==1) { query=clauses.get(0).getClause(); } else { List cl=new ArrayList<>(clauses.size()); for(Conjunct c:clauses) cl.add(c.getClause()); query=new NaryLogicalExpression(NaryLogicalOperator._and,cl); } LOGGER.debug("Resolving bindings for {}",query); List bindable=query.getBindableClauses(); LOGGER.debug("Bindable clauses:{}",bindable); // default the bound query to input query QueryExpression boundQuery = query; List bindings = null; if(!bindable.isEmpty()) { LOGGER.debug("Building bind request"); Set bindRequest=new HashSet<>(); for(QueryInContext qic:bindable) { // TODO do you really know the class for the query here? FieldComparisonExpression fce=(FieldComparisonExpression)qic.getQuery(); // Find the field that refers to a node before // the destination Path lfield=new Path(qic.getContext(),fce.getField()); QueryPlanNode lfieldNode=null; for(Conjunct c:clauses) { lfieldNode=c.getFieldNode(lfield); if(lfieldNode!=null) break; } Path rfield=new Path(qic.getContext(),fce.getRfield()); QueryPlanNode rfieldNode=null; for(Conjunct c:clauses) { rfieldNode=c.getFieldNode(rfield); if(rfieldNode!=null) break; } LOGGER.debug("lfield={}, rfield={}",lfieldNode==null?null:lfieldNode.getName(), rfieldNode==null?null:rfieldNode.getName()); // If lfieldNode points to the destination node, // rfieldNode points to an ancestor, or vice versa if(lfieldNode!=null&&lfieldNode.getName().equals(atNode.getName())) { bindRequest.add(rfield); } else { bindRequest.add(lfield); } } LOGGER.debug("Bind fields:{}",bindRequest); List fb=new ArrayList<>(); // get the bound query boundQuery=query.bind(fb,bindRequest); // collect bindings bindings=new ArrayList<>(); for(FieldBinding b:fb) { bindings.add(new ResolvedFieldBinding(b,root)); } } // note if there was nothing bindable: // * field 'boundQuery' equals 'query' // * field 'bindings' equals null LOGGER.debug("Bound query:{}",boundQuery); // If destination node is not the root node, we need to rewrite the query relative to that node // Otherwise, absolute query will work for the root node QueryExpression runExpression; if(atNode.getMetadata().getParent()==null) { runExpression=boundQuery; } else { runExpression=new RelativeRewriteIterator(new Path(atNode.getMetadata().getEntityPath(), Path.ANYPATH)).iterate(boundQuery); } LOGGER.debug("Run expression:{}",runExpression); ret=new BindResult(bindings,runExpression); } return ret; } public static void refresh(List bindings,QueryPlanDoc doc) { for(ResolvedFieldBinding binding:bindings) { binding.refresh(doc); } } /** * Attempts to refresh this binding starting at the given document, and ascending to its parents */ public boolean refresh(QueryPlanDoc doc) { if(doc.getMetadata()==entity) { JsonNode valueNode=doc.getDoc().get(valueField); if(valueNode==null) binding.getValue().setValue(null); else binding.getValue().setValue(type.fromJson(valueNode)); return true; } else { for(QueryPlanDoc parent: doc.getParents()) { if(refresh(parent)) return true; } return false; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy