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

com.redhat.lightblue.eval.FieldComparisonEvaluator 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.eval;

import java.util.List;
import java.util.Iterator;
import java.util.ArrayList;

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.crud.CrudConstants;
import com.redhat.lightblue.metadata.FieldTreeNode;
import com.redhat.lightblue.metadata.SimpleField;
import com.redhat.lightblue.metadata.ArrayField;
import com.redhat.lightblue.metadata.SimpleArrayElement;
import com.redhat.lightblue.metadata.types.ArrayType;
import com.redhat.lightblue.metadata.Type;
import com.redhat.lightblue.query.BinaryComparisonOperator;
import com.redhat.lightblue.query.FieldComparisonExpression;
import com.redhat.lightblue.util.Path;
import com.redhat.lightblue.util.KeyValueCursor;

/**
 * Evaluates lfield op rfield type comparisons. Both fields can be
 * simple fields or arrays. Here's the semantics of the operation:
 *
 * 
    *
  • lfield op rfield : true if lfield op rfield
  • *
  • lfield op rarray: true if lfield op rarray[i] for all elements i
  • *
  • larray op rarray: true if larray[i] op rarray[i] for all elements i. for op other than $ne, array sizes must be equal
  • *
* * */ public class FieldComparisonEvaluator extends QueryEvaluator { private static final Logger LOGGER = LoggerFactory.getLogger(FieldComparisonEvaluator.class); private final FieldTreeNode fieldMd; private final FieldTreeNode rfieldMd; private final Path relativePath; private final Path rfieldRelativePath; private final BinaryComparisonOperator operator; /** * Constructs evaluator for {field op field} style comparison * * @param expr The expression * @param md Entity metadata * @param context The path relative to which the expression will be * evaluated */ public FieldComparisonEvaluator(FieldComparisonExpression expr, FieldTreeNode context) { this.relativePath = expr.getField(); this.rfieldRelativePath = expr.getRfield(); fieldMd = context.resolve(relativePath); if (fieldMd == null) { throw new EvaluationError(expr, CrudConstants.ERR_FIELD_NOT_THERE + " "+relativePath); } rfieldMd = context.resolve(rfieldRelativePath); if (rfieldMd == null) { throw new EvaluationError(expr, CrudConstants.ERR_FIELD_NOT_THERE + " "+rfieldRelativePath); } // Both fields must be simple fields or simple arrays if(!(fieldMd instanceof SimpleField || (fieldMd instanceof ArrayField && ((ArrayField)fieldMd).getElement() instanceof SimpleArrayElement) ) ) { throw new EvaluationError(expr, CrudConstants.ERR_EXPECTED_SIMPLE_FIELD_OR_SIMPLE_ARRAY + " "+relativePath); } if(!(rfieldMd instanceof SimpleField || (rfieldMd instanceof ArrayField && ((ArrayField)rfieldMd).getElement() instanceof SimpleArrayElement) ) ) { throw new EvaluationError(expr, CrudConstants.ERR_EXPECTED_SIMPLE_FIELD_OR_SIMPLE_ARRAY + " "+rfieldRelativePath); } operator = expr.getOp(); LOGGER.debug("ctor {} {} {}", relativePath, operator, rfieldRelativePath); } @Override public boolean evaluate(QueryEvaluationContext ctx) { LOGGER.debug("evaluate {} {} {}", relativePath, operator, rfieldRelativePath); KeyValueCursor lcursor = ctx.getNodes(relativePath); ctx.setResult(false); if (lcursor != null) { while (lcursor.hasNext()&&!ctx.getResult()) { lcursor.next(); JsonNode lvalueNode = lcursor.getCurrentValue(); Object ldocValue=null; List ldocList=null; if (lvalueNode != null) { if(fieldMd.getType() instanceof ArrayType) { ldocList=makeList(((ArrayField)fieldMd).getElement().getType(), lvalueNode); } else { ldocValue = fieldMd.getType().fromJson(lvalueNode); } } KeyValueCursor rcursor = ctx.getNodes(rfieldRelativePath); if (rcursor != null) { while (rcursor.hasNext()&&!ctx.getResult()) { rcursor.next(); JsonNode rvalueNode = rcursor.getCurrentValue(); Object rdocValue=null; List rdocList=null; if (rvalueNode != null) { if(rfieldMd.getType() instanceof ArrayType) { rdocList=makeList(((ArrayField)rfieldMd).getElement().getType(),rvalueNode); } else { rdocValue = rfieldMd.getType().fromJson(rvalueNode); } } LOGGER.debug(" lvalue={} rvalue={}", lvalueNode, rvalueNode); if(ldocValue!=null&&rdocValue!=null) { // Both fields are values int result=fieldMd.getType().compare(ldocValue, rdocValue); LOGGER.debug(" result={}", result); if(operator.apply(result)) { ctx.setResult(true); } } else if(ldocList!=null&&rdocList!=null) { // Both fields are arrays. Compare each element Type type=((ArrayField)fieldMd).getElement().getType(); int ln=ldocList.size(); int rn=rdocList.size(); int cmp=0; if(ln==rn) { for(int i=0;i makeList(Type t,JsonNode node) { if(node instanceof ArrayNode) { List list=new ArrayList<>(node.size()); for(Iterator itr=((ArrayNode)node).elements();itr.hasNext();) { list.add(t.fromJson(itr.next())); } return list; } return null; } /** * The result is the result obtained from lvCompare. The op is * the operator for the evaluator. Returns if the result satisties the operator. */ private static boolean cmpOp(BinaryComparisonOperator result,BinaryComparisonOperator op) { if(result!=op) { switch(op) { case _neq: if(result==BinaryComparisonOperator._eq) { return false; } break; case _gte: if(result!=BinaryComparisonOperator._gte&& result!=BinaryComparisonOperator._gt&& result!=BinaryComparisonOperator._eq) { return false; } break; case _lte: if(result!=BinaryComparisonOperator._lte&& result!=BinaryComparisonOperator._lt&& result!=BinaryComparisonOperator._eq) { return false; } break; default: return false; } } return true; } /** * Comparison lookup table. A 3-bit value is the index into this table Bit0 means * there are greater values. Bit1 means there are equal values. Bit2 means * there are less values. */ private static final BinaryComparisonOperator CMP_LOOKUP[]= { BinaryComparisonOperator._neq, // 000 BinaryComparisonOperator._gt, // 001 BinaryComparisonOperator._eq, // 010 BinaryComparisonOperator._gte, // 011 BinaryComparisonOperator._lt, // 100 BinaryComparisonOperator._neq, // 101 BinaryComparisonOperator._lte, // 110 BinaryComparisonOperator._neq // 111 }; private static int apply(int cmp,int result) { if(result==0) { return cmp|0x02; } else if(result<0) { return cmp|0x04; } else { return cmp|0x01; } } /** * Compare a value to a list. Returns: *
    *
  • _eq: if all values in list are equal to value
  • *
  • _lt: if value is less than all values in the list
  • *
  • _lte: if value is less than or equal to all the values in the list
  • *
  • _gt: if value is greater than all values in the list
  • *
  • _gte: if value is greater or equal to all the values in the list
  • *
  • _neq: otherwise
  • *
      */ private static BinaryComparisonOperator lvCompare(Object value,List list,Type t) { int cmp=0; if(value!=null) { if(list!=null&&!list.isEmpty()) { for(Object x:list) { cmp=apply(cmp,t.compare(value,x)); } } } return CMP_LOOKUP[cmp]; } }