You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@accumulo.apache.org by bi...@apache.org on 2011/12/05 21:05:51 UTC
svn commit: r1210600 [14/16] - in
/incubator/accumulo/trunk/contrib/accumulo_sample: ./ ingest/
ingest/src/main/java/aggregator/ ingest/src/main/java/ingest/
ingest/src/main/java/iterator/ ingest/src/main/java/normalizer/
ingest/src/main/java/protobuf/...
Modified: incubator/accumulo/trunk/contrib/accumulo_sample/query/src/main/java/parser/RangeCalculator.java
URL: http://svn.apache.org/viewvc/incubator/accumulo/trunk/contrib/accumulo_sample/query/src/main/java/parser/RangeCalculator.java?rev=1210600&r1=1210599&r2=1210600&view=diff
==============================================================================
--- incubator/accumulo/trunk/contrib/accumulo_sample/query/src/main/java/parser/RangeCalculator.java (original)
+++ incubator/accumulo/trunk/contrib/accumulo_sample/query/src/main/java/parser/RangeCalculator.java Mon Dec 5 20:05:49 2011
@@ -1,19 +1,19 @@
/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements. See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License. You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package parser;
import iterator.EvaluatingIterator;
@@ -62,1138 +62,1151 @@ import com.google.common.collect.HashMul
import com.google.common.collect.Multimap;
import com.google.protobuf.InvalidProtocolBufferException;
-
/**
- * This class is used to query the global indices to determine that set of ranges to use
- * when querying the shard table. The RangeCalculator looks at each term in the query to determine
- * if it is a equivalence, range, or wildcard comparison, and queries the appropriate index to find
- * the ranges for the terms which are then cached. The final set of ranges is computed as the AST is
- * traversed.
+ * This class is used to query the global indices to determine that set of ranges to use when querying the shard table. The RangeCalculator looks at each term
+ * in the query to determine if it is a equivalence, range, or wildcard comparison, and queries the appropriate index to find the ranges for the terms which are
+ * then cached. The final set of ranges is computed as the AST is traversed.
*/
public class RangeCalculator extends QueryParser {
-
- /**
- * Container used as map keys in this class
- *
- */
- public static class MapKey implements Comparable<MapKey>{
- private String fieldName = null;
- private String fieldValue = null;
- private String originalQueryValue = null;
-
- public MapKey(String fieldName, String fieldValue) {
- super();
- this.fieldName = fieldName;
- this.fieldValue = fieldValue;
- }
-
- public String getFieldName() {
- return fieldName;
- }
-
- public String getFieldValue() {
- return fieldValue;
- }
-
- public void setFieldName(String fieldName) {
- this.fieldName = fieldName;
- }
-
- public void setFieldValue(String fieldValue) {
- this.fieldValue = fieldValue;
- }
-
- public String getOriginalQueryValue() {
- return originalQueryValue;
- }
-
- public void setOriginalQueryValue(String originalQueryValue) {
- this.originalQueryValue = originalQueryValue;
- }
-
- @Override
- public int hashCode() {
- return new HashCodeBuilder(17,37).append(fieldName).append(fieldValue).toHashCode();
- }
-
- @Override
- public String toString() {
- return this.fieldName + " " + this.fieldValue;
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == null)
- return false;
- if (other instanceof MapKey) {
- MapKey o = (MapKey) other;
- return (this.fieldName.equals(o.fieldName) && this.fieldValue.equals(o.fieldValue));
- } else
- return false;
- }
-
- public int compareTo(MapKey o) {
- int result = this.fieldName.compareTo(o.fieldName);
- if (result != 0) {
- return this.fieldValue.compareTo(o.fieldValue);
- } else {
- return result;
- }
- }
-
- }
-
- /**
- * Container used to hold the lower and upper bound of a range
- *
- */
- public static class RangeBounds {
- private String originalLower = null;
- private Text lower = null;
- private String originalUpper = null;
- private Text upper = null;
- public Text getLower() {
- return lower;
- }
- public Text getUpper() {
- return upper;
- }
- public void setLower(Text lower) {
- this.lower = lower;
- }
- public void setUpper(Text upper) {
- this.upper = upper;
- }
- public String getOriginalLower() {
- return originalLower;
- }
- public String getOriginalUpper() {
- return originalUpper;
- }
- public void setOriginalLower(String originalLower) {
- this.originalLower = originalLower;
- }
- public void setOriginalUpper(String originalUpper) {
- this.originalUpper = originalUpper;
- }
- }
-
- /**
-
- * Object that is used to hold ranges found in the index. Subclasses may compute
- * the final range set in various ways.
- */
- protected static class TermRange implements Comparable<TermRange> {
-
- private String fieldName = null;
- private Object fieldValue = null;
- private Set<Range> ranges = new TreeSet<Range>();
-
- public TermRange(String name, Object fieldValue) {
- this.fieldName = name;
- this.fieldValue = fieldValue;
- }
-
- public String getFieldName() {
- return this.fieldName;
- }
- public Object getFieldValue() {
- return this.fieldValue;
- }
- public void addAll(Set<Range> r) {
- ranges.addAll(r);
- }
- public void add(Range r) {
- ranges.add(r);
- }
- public Set<Range> getRanges() {
- return ranges;
- }
-
- @Override
- public String toString() {
- ToStringBuilder tsb = new ToStringBuilder(this);
- tsb.append("fieldName", fieldName);
- tsb.append("fieldValue", fieldValue);
- tsb.append("ranges", ranges);
- return tsb.toString();
- }
-
- public int compareTo(TermRange o) {
- int result = this.fieldName.compareTo(o.fieldName);
- if (result == 0) {
- return ((Integer) ranges.size()).compareTo(o.ranges.size());
- } else {
- return result;
- }
- }
- }
-
- /**
- * Object used to store context information as the AST is being traversed.
- */
- static class EvaluationContext {
- boolean inOrContext = false;
- boolean inNotContext = false;
- boolean inAndContext = false;
- TermRange lastRange = null;
- String lastProcessedTerm = null;
- }
-
- protected static Logger log = Logger.getLogger(RangeCalculator.class);
- private static String WILDCARD = ".*";
- private static String SINGLE_WILDCARD = "\\.";
- protected static String START_ROW = "0";
-
- protected Connector c;
- protected Authorizations auths;
- protected Multimap<String, Normalizer> indexedTerms;
- protected Multimap<String, QueryTerm> termsCopy = HashMultimap.create();
- protected String indexTableName;
- protected String reverseIndexTableName;
- protected int queryThreads = 8;
- protected String END_ROW = null;
-
- /* final results of index lookups, ranges for the shard table */
- protected Set<Range> result = null;
- /* map of field names to values found in the index */
- protected Multimap<String,String> indexEntries = HashMultimap.create();
- /* map of value in the index to the original query values */
- protected Map<String,String> indexValues = new HashMap<String,String>();
- /* map of values in the query to map keys used */
- protected Multimap<String,MapKey> originalQueryValues = HashMultimap.create();
- /* map of field name to cardinality */
- protected Map<String, Long> termCardinalities = new HashMap<String, Long>();
- /* cached results of all ranges found global index lookups */
- protected Map<MapKey, TermRange> globalIndexResults = new HashMap<MapKey, TermRange>();
- /**
- *
- * @param c
- * @param auths
- * @param indexedTerms
- * @param terms
- * @param begin
- * @param end
- * @param dateFormatter
- * @param query
- * @param logic
- * @param typeFilter
- * @throws ParseException
- */
- public void execute (Connector c, Authorizations auths, Multimap<String, Normalizer> indexedTerms,
- Multimap<String, QueryTerm> terms, String query, AbstractQueryLogic logic, Set<String> typeFilter) throws ParseException {
- super.execute(query);
- this.c = c;
- this.auths = auths;
- this.indexedTerms = indexedTerms;
- this.termsCopy.putAll(terms);
- this.indexTableName = logic.getIndexTableName();
- this.reverseIndexTableName = logic.getReverseIndexTableName();
- this.queryThreads = logic.getQueryThreads();
- this.END_ROW = Integer.toString(logic.getNumPartitions());
-
- Map<MapKey, Set<Range>> indexRanges = new HashMap<MapKey, Set<Range>>();
- Map<MapKey, Set<Range>> trailingWildcardRanges = new HashMap<MapKey, Set<Range>>();
- Map<MapKey, Set<Range>> leadingWildcardRanges = new HashMap<MapKey, Set<Range>>();
- Map<Text, RangeBounds> rangeMap = new HashMap<Text, RangeBounds>();
-
- //Here we iterate over all of the terms in the query to determine if they are an equivalence,
- //wildcard, or range type operator
- for (Entry<String, QueryTerm> entry : terms.entries()) {
- if (entry.getValue().getOperator().equals(JexlOperatorConstants.getOperator(ASTEQNode.class)) ||
- entry.getValue().getOperator().equals(JexlOperatorConstants.getOperator(ASTERNode.class)) ||
- entry.getValue().getOperator().equals(JexlOperatorConstants.getOperator(ASTLTNode.class)) ||
- entry.getValue().getOperator().equals(JexlOperatorConstants.getOperator(ASTLENode.class)) ||
- entry.getValue().getOperator().equals(JexlOperatorConstants.getOperator(ASTGTNode.class)) ||
- entry.getValue().getOperator().equals(JexlOperatorConstants.getOperator(ASTGENode.class))) {
- //If this term is not in the set of indexed terms, then bail
- if (!indexedTerms.containsKey(entry.getKey())) {
- termCardinalities.put(entry.getKey().toUpperCase(), 0L);
- continue;
- }
- //In the case of function calls, the query term could be null. Dont query the index for it.
- if (null == entry.getValue()) {
- termCardinalities.put(entry.getKey().toUpperCase(), 0L);
- continue;
- }
- //In the case where we are looking for 'null', then skip.
- if (null == entry.getValue().getValue() || ((String) entry.getValue().getValue()).equals("null")) {
- termCardinalities.put(entry.getKey().toUpperCase(), 0L);
- continue;
- }
-
- //Remove the begin and end ' marks
- String value = null;
- if (((String) entry.getValue().getValue()).startsWith("'") && ((String) entry.getValue().getValue()).endsWith("'"))
- value = ((String) entry.getValue().getValue()).substring(1, ((String) entry.getValue().getValue()).length() - 1);
- else
- value = (String) entry.getValue().getValue();
- //The entries in the index are normalized
- for (Normalizer normalizer : indexedTerms.get(entry.getKey())) {
- String normalizedFieldValue = normalizer.normalizeFieldValue(null, value);
- Text fieldValue = new Text(normalizedFieldValue);
- Text fieldName = new Text(entry.getKey().toUpperCase());
-
- //EQUALS
- if (entry.getValue().getOperator().equals(JexlOperatorConstants.getOperator(ASTEQNode.class))) {
- Key startRange = new Key(fieldValue, fieldName, new Text(START_ROW));
- Key endRange = new Key(fieldValue, fieldName, new Text(END_ROW));
- Range r = new Range(startRange, true, endRange, false);
-
- MapKey key = new MapKey(fieldName.toString(), fieldValue.toString());
- key.setOriginalQueryValue(value);
- this.originalQueryValues.put(value, key);
- if (!indexRanges.containsKey(key))
- indexRanges.put(key, new HashSet<Range>());
- indexRanges.get(key).add(r);
- //WILDCARD
- } else if (entry.getValue().getOperator().equals(JexlOperatorConstants.getOperator(ASTERNode.class))) {
- //This is a wildcard query using regex. We can only support leading and trailing wildcards at this time. Leading
- //wildcards will need be reversed and sent to the global reverse index. Trailing wildcard queries will be sent to the
- //global index. In all cases, the range for the wilcard will be the range of possible UNICODE codepoints, hex 0 to 10FFFF.
- int loc = normalizedFieldValue.indexOf(WILDCARD);
- if (-1 == loc)
- loc = normalizedFieldValue.indexOf(SINGLE_WILDCARD);
- if (-1 == loc) {
- //Then no wildcard in the query? Treat like the equals case above.
- Key startRange = new Key(fieldValue, fieldName, new Text(START_ROW));
- Key endRange = new Key(fieldValue, fieldName, new Text(END_ROW));
- Range r = new Range(startRange, true, endRange, false);
-
- MapKey key = new MapKey(fieldName.toString(), fieldValue.toString());
- key.setOriginalQueryValue(value);
- this.originalQueryValues.put(value, key);
- if (!indexRanges.containsKey(key))
- indexRanges.put(key, new HashSet<Range>());
- indexRanges.get(key).add(r);
- } else {
- if (loc == 0) {
- //Then we have a leading wildcard, reverse the term and use the global reverse index.
- StringBuilder buf = new StringBuilder(normalizedFieldValue.substring(2));
- normalizedFieldValue = buf.reverse().toString();
- Key startRange = new Key(new Text(normalizedFieldValue + "\u0000"), fieldName, new Text(START_ROW));
- Key endRange = new Key(new Text(normalizedFieldValue + "\u10FFFF"), fieldName, new Text(END_ROW));
- Range r = new Range(startRange, true, endRange, false);
-
- MapKey key = new MapKey(fieldName.toString(), normalizedFieldValue);
- key.setOriginalQueryValue(value);
- this.originalQueryValues.put(value, key);
- if (!leadingWildcardRanges.containsKey(key))
- leadingWildcardRanges.put(key, new HashSet<Range>());
- leadingWildcardRanges.get(key).add(r);
- } else if (loc == (normalizedFieldValue.length() -2)) {
- normalizedFieldValue = normalizedFieldValue.substring(0,loc);
- //Then we have a trailing wildcard character.
- Key startRange = new Key(new Text(normalizedFieldValue + "\u0000"), fieldName, new Text(START_ROW));
- Key endRange = new Key(new Text(normalizedFieldValue + "\u10FFFF"), fieldName, new Text(END_ROW));
- Range r = new Range(startRange, true, endRange, false);
-
- MapKey key = new MapKey(fieldName.toString(), normalizedFieldValue);
- key.setOriginalQueryValue(value);
- this.originalQueryValues.put(value, key);
- if (!trailingWildcardRanges.containsKey(key))
- trailingWildcardRanges.put(key, new HashSet<Range>());
- trailingWildcardRanges.get(key).add(r);
- } else {
- //throw new RuntimeException("Unsupported wildcard location. Only trailing or leading wildcards are supported: " + normalizedFieldValue);
- //Don't throw an exception, there must be a wildcard in the query, we'll treat it as a filter on the results since it is not
- //leading or trailing.
- }
- }
- //RANGES
- } else if (entry.getValue().getOperator().equals(JexlOperatorConstants.getOperator(ASTGTNode.class)) ||
- entry.getValue().getOperator().equals(JexlOperatorConstants.getOperator(ASTGENode.class))) {
- //Then we have a lower bound to a range query
- if (!rangeMap.containsKey(fieldName))
- rangeMap.put(fieldName, new RangeBounds());
- rangeMap.get(fieldName).setLower(fieldValue);
- rangeMap.get(fieldName).setOriginalLower(value);
- } else if (entry.getValue().getOperator().equals(JexlOperatorConstants.getOperator(ASTLTNode.class)) ||
- entry.getValue().getOperator().equals(JexlOperatorConstants.getOperator(ASTLENode.class))) {
- //Then we have an upper bound to a range query
- if (!rangeMap.containsKey(fieldName))
- rangeMap.put(fieldName, new RangeBounds());
- rangeMap.get(fieldName).setUpper(fieldValue);
- rangeMap.get(fieldName).setOriginalUpper(value);
- }
- }
- }
- }
-
- //INDEX RANGE QUERY
- //Now that we have figured out the range bounds, create the index ranges.
- for (Entry<Text,RangeBounds> entry : rangeMap.entrySet()) {
- if (entry.getValue().getLower() != null && entry.getValue().getUpper() != null) {
- //Figure out the key order
- Key lk = new Key(entry.getValue().getLower());
- Key up = new Key(entry.getValue().getUpper());
- Text lower = lk.getRow();
- Text upper = up.getRow();
- //Swith the order if needed.
- if (lk.compareTo(up) > 0) {
- lower = up.getRow();
- upper = lk.getRow();
- }
- Key startRange = new Key(lower, entry.getKey(), new Text(START_ROW));
- Key endRange = new Key(upper, entry.getKey(), new Text(END_ROW));
- Range r = new Range(startRange, true, endRange, false);
- //For the range queries we need to query the global index and then handle the results a little differently.
- Map<MapKey, Set<Range>> ranges = new HashMap<MapKey, Set<Range>>();
- MapKey key = new MapKey(entry.getKey().toString(), entry.getValue().getLower().toString());
- key.setOriginalQueryValue(entry.getValue().getOriginalLower().toString());
- this.originalQueryValues.put(entry.getValue().getOriginalLower().toString(), key);
- ranges.put(key, new HashSet<Range>());
- ranges.get(key).add(r);
-
- //Now query the global index and override the field value used in the results map
- try {
- Map<MapKey, TermRange> lowerResults = queryGlobalIndex(ranges, entry.getKey().toString(), this.indexTableName, false, key, typeFilter);
- //Add the results to the global index results for both the upper and lower field values.
- Map<MapKey, TermRange> upperResults = new HashMap<MapKey, TermRange>();
- for (Entry<MapKey, TermRange> e : lowerResults.entrySet()) {
- MapKey key2 = new MapKey(e.getKey().getFieldName(), entry.getValue().getUpper().toString());
- key2.setOriginalQueryValue(entry.getValue().getOriginalUpper().toString());
- upperResults.put(key2, e.getValue());
- this.originalQueryValues.put(entry.getValue().getOriginalUpper(), key2);
-
- }
-
- this.globalIndexResults.putAll(lowerResults);
- this.globalIndexResults.putAll(upperResults);
-
- } catch (TableNotFoundException e) {
- log.error("index table not found", e);
- throw new RuntimeException(" index table not found", e);
- }
- } else {
- log.warn("Unbounded range detected, not querying index for it. Field " + entry.getKey().toString() +
- " in query: " + query);
- }
+
+ /**
+ * Container used as map keys in this class
+ *
+ */
+ public static class MapKey implements Comparable<MapKey> {
+ private String fieldName = null;
+ private String fieldValue = null;
+ private String originalQueryValue = null;
+
+ public MapKey(String fieldName, String fieldValue) {
+ super();
+ this.fieldName = fieldName;
+ this.fieldValue = fieldValue;
+ }
+
+ public String getFieldName() {
+ return fieldName;
+ }
+
+ public String getFieldValue() {
+ return fieldValue;
+ }
+
+ public void setFieldName(String fieldName) {
+ this.fieldName = fieldName;
+ }
+
+ public void setFieldValue(String fieldValue) {
+ this.fieldValue = fieldValue;
+ }
+
+ public String getOriginalQueryValue() {
+ return originalQueryValue;
+ }
+
+ public void setOriginalQueryValue(String originalQueryValue) {
+ this.originalQueryValue = originalQueryValue;
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder(17, 37).append(fieldName).append(fieldValue).toHashCode();
+ }
+
+ @Override
+ public String toString() {
+ return this.fieldName + " " + this.fieldValue;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null)
+ return false;
+ if (other instanceof MapKey) {
+ MapKey o = (MapKey) other;
+ return (this.fieldName.equals(o.fieldName) && this.fieldValue.equals(o.fieldValue));
+ } else
+ return false;
+ }
+
+ public int compareTo(MapKey o) {
+ int result = this.fieldName.compareTo(o.fieldName);
+ if (result != 0) {
+ return this.fieldValue.compareTo(o.fieldValue);
+ } else {
+ return result;
+ }
+ }
+
+ }
+
+ /**
+ * Container used to hold the lower and upper bound of a range
+ *
+ */
+ public static class RangeBounds {
+ private String originalLower = null;
+ private Text lower = null;
+ private String originalUpper = null;
+ private Text upper = null;
+
+ public Text getLower() {
+ return lower;
+ }
+
+ public Text getUpper() {
+ return upper;
+ }
+
+ public void setLower(Text lower) {
+ this.lower = lower;
+ }
+
+ public void setUpper(Text upper) {
+ this.upper = upper;
+ }
+
+ public String getOriginalLower() {
+ return originalLower;
+ }
+
+ public String getOriginalUpper() {
+ return originalUpper;
+ }
+
+ public void setOriginalLower(String originalLower) {
+ this.originalLower = originalLower;
+ }
+
+ public void setOriginalUpper(String originalUpper) {
+ this.originalUpper = originalUpper;
+ }
+ }
+
+ /**
+ *
+ * Object that is used to hold ranges found in the index. Subclasses may compute the final range set in various ways.
+ */
+ protected static class TermRange implements Comparable<TermRange> {
+
+ private String fieldName = null;
+ private Object fieldValue = null;
+ private Set<Range> ranges = new TreeSet<Range>();
+
+ public TermRange(String name, Object fieldValue) {
+ this.fieldName = name;
+ this.fieldValue = fieldValue;
+ }
+
+ public String getFieldName() {
+ return this.fieldName;
+ }
+
+ public Object getFieldValue() {
+ return this.fieldValue;
+ }
+
+ public void addAll(Set<Range> r) {
+ ranges.addAll(r);
+ }
+
+ public void add(Range r) {
+ ranges.add(r);
+ }
+
+ public Set<Range> getRanges() {
+ return ranges;
+ }
+
+ @Override
+ public String toString() {
+ ToStringBuilder tsb = new ToStringBuilder(this);
+ tsb.append("fieldName", fieldName);
+ tsb.append("fieldValue", fieldValue);
+ tsb.append("ranges", ranges);
+ return tsb.toString();
+ }
+
+ public int compareTo(TermRange o) {
+ int result = this.fieldName.compareTo(o.fieldName);
+ if (result == 0) {
+ return ((Integer) ranges.size()).compareTo(o.ranges.size());
+ } else {
+ return result;
+ }
+ }
+ }
+
+ /**
+ * Object used to store context information as the AST is being traversed.
+ */
+ static class EvaluationContext {
+ boolean inOrContext = false;
+ boolean inNotContext = false;
+ boolean inAndContext = false;
+ TermRange lastRange = null;
+ String lastProcessedTerm = null;
+ }
+
+ protected static Logger log = Logger.getLogger(RangeCalculator.class);
+ private static String WILDCARD = ".*";
+ private static String SINGLE_WILDCARD = "\\.";
+ protected static String START_ROW = "0";
+
+ protected Connector c;
+ protected Authorizations auths;
+ protected Multimap<String,Normalizer> indexedTerms;
+ protected Multimap<String,QueryTerm> termsCopy = HashMultimap.create();
+ protected String indexTableName;
+ protected String reverseIndexTableName;
+ protected int queryThreads = 8;
+ protected String END_ROW = null;
+
+ /* final results of index lookups, ranges for the shard table */
+ protected Set<Range> result = null;
+ /* map of field names to values found in the index */
+ protected Multimap<String,String> indexEntries = HashMultimap.create();
+ /* map of value in the index to the original query values */
+ protected Map<String,String> indexValues = new HashMap<String,String>();
+ /* map of values in the query to map keys used */
+ protected Multimap<String,MapKey> originalQueryValues = HashMultimap.create();
+ /* map of field name to cardinality */
+ protected Map<String,Long> termCardinalities = new HashMap<String,Long>();
+ /* cached results of all ranges found global index lookups */
+ protected Map<MapKey,TermRange> globalIndexResults = new HashMap<MapKey,TermRange>();
+
+ /**
+ *
+ * @param c
+ * @param auths
+ * @param indexedTerms
+ * @param terms
+ * @param begin
+ * @param end
+ * @param dateFormatter
+ * @param query
+ * @param logic
+ * @param typeFilter
+ * @throws ParseException
+ */
+ public void execute(Connector c, Authorizations auths, Multimap<String,Normalizer> indexedTerms, Multimap<String,QueryTerm> terms, String query,
+ AbstractQueryLogic logic, Set<String> typeFilter) throws ParseException {
+ super.execute(query);
+ this.c = c;
+ this.auths = auths;
+ this.indexedTerms = indexedTerms;
+ this.termsCopy.putAll(terms);
+ this.indexTableName = logic.getIndexTableName();
+ this.reverseIndexTableName = logic.getReverseIndexTableName();
+ this.queryThreads = logic.getQueryThreads();
+ this.END_ROW = Integer.toString(logic.getNumPartitions());
+
+ Map<MapKey,Set<Range>> indexRanges = new HashMap<MapKey,Set<Range>>();
+ Map<MapKey,Set<Range>> trailingWildcardRanges = new HashMap<MapKey,Set<Range>>();
+ Map<MapKey,Set<Range>> leadingWildcardRanges = new HashMap<MapKey,Set<Range>>();
+ Map<Text,RangeBounds> rangeMap = new HashMap<Text,RangeBounds>();
+
+ // Here we iterate over all of the terms in the query to determine if they are an equivalence,
+ // wildcard, or range type operator
+ for (Entry<String,QueryTerm> entry : terms.entries()) {
+ if (entry.getValue().getOperator().equals(JexlOperatorConstants.getOperator(ASTEQNode.class))
+ || entry.getValue().getOperator().equals(JexlOperatorConstants.getOperator(ASTERNode.class))
+ || entry.getValue().getOperator().equals(JexlOperatorConstants.getOperator(ASTLTNode.class))
+ || entry.getValue().getOperator().equals(JexlOperatorConstants.getOperator(ASTLENode.class))
+ || entry.getValue().getOperator().equals(JexlOperatorConstants.getOperator(ASTGTNode.class))
+ || entry.getValue().getOperator().equals(JexlOperatorConstants.getOperator(ASTGENode.class))) {
+ // If this term is not in the set of indexed terms, then bail
+ if (!indexedTerms.containsKey(entry.getKey())) {
+ termCardinalities.put(entry.getKey().toUpperCase(), 0L);
+ continue;
+ }
+ // In the case of function calls, the query term could be null. Dont query the index for it.
+ if (null == entry.getValue()) {
+ termCardinalities.put(entry.getKey().toUpperCase(), 0L);
+ continue;
+ }
+ // In the case where we are looking for 'null', then skip.
+ if (null == entry.getValue().getValue() || ((String) entry.getValue().getValue()).equals("null")) {
+ termCardinalities.put(entry.getKey().toUpperCase(), 0L);
+ continue;
}
- //Now that we have calculated all of the ranges, query the global index.
- try {
-
- //Query for the trailing wildcards if we have any
- for (Entry<MapKey, Set<Range>> trailing : trailingWildcardRanges.entrySet()) {
- Map<MapKey, Set<Range>> m = new HashMap<MapKey, Set<Range>>();
- m.put(trailing.getKey(), trailing.getValue());
- if (log.isDebugEnabled())
- log.debug("Ranges for Wildcard Global Index query: " + m.toString());
- this.globalIndexResults.putAll(queryGlobalIndex(m, trailing.getKey().getFieldName(), this.indexTableName, false, trailing.getKey(), typeFilter));
- }
-
- //Query for the leading wildcards if we have any
- for (Entry<MapKey, Set<Range>> leading : leadingWildcardRanges.entrySet()) {
- Map<MapKey, Set<Range>> m = new HashMap<MapKey, Set<Range>>();
- m.put(leading.getKey(), leading.getValue());
- if (log.isDebugEnabled())
- log.debug("Ranges for Wildcard Global Reverse Index query: " + m.toString());
- this.globalIndexResults.putAll(queryGlobalIndex(m, leading.getKey().getFieldName(), this.reverseIndexTableName, true, leading.getKey(), typeFilter));
- }
-
- //Query for the equals case
- for (Entry<MapKey, Set<Range>> equals : indexRanges.entrySet()) {
- Map<MapKey, Set<Range>> m = new HashMap<MapKey, Set<Range>>();
- m.put(equals.getKey(), equals.getValue());
- if (log.isDebugEnabled())
- log.debug("Ranges for Global Index query: " + m.toString());
- this.globalIndexResults.putAll(queryGlobalIndex(m, equals.getKey().getFieldName(), this.indexTableName, false, equals.getKey(), typeFilter));
- }
- } catch (TableNotFoundException e) {
- log.error("index table not found", e);
- throw new RuntimeException(" index table not found", e);
- }
-
- if (log.isDebugEnabled())
- log.debug("Ranges from Global Index query: " + globalIndexResults.toString());
-
- //Now traverse the AST
- EvaluationContext ctx = new EvaluationContext();
- this.getAST().childrenAccept(this, ctx);
- if (ctx.lastRange.getRanges().size() == 0) {
- log.debug("No resulting range set");
- } else {
- if (log.isDebugEnabled())
- log.debug("Setting range results to: " + ctx.lastRange.getRanges().toString());
- this.result = ctx.lastRange.getRanges();
- }
- }
-
- /**
- *
- * @return set of ranges to use for the shard table
- */
- public Set<Range> getResult() {
- return result;
- }
-
- /**
- *
- * @return map of field names to index field values
- */
- public Multimap<String,String> getIndexEntries() {
- return indexEntries;
- }
-
- public Map<String,String> getIndexValues() {
- return indexValues;
- }
- /**
- *
- * @return Cardinality for each field name.
- */
- public Map<String, Long> getTermCardinalities() {
- return termCardinalities;
- }
-
- /**
- *
- * @param indexRanges
- * @param tableName
- * @param isReverse switch that determines whether or not to reverse the results
- * @param override mapKey for wildcard and range queries that specify which mapkey to use in the results
- * @param typeFilter - optional list of datatypes
- * @return
- * @throws TableNotFoundException
- */
- protected Map<MapKey, TermRange> queryGlobalIndex(Map<MapKey, Set<Range>> indexRanges, String specificFieldName, String tableName, boolean isReverse, MapKey override, Set<String> typeFilter) throws TableNotFoundException {
-
- //The results map where the key is the field name and field value and the
- //value is a set of ranges. The mapkey will always be the field name
- //and field value that was passed in the original query. The TermRange
- //will contain the field name and field value found in the index.
- Map<MapKey, TermRange> results = new HashMap<MapKey, TermRange>();
-
- //Seed the results map and create the range set for the batch scanner
- Set<Range> rangeSuperSet = new HashSet<Range>();
- for (Entry<MapKey, Set<Range>> entry : indexRanges.entrySet()) {
- rangeSuperSet.addAll(entry.getValue());
- TermRange tr = new TermRange(entry.getKey().getFieldName(), entry.getKey().getFieldValue());
- if (null == override)
- results.put(entry.getKey(), tr);
- else
- results.put(override, tr);
- }
-
- if (log.isDebugEnabled())
- log.debug("Querying global index table: " + tableName + ", range: " + rangeSuperSet.toString() + " colf: " + specificFieldName);
- BatchScanner bs = this.c.createBatchScanner(tableName, this.auths, this.queryThreads);
- bs.setRanges(rangeSuperSet);
- if (null != specificFieldName) {
- bs.fetchColumnFamily(new Text(specificFieldName));
- }
-
- long indexEntries = 0;
- for (Entry<Key, Value> entry : bs) {
- if (log.isDebugEnabled()) {
- log.debug("Index entry: " + entry.getKey().toString());
- }
- String fieldValue = null;
- if (!isReverse) {
- fieldValue = entry.getKey().getRow().toString();
- } else {
- StringBuilder buf = new StringBuilder(entry.getKey().getRow().toString());
- fieldValue = buf.reverse().toString();
- }
-
- String fieldName = entry.getKey().getColumnFamily().toString();
- //Get the shard id and datatype from the colq
- String colq = entry.getKey().getColumnQualifier().toString();
- int separator = colq.indexOf(EvaluatingIterator.NULL_BYTE_STRING);
- String shardId = null;
- String datatype = null;
- if (separator != -1) {
- shardId = colq.substring(0, separator);
- datatype = colq.substring(separator + 1);
- } else {
- shardId = colq;
- }
- //Skip this entry if the type is not correct
- if (null != datatype && null != typeFilter && !typeFilter.contains(datatype))
- continue;
- //Parse the UID.List object from the value
- Uid.List uidList = null;
- try {
- uidList = Uid.List.parseFrom(entry.getValue().get());
- } catch (InvalidProtocolBufferException e) {
- //Don't add UID information, at least we know what shards
- //it is located in.
- }
-
- //Add the count for this shard to the total count for the term.
- long count = 0;
- Long storedCount = termCardinalities.get(fieldName);
- if (null == storedCount || 0 == storedCount) {
- count = uidList.getCOUNT();
- } else {
- count = uidList.getCOUNT() + storedCount;
- }
- termCardinalities.put(fieldName, count);
- this.indexEntries.put(fieldName, fieldValue);
-
- if (null == override)
- this.indexValues.put(fieldValue, fieldValue);
- else
- this.indexValues.put(fieldValue, override.getOriginalQueryValue());
+ // Remove the begin and end ' marks
+ String value = null;
+ if (((String) entry.getValue().getValue()).startsWith("'") && ((String) entry.getValue().getValue()).endsWith("'"))
+ value = ((String) entry.getValue().getValue()).substring(1, ((String) entry.getValue().getValue()).length() - 1);
+ else
+ value = (String) entry.getValue().getValue();
+ // The entries in the index are normalized
+ for (Normalizer normalizer : indexedTerms.get(entry.getKey())) {
+ String normalizedFieldValue = normalizer.normalizeFieldValue(null, value);
+ Text fieldValue = new Text(normalizedFieldValue);
+ Text fieldName = new Text(entry.getKey().toUpperCase());
+
+ // EQUALS
+ if (entry.getValue().getOperator().equals(JexlOperatorConstants.getOperator(ASTEQNode.class))) {
+ Key startRange = new Key(fieldValue, fieldName, new Text(START_ROW));
+ Key endRange = new Key(fieldValue, fieldName, new Text(END_ROW));
+ Range r = new Range(startRange, true, endRange, false);
- //Create the keys
- Text shard = new Text(shardId);
- if (uidList.getIGNORE()) {
- //Then we create a scan range that is the entire shard
- if (null == override)
- results.get(new MapKey(fieldName, fieldValue)).add(new Range(shard));
- else
- results.get(override).add(new Range(shard));
- indexEntries++;
+ MapKey key = new MapKey(fieldName.toString(), fieldValue.toString());
+ key.setOriginalQueryValue(value);
+ this.originalQueryValues.put(value, key);
+ if (!indexRanges.containsKey(key))
+ indexRanges.put(key, new HashSet<Range>());
+ indexRanges.get(key).add(r);
+ // WILDCARD
+ } else if (entry.getValue().getOperator().equals(JexlOperatorConstants.getOperator(ASTERNode.class))) {
+ // This is a wildcard query using regex. We can only support leading and trailing wildcards at this time. Leading
+ // wildcards will need be reversed and sent to the global reverse index. Trailing wildcard queries will be sent to the
+ // global index. In all cases, the range for the wilcard will be the range of possible UNICODE codepoints, hex 0 to 10FFFF.
+ int loc = normalizedFieldValue.indexOf(WILDCARD);
+ if (-1 == loc)
+ loc = normalizedFieldValue.indexOf(SINGLE_WILDCARD);
+ if (-1 == loc) {
+ // Then no wildcard in the query? Treat like the equals case above.
+ Key startRange = new Key(fieldValue, fieldName, new Text(START_ROW));
+ Key endRange = new Key(fieldValue, fieldName, new Text(END_ROW));
+ Range r = new Range(startRange, true, endRange, false);
+
+ MapKey key = new MapKey(fieldName.toString(), fieldValue.toString());
+ key.setOriginalQueryValue(value);
+ this.originalQueryValues.put(value, key);
+ if (!indexRanges.containsKey(key))
+ indexRanges.put(key, new HashSet<Range>());
+ indexRanges.get(key).add(r);
} else {
- //We should have UUIDs, create event ranges
- for (String uuid : uidList.getUIDList()) {
- Text cf = new Text(datatype);
- TextUtil.textAppend(cf, uuid);
- Key startKey = new Key(shard, cf);
- Key endKey = new Key(shard, new Text(cf.toString() + EvaluatingIterator.NULL_BYTE_STRING));
- Range eventRange = new Range(startKey, true, endKey, false);
- if (null == override)
- results.get(new MapKey(fieldName, fieldValue)).add(eventRange);
- else
- results.get(override).add(eventRange);
- indexEntries++;
- }
+ if (loc == 0) {
+ // Then we have a leading wildcard, reverse the term and use the global reverse index.
+ StringBuilder buf = new StringBuilder(normalizedFieldValue.substring(2));
+ normalizedFieldValue = buf.reverse().toString();
+ Key startRange = new Key(new Text(normalizedFieldValue + "\u0000"), fieldName, new Text(START_ROW));
+ Key endRange = new Key(new Text(normalizedFieldValue + "\u10FFFF"), fieldName, new Text(END_ROW));
+ Range r = new Range(startRange, true, endRange, false);
+
+ MapKey key = new MapKey(fieldName.toString(), normalizedFieldValue);
+ key.setOriginalQueryValue(value);
+ this.originalQueryValues.put(value, key);
+ if (!leadingWildcardRanges.containsKey(key))
+ leadingWildcardRanges.put(key, new HashSet<Range>());
+ leadingWildcardRanges.get(key).add(r);
+ } else if (loc == (normalizedFieldValue.length() - 2)) {
+ normalizedFieldValue = normalizedFieldValue.substring(0, loc);
+ // Then we have a trailing wildcard character.
+ Key startRange = new Key(new Text(normalizedFieldValue + "\u0000"), fieldName, new Text(START_ROW));
+ Key endRange = new Key(new Text(normalizedFieldValue + "\u10FFFF"), fieldName, new Text(END_ROW));
+ Range r = new Range(startRange, true, endRange, false);
+
+ MapKey key = new MapKey(fieldName.toString(), normalizedFieldValue);
+ key.setOriginalQueryValue(value);
+ this.originalQueryValues.put(value, key);
+ if (!trailingWildcardRanges.containsKey(key))
+ trailingWildcardRanges.put(key, new HashSet<Range>());
+ trailingWildcardRanges.get(key).add(r);
+ } else {
+ // throw new RuntimeException("Unsupported wildcard location. Only trailing or leading wildcards are supported: " + normalizedFieldValue);
+ // Don't throw an exception, there must be a wildcard in the query, we'll treat it as a filter on the results since it is not
+ // leading or trailing.
+ }
}
+ // RANGES
+ } else if (entry.getValue().getOperator().equals(JexlOperatorConstants.getOperator(ASTGTNode.class))
+ || entry.getValue().getOperator().equals(JexlOperatorConstants.getOperator(ASTGENode.class))) {
+ // Then we have a lower bound to a range query
+ if (!rangeMap.containsKey(fieldName))
+ rangeMap.put(fieldName, new RangeBounds());
+ rangeMap.get(fieldName).setLower(fieldValue);
+ rangeMap.get(fieldName).setOriginalLower(value);
+ } else if (entry.getValue().getOperator().equals(JexlOperatorConstants.getOperator(ASTLTNode.class))
+ || entry.getValue().getOperator().equals(JexlOperatorConstants.getOperator(ASTLENode.class))) {
+ // Then we have an upper bound to a range query
+ if (!rangeMap.containsKey(fieldName))
+ rangeMap.put(fieldName, new RangeBounds());
+ rangeMap.get(fieldName).setUpper(fieldValue);
+ rangeMap.get(fieldName).setOriginalUpper(value);
+ }
}
- bs.close();
- return results;
- }
-
-
- @Override
- public Object visit(ASTOrNode node, Object data) {
- boolean previouslyInOrContext = false;
- EvaluationContext ctx = null;
- if (null != data && data instanceof EvaluationContext) {
- ctx = (EvaluationContext) data;
- previouslyInOrContext = ctx.inOrContext;
- } else {
- ctx = new EvaluationContext();
- }
- ctx.inOrContext = true;
- //Process both sides of this node. Left branch first
- node.jjtGetChild(0).jjtAccept(this, ctx);
- Long leftCardinality = this.termCardinalities.get(ctx.lastProcessedTerm);
- if (null == leftCardinality)
- leftCardinality = 0L;
- TermRange leftRange = ctx.lastRange;
- if (log.isDebugEnabled())
- log.debug("[OR-left] term: " + ctx.lastProcessedTerm + ", cardinality: " + leftCardinality + ", ranges: " + leftRange.getRanges().size());
+ }
+ }
+
+ // INDEX RANGE QUERY
+ // Now that we have figured out the range bounds, create the index ranges.
+ for (Entry<Text,RangeBounds> entry : rangeMap.entrySet()) {
+ if (entry.getValue().getLower() != null && entry.getValue().getUpper() != null) {
+ // Figure out the key order
+ Key lk = new Key(entry.getValue().getLower());
+ Key up = new Key(entry.getValue().getUpper());
+ Text lower = lk.getRow();
+ Text upper = up.getRow();
+ // Swith the order if needed.
+ if (lk.compareTo(up) > 0) {
+ lower = up.getRow();
+ upper = lk.getRow();
+ }
+ Key startRange = new Key(lower, entry.getKey(), new Text(START_ROW));
+ Key endRange = new Key(upper, entry.getKey(), new Text(END_ROW));
+ Range r = new Range(startRange, true, endRange, false);
+ // For the range queries we need to query the global index and then handle the results a little differently.
+ Map<MapKey,Set<Range>> ranges = new HashMap<MapKey,Set<Range>>();
+ MapKey key = new MapKey(entry.getKey().toString(), entry.getValue().getLower().toString());
+ key.setOriginalQueryValue(entry.getValue().getOriginalLower().toString());
+ this.originalQueryValues.put(entry.getValue().getOriginalLower().toString(), key);
+ ranges.put(key, new HashSet<Range>());
+ ranges.get(key).add(r);
- //Process the right branch
- node.jjtGetChild(1).jjtAccept(this, ctx);
- Long rightCardinality = this.termCardinalities.get(ctx.lastProcessedTerm);
- if (null == rightCardinality)
- rightCardinality = 0L;
- TermRange rightRange = ctx.lastRange;
+ // Now query the global index and override the field value used in the results map
+ try {
+ Map<MapKey,TermRange> lowerResults = queryGlobalIndex(ranges, entry.getKey().toString(), this.indexTableName, false, key, typeFilter);
+ // Add the results to the global index results for both the upper and lower field values.
+ Map<MapKey,TermRange> upperResults = new HashMap<MapKey,TermRange>();
+ for (Entry<MapKey,TermRange> e : lowerResults.entrySet()) {
+ MapKey key2 = new MapKey(e.getKey().getFieldName(), entry.getValue().getUpper().toString());
+ key2.setOriginalQueryValue(entry.getValue().getOriginalUpper().toString());
+ upperResults.put(key2, e.getValue());
+ this.originalQueryValues.put(entry.getValue().getOriginalUpper(), key2);
+
+ }
+
+ this.globalIndexResults.putAll(lowerResults);
+ this.globalIndexResults.putAll(upperResults);
+
+ } catch (TableNotFoundException e) {
+ log.error("index table not found", e);
+ throw new RuntimeException(" index table not found", e);
+ }
+ } else {
+ log.warn("Unbounded range detected, not querying index for it. Field " + entry.getKey().toString() + " in query: " + query);
+ }
+ }
+ // Now that we have calculated all of the ranges, query the global index.
+ try {
+
+ // Query for the trailing wildcards if we have any
+ for (Entry<MapKey,Set<Range>> trailing : trailingWildcardRanges.entrySet()) {
+ Map<MapKey,Set<Range>> m = new HashMap<MapKey,Set<Range>>();
+ m.put(trailing.getKey(), trailing.getValue());
if (log.isDebugEnabled())
- log.debug("[OR-right] term: " + ctx.lastProcessedTerm + ", cardinality: " + rightCardinality + ", ranges: " + rightRange.getRanges().size());
-
- //reset the state
- if (null != data && !previouslyInOrContext)
- ctx.inOrContext = false;
- //Add the ranges for the left and right branches to a TreeSet to sort them
- Set<Range> ranges = new TreeSet<Range>();
- ranges.addAll(leftRange.getRanges());
- ranges.addAll(rightRange.getRanges());
- //Now create the union set
- Set<Text> shardsAdded = new HashSet<Text>();
- Set<Range> returnSet = new HashSet<Range>();
- for (Range r : ranges) {
- if (!shardsAdded.contains(r.getStartKey().getRow())) {
- //Only add ranges with a start key for the entire shard.
- if (r.getStartKey().getColumnFamily() == null) {
- shardsAdded.add(r.getStartKey().getRow());
- }
- returnSet.add(r);
- } else {
- //if (log.isTraceEnabled())
- log.info("Skipping event specific range: " + r.toString() + " because shard range has already been added: " + shardsAdded.contains(r.getStartKey().getRow()));
- }
+ log.debug("Ranges for Wildcard Global Index query: " + m.toString());
+ this.globalIndexResults.putAll(queryGlobalIndex(m, trailing.getKey().getFieldName(), this.indexTableName, false, trailing.getKey(), typeFilter));
+ }
+
+ // Query for the leading wildcards if we have any
+ for (Entry<MapKey,Set<Range>> leading : leadingWildcardRanges.entrySet()) {
+ Map<MapKey,Set<Range>> m = new HashMap<MapKey,Set<Range>>();
+ m.put(leading.getKey(), leading.getValue());
+ if (log.isDebugEnabled())
+ log.debug("Ranges for Wildcard Global Reverse Index query: " + m.toString());
+ this.globalIndexResults.putAll(queryGlobalIndex(m, leading.getKey().getFieldName(), this.reverseIndexTableName, true, leading.getKey(), typeFilter));
+ }
+
+ // Query for the equals case
+ for (Entry<MapKey,Set<Range>> equals : indexRanges.entrySet()) {
+ Map<MapKey,Set<Range>> m = new HashMap<MapKey,Set<Range>>();
+ m.put(equals.getKey(), equals.getValue());
+ if (log.isDebugEnabled())
+ log.debug("Ranges for Global Index query: " + m.toString());
+ this.globalIndexResults.putAll(queryGlobalIndex(m, equals.getKey().getFieldName(), this.indexTableName, false, equals.getKey(), typeFilter));
+ }
+ } catch (TableNotFoundException e) {
+ log.error("index table not found", e);
+ throw new RuntimeException(" index table not found", e);
+ }
+
+ if (log.isDebugEnabled())
+ log.debug("Ranges from Global Index query: " + globalIndexResults.toString());
+
+ // Now traverse the AST
+ EvaluationContext ctx = new EvaluationContext();
+ this.getAST().childrenAccept(this, ctx);
+
+ if (ctx.lastRange.getRanges().size() == 0) {
+ log.debug("No resulting range set");
+ } else {
+ if (log.isDebugEnabled())
+ log.debug("Setting range results to: " + ctx.lastRange.getRanges().toString());
+ this.result = ctx.lastRange.getRanges();
+ }
+ }
+
+ /**
+ *
+ * @return set of ranges to use for the shard table
+ */
+ public Set<Range> getResult() {
+ return result;
+ }
+
+ /**
+ *
+ * @return map of field names to index field values
+ */
+ public Multimap<String,String> getIndexEntries() {
+ return indexEntries;
+ }
+
+ public Map<String,String> getIndexValues() {
+ return indexValues;
+ }
+
+ /**
+ *
+ * @return Cardinality for each field name.
+ */
+ public Map<String,Long> getTermCardinalities() {
+ return termCardinalities;
+ }
+
+ /**
+ *
+ * @param indexRanges
+ * @param tableName
+ * @param isReverse
+ * switch that determines whether or not to reverse the results
+ * @param override
+ * mapKey for wildcard and range queries that specify which mapkey to use in the results
+ * @param typeFilter
+ * - optional list of datatypes
+ * @return
+ * @throws TableNotFoundException
+ */
+ protected Map<MapKey,TermRange> queryGlobalIndex(Map<MapKey,Set<Range>> indexRanges, String specificFieldName, String tableName, boolean isReverse,
+ MapKey override, Set<String> typeFilter) throws TableNotFoundException {
+
+ // The results map where the key is the field name and field value and the
+ // value is a set of ranges. The mapkey will always be the field name
+ // and field value that was passed in the original query. The TermRange
+ // will contain the field name and field value found in the index.
+ Map<MapKey,TermRange> results = new HashMap<MapKey,TermRange>();
+
+ // Seed the results map and create the range set for the batch scanner
+ Set<Range> rangeSuperSet = new HashSet<Range>();
+ for (Entry<MapKey,Set<Range>> entry : indexRanges.entrySet()) {
+ rangeSuperSet.addAll(entry.getValue());
+ TermRange tr = new TermRange(entry.getKey().getFieldName(), entry.getKey().getFieldValue());
+ if (null == override)
+ results.put(entry.getKey(), tr);
+ else
+ results.put(override, tr);
+ }
+
+ if (log.isDebugEnabled())
+ log.debug("Querying global index table: " + tableName + ", range: " + rangeSuperSet.toString() + " colf: " + specificFieldName);
+ BatchScanner bs = this.c.createBatchScanner(tableName, this.auths, this.queryThreads);
+ bs.setRanges(rangeSuperSet);
+ if (null != specificFieldName) {
+ bs.fetchColumnFamily(new Text(specificFieldName));
+ }
+
+ long indexEntries = 0;
+ for (Entry<Key,Value> entry : bs) {
+ if (log.isDebugEnabled()) {
+ log.debug("Index entry: " + entry.getKey().toString());
+ }
+ String fieldValue = null;
+ if (!isReverse) {
+ fieldValue = entry.getKey().getRow().toString();
+ } else {
+ StringBuilder buf = new StringBuilder(entry.getKey().getRow().toString());
+ fieldValue = buf.reverse().toString();
+ }
+
+ String fieldName = entry.getKey().getColumnFamily().toString();
+ // Get the shard id and datatype from the colq
+ String colq = entry.getKey().getColumnQualifier().toString();
+ int separator = colq.indexOf(EvaluatingIterator.NULL_BYTE_STRING);
+ String shardId = null;
+ String datatype = null;
+ if (separator != -1) {
+ shardId = colq.substring(0, separator);
+ datatype = colq.substring(separator + 1);
+ } else {
+ shardId = colq;
+ }
+ // Skip this entry if the type is not correct
+ if (null != datatype && null != typeFilter && !typeFilter.contains(datatype))
+ continue;
+ // Parse the UID.List object from the value
+ Uid.List uidList = null;
+ try {
+ uidList = Uid.List.parseFrom(entry.getValue().get());
+ } catch (InvalidProtocolBufferException e) {
+ // Don't add UID information, at least we know what shards
+ // it is located in.
+ }
+
+ // Add the count for this shard to the total count for the term.
+ long count = 0;
+ Long storedCount = termCardinalities.get(fieldName);
+ if (null == storedCount || 0 == storedCount) {
+ count = uidList.getCOUNT();
+ } else {
+ count = uidList.getCOUNT() + storedCount;
+ }
+ termCardinalities.put(fieldName, count);
+ this.indexEntries.put(fieldName, fieldValue);
+
+ if (null == override)
+ this.indexValues.put(fieldValue, fieldValue);
+ else
+ this.indexValues.put(fieldValue, override.getOriginalQueryValue());
+
+ // Create the keys
+ Text shard = new Text(shardId);
+ if (uidList.getIGNORE()) {
+ // Then we create a scan range that is the entire shard
+ if (null == override)
+ results.get(new MapKey(fieldName, fieldValue)).add(new Range(shard));
+ else
+ results.get(override).add(new Range(shard));
+ indexEntries++;
+ } else {
+ // We should have UUIDs, create event ranges
+ for (String uuid : uidList.getUIDList()) {
+ Text cf = new Text(datatype);
+ TextUtil.textAppend(cf, uuid);
+ Key startKey = new Key(shard, cf);
+ Key endKey = new Key(shard, new Text(cf.toString() + EvaluatingIterator.NULL_BYTE_STRING));
+ Range eventRange = new Range(startKey, true, endKey, false);
+ if (null == override)
+ results.get(new MapKey(fieldName, fieldValue)).add(eventRange);
+ else
+ results.get(override).add(eventRange);
+ indexEntries++;
}
- //Clear the ranges from the context and add the result in its place
- TermRange orRange = new TermRange("OR_RESULT", "foo");
- orRange.addAll(returnSet);
+ }
+ }
+ bs.close();
+ return results;
+ }
+
+ @Override
+ public Object visit(ASTOrNode node, Object data) {
+ boolean previouslyInOrContext = false;
+ EvaluationContext ctx = null;
+ if (null != data && data instanceof EvaluationContext) {
+ ctx = (EvaluationContext) data;
+ previouslyInOrContext = ctx.inOrContext;
+ } else {
+ ctx = new EvaluationContext();
+ }
+ ctx.inOrContext = true;
+ // Process both sides of this node. Left branch first
+ node.jjtGetChild(0).jjtAccept(this, ctx);
+ Long leftCardinality = this.termCardinalities.get(ctx.lastProcessedTerm);
+ if (null == leftCardinality)
+ leftCardinality = 0L;
+ TermRange leftRange = ctx.lastRange;
+ if (log.isDebugEnabled())
+ log.debug("[OR-left] term: " + ctx.lastProcessedTerm + ", cardinality: " + leftCardinality + ", ranges: " + leftRange.getRanges().size());
+
+ // Process the right branch
+ node.jjtGetChild(1).jjtAccept(this, ctx);
+ Long rightCardinality = this.termCardinalities.get(ctx.lastProcessedTerm);
+ if (null == rightCardinality)
+ rightCardinality = 0L;
+ TermRange rightRange = ctx.lastRange;
+ if (log.isDebugEnabled())
+ log.debug("[OR-right] term: " + ctx.lastProcessedTerm + ", cardinality: " + rightCardinality + ", ranges: " + rightRange.getRanges().size());
+
+ // reset the state
+ if (null != data && !previouslyInOrContext)
+ ctx.inOrContext = false;
+ // Add the ranges for the left and right branches to a TreeSet to sort them
+ Set<Range> ranges = new TreeSet<Range>();
+ ranges.addAll(leftRange.getRanges());
+ ranges.addAll(rightRange.getRanges());
+ // Now create the union set
+ Set<Text> shardsAdded = new HashSet<Text>();
+ Set<Range> returnSet = new HashSet<Range>();
+ for (Range r : ranges) {
+ if (!shardsAdded.contains(r.getStartKey().getRow())) {
+ // Only add ranges with a start key for the entire shard.
+ if (r.getStartKey().getColumnFamily() == null) {
+ shardsAdded.add(r.getStartKey().getRow());
+ }
+ returnSet.add(r);
+ } else {
+ // if (log.isTraceEnabled())
+ log.info("Skipping event specific range: " + r.toString() + " because shard range has already been added: "
+ + shardsAdded.contains(r.getStartKey().getRow()));
+ }
+ }
+ // Clear the ranges from the context and add the result in its place
+ TermRange orRange = new TermRange("OR_RESULT", "foo");
+ orRange.addAll(returnSet);
+ if (log.isDebugEnabled())
+ log.debug("[OR] results: " + orRange.getRanges().toString());
+ ctx.lastRange = orRange;
+ ctx.lastProcessedTerm = "OR_RESULT";
+ this.termCardinalities.put("OR_RESULT", (leftCardinality + rightCardinality));
+ return null;
+ }
+
+ @Override
+ public Object visit(ASTAndNode node, Object data) {
+ boolean previouslyInAndContext = false;
+ EvaluationContext ctx = null;
+ if (null != data && data instanceof EvaluationContext) {
+ ctx = (EvaluationContext) data;
+ previouslyInAndContext = ctx.inAndContext;
+ } else {
+ ctx = new EvaluationContext();
+ }
+ ctx.inAndContext = true;
+ // Process both sides of this node.
+ node.jjtGetChild(0).jjtAccept(this, ctx);
+ String leftTerm = ctx.lastProcessedTerm;
+ Long leftCardinality = this.termCardinalities.get(leftTerm);
+ if (null == leftCardinality)
+ leftCardinality = 0L;
+ TermRange leftRange = ctx.lastRange;
+ if (log.isDebugEnabled())
+ log.debug("[AND-left] term: " + ctx.lastProcessedTerm + ", cardinality: " + leftCardinality + ", ranges: " + leftRange.getRanges().size());
+
+ // Process the right branch
+ node.jjtGetChild(1).jjtAccept(this, ctx);
+ String rightTerm = ctx.lastProcessedTerm;
+ Long rightCardinality = this.termCardinalities.get(rightTerm);
+ if (null == rightCardinality)
+ rightCardinality = 0L;
+ TermRange rightRange = ctx.lastRange;
+ if (log.isDebugEnabled())
+ log.debug("[AND-right] term: " + ctx.lastProcessedTerm + ", cardinality: " + rightCardinality + ", ranges: " + rightRange.getRanges().size());
+
+ // reset the state
+ if (null != data && !previouslyInAndContext)
+ ctx.inAndContext = false;
+
+ long card = 0L;
+ TermRange andRange = new TermRange("AND_RESULT", "foo");
+ if ((leftCardinality > 0 && leftCardinality <= rightCardinality) || rightCardinality == 0) {
+ card = leftCardinality;
+ andRange.addAll(leftRange.getRanges());
+ } else if ((rightCardinality > 0 && rightCardinality <= leftCardinality) || leftCardinality == 0) {
+ card = rightCardinality;
+ andRange.addAll(rightRange.getRanges());
+ }
+ if (log.isDebugEnabled())
+ log.debug("[AND] results: " + andRange.getRanges().toString());
+ ctx.lastRange = andRange;
+ ctx.lastProcessedTerm = "AND_RESULT";
+ this.termCardinalities.put("AND_RESULT", card);
+
+ return null;
+ }
+
+ @Override
+ public Object visit(ASTEQNode node, Object data) {
+ StringBuilder fieldName = new StringBuilder();
+ ObjectHolder value = new ObjectHolder();
+ // Process both sides of this node.
+ Object left = node.jjtGetChild(0).jjtAccept(this, data);
+ Object right = node.jjtGetChild(1).jjtAccept(this, data);
+ // Ignore functions in the query
+ if (left instanceof FunctionResult || right instanceof FunctionResult)
+ return null;
+ decodeResults(left, right, fieldName, value);
+ // We need to check to see if we are in a NOT context. If so,
+ // then we need to reverse the negation.
+ boolean negated = false;
+ if (null != data && data instanceof EvaluationContext) {
+ EvaluationContext ctx = (EvaluationContext) data;
+ if (ctx.inNotContext)
+ negated = !negated;
+ }
+ QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
+ termsCopy.put(fieldName.toString(), term);
+ // Get the terms from the global index
+ // Remove the begin and end ' marks
+ String termValue = null;
+ if (((String) term.getValue()).startsWith("'") && ((String) term.getValue()).endsWith("'"))
+ termValue = ((String) term.getValue()).substring(1, ((String) term.getValue()).length() - 1);
+ else
+ termValue = (String) term.getValue();
+ // Get the values found in the index for this query term
+ TermRange ranges = null;
+ for (MapKey key : this.originalQueryValues.get(termValue)) {
+ if (key.getFieldName().equalsIgnoreCase(fieldName.toString())) {
+ ranges = this.globalIndexResults.get(key);
if (log.isDebugEnabled())
- log.debug("[OR] results: " + orRange.getRanges().toString());
- ctx.lastRange = orRange;
- ctx.lastProcessedTerm = "OR_RESULT";
- this.termCardinalities.put("OR_RESULT", (leftCardinality + rightCardinality));
- return null;
- }
-
- @Override
- public Object visit(ASTAndNode node, Object data) {
- boolean previouslyInAndContext = false;
- EvaluationContext ctx = null;
- if (null != data && data instanceof EvaluationContext) {
- ctx = (EvaluationContext) data;
- previouslyInAndContext = ctx.inAndContext;
- } else {
- ctx = new EvaluationContext();
- }
- ctx.inAndContext = true;
- //Process both sides of this node.
- node.jjtGetChild(0).jjtAccept(this, ctx);
- String leftTerm = ctx.lastProcessedTerm;
- Long leftCardinality = this.termCardinalities.get(leftTerm);
- if (null == leftCardinality)
- leftCardinality = 0L;
- TermRange leftRange = ctx.lastRange;
+ log.debug("Results for cached index ranges for key: " + key + " are " + ranges);
+ }
+ }
+ // If no result for this field name and value, then add empty range
+ if (null == ranges)
+ ranges = new TermRange(fieldName.toString(), (String) term.getValue());
+ if (null != data && data instanceof EvaluationContext) {
+ EvaluationContext ctx = (EvaluationContext) data;
+ ctx.lastRange = ranges;
+ ctx.lastProcessedTerm = fieldName.toString();
+ }
+ return null;
+ }
+
+ @Override
+ public Object visit(ASTNENode node, Object data) {
+ StringBuilder fieldName = new StringBuilder();
+ ObjectHolder value = new ObjectHolder();
+ // Process both sides of this node.
+ Object left = node.jjtGetChild(0).jjtAccept(this, data);
+ Object right = node.jjtGetChild(1).jjtAccept(this, data);
+ // Ignore functions in the query
+ if (left instanceof FunctionResult || right instanceof FunctionResult)
+ return null;
+ decodeResults(left, right, fieldName, value);
+ // We need to check to see if we are in a NOT context. If so,
+ // then we need to reverse the negation.
+ boolean negated = true;
+ if (null != data && data instanceof EvaluationContext) {
+ EvaluationContext ctx = (EvaluationContext) data;
+ if (ctx.inNotContext)
+ negated = !negated;
+ }
+ if (negated)
+ negatedTerms.add(fieldName.toString());
+ QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
+ termsCopy.put(fieldName.toString(), term);
+ // We can only use the global index for equality, put in fake results
+ if (null != data && data instanceof EvaluationContext) {
+ EvaluationContext ctx = (EvaluationContext) data;
+ ctx.lastRange = new TermRange(fieldName.toString(), term.getValue());
+ ctx.lastProcessedTerm = fieldName.toString();
+ termCardinalities.put(fieldName.toString(), 0L);
+ }
+ return null;
+ }
+
+ @Override
+ public Object visit(ASTLTNode node, Object data) {
+ StringBuilder fieldName = new StringBuilder();
+ ObjectHolder value = new ObjectHolder();
+ // Process both sides of this node.
+ Object left = node.jjtGetChild(0).jjtAccept(this, data);
+ Object right = node.jjtGetChild(1).jjtAccept(this, data);
+ // Ignore functions in the query
+ if (left instanceof FunctionResult || right instanceof FunctionResult)
+ return null;
+ decodeResults(left, right, fieldName, value);
+ // We need to check to see if we are in a NOT context. If so,
+ // then we need to reverse the negation.
+ boolean negated = false;
+ if (null != data && data instanceof EvaluationContext) {
+ EvaluationContext ctx = (EvaluationContext) data;
+ if (ctx.inNotContext)
+ negated = !negated;
+ }
+ QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
+ termsCopy.put(fieldName.toString(), term);
+ // Get the terms from the global index
+ // Remove the begin and end ' marks
+ String termValue = null;
+ if (((String) term.getValue()).startsWith("'") && ((String) term.getValue()).endsWith("'"))
+ termValue = ((String) term.getValue()).substring(1, ((String) term.getValue()).length() - 1);
+ else
+ termValue = (String) term.getValue();
+ // Get the values found in the index for this query term
+ TermRange ranges = null;
+ for (MapKey key : this.originalQueryValues.get(termValue)) {
+ if (key.getFieldName().equalsIgnoreCase(fieldName.toString())) {
+ ranges = this.globalIndexResults.get(key);
if (log.isDebugEnabled())
- log.debug("[AND-left] term: " + ctx.lastProcessedTerm + ", cardinality: " + leftCardinality + ", ranges: " + leftRange.getRanges().size());
-
- //Process the right branch
- node.jjtGetChild(1).jjtAccept(this, ctx);
- String rightTerm = ctx.lastProcessedTerm;
- Long rightCardinality = this.termCardinalities.get(rightTerm);
- if (null == rightCardinality)
- rightCardinality = 0L;
- TermRange rightRange = ctx.lastRange;
+ log.debug("Results for cached index ranges for key: " + key + " are " + ranges);
+ }
+ }
+ // If no result for this field name and value, then add empty range
+ if (null == ranges)
+ ranges = new TermRange(fieldName.toString(), (String) term.getValue());
+ if (null != data && data instanceof EvaluationContext) {
+ EvaluationContext ctx = (EvaluationContext) data;
+ ctx.lastRange = ranges;
+ ctx.lastProcessedTerm = fieldName.toString();
+ }
+ return null;
+ }
+
+ @Override
+ public Object visit(ASTGTNode node, Object data) {
+ StringBuilder fieldName = new StringBuilder();
+ ObjectHolder value = new ObjectHolder();
+ // Process both sides of this node.
+ Object left = node.jjtGetChild(0).jjtAccept(this, data);
+ Object right = node.jjtGetChild(1).jjtAccept(this, data);
+ // Ignore functions in the query
+ if (left instanceof FunctionResult || right instanceof FunctionResult)
+ return null;
+ decodeResults(left, right, fieldName, value);
+ // We need to check to see if we are in a NOT context. If so,
+ // then we need to reverse the negation.
+ boolean negated = false;
+ if (null != data && data instanceof EvaluationContext) {
+ EvaluationContext ctx = (EvaluationContext) data;
+ if (ctx.inNotContext)
+ negated = !negated;
+ }
+ QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
+ termsCopy.put(fieldName.toString(), term);
+ // Get the terms from the global index
+ // Remove the begin and end ' marks
+ String termValue = null;
+ if (((String) term.getValue()).startsWith("'") && ((String) term.getValue()).endsWith("'"))
+ termValue = ((String) term.getValue()).substring(1, ((String) term.getValue()).length() - 1);
+ else
+ termValue = (String) term.getValue();
+ // Get the values found in the index for this query term
+ TermRange ranges = null;
+ for (MapKey key : this.originalQueryValues.get(termValue)) {
+ if (key.getFieldName().equalsIgnoreCase(fieldName.toString())) {
+ ranges = this.globalIndexResults.get(key);
if (log.isDebugEnabled())
- log.debug("[AND-right] term: " + ctx.lastProcessedTerm + ", cardinality: " + rightCardinality + ", ranges: " + rightRange.getRanges().size());
-
- //reset the state
- if (null != data && !previouslyInAndContext)
- ctx.inAndContext = false;
-
- long card = 0L;
- TermRange andRange = new TermRange("AND_RESULT", "foo");
- if ((leftCardinality > 0 && leftCardinality <= rightCardinality) || rightCardinality == 0) {
- card = leftCardinality;
- andRange.addAll(leftRange.getRanges());
- } else if ((rightCardinality > 0 && rightCardinality <= leftCardinality) || leftCardinality == 0) {
- card = rightCardinality;
- andRange.addAll(rightRange.getRanges());
- }
+ log.debug("Results for cached index ranges for key: " + key + " are " + ranges);
+ }
+ }
+ // If no result for this field name and value, then add empty range
+ if (null == ranges)
+ ranges = new TermRange(fieldName.toString(), (String) term.getValue());
+ if (null != data && data instanceof EvaluationContext) {
+ EvaluationContext ctx = (EvaluationContext) data;
+ ctx.lastRange = ranges;
+ ctx.lastProcessedTerm = fieldName.toString();
+ }
+ return null;
+ }
+
+ @Override
+ public Object visit(ASTLENode node, Object data) {
+ StringBuilder fieldName = new StringBuilder();
+ ObjectHolder value = new ObjectHolder();
+ // Process both sides of this node.
+ Object left = node.jjtGetChild(0).jjtAccept(this, data);
+ Object right = node.jjtGetChild(1).jjtAccept(this, data);
+ // Ignore functions in the query
+ if (left instanceof FunctionResult || right instanceof FunctionResult)
+ return null;
+ decodeResults(left, right, fieldName, value);
+ // We need to check to see if we are in a NOT context. If so,
+ // then we need to reverse the negation.
+ boolean negated = false;
+ if (null != data && data instanceof EvaluationContext) {
+ EvaluationContext ctx = (EvaluationContext) data;
+ if (ctx.inNotContext)
+ negated = !negated;
+ }
+ QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
+ termsCopy.put(fieldName.toString(), term);
+ // Get the terms from the global index
+ // Remove the begin and end ' marks
+ String termValue = null;
+ if (((String) term.getValue()).startsWith("'") && ((String) term.getValue()).endsWith("'"))
+ termValue = ((String) term.getValue()).substring(1, ((String) term.getValue()).length() - 1);
+ else
+ termValue = (String) term.getValue();
+ // Get the values found in the index for this query term
+ TermRange ranges = null;
+ for (MapKey key : this.originalQueryValues.get(termValue)) {
+ if (key.getFieldName().equalsIgnoreCase(fieldName.toString())) {
+ ranges = this.globalIndexResults.get(key);
if (log.isDebugEnabled())
- log.debug("[AND] results: " + andRange.getRanges().toString());
- ctx.lastRange = andRange;
- ctx.lastProcessedTerm = "AND_RESULT";
- this.termCardinalities.put("AND_RESULT", card);
-
- return null;
- }
-
- @Override
- public Object visit(ASTEQNode node, Object data) {
- StringBuilder fieldName = new StringBuilder();
- ObjectHolder value = new ObjectHolder();
- //Process both sides of this node.
- Object left = node.jjtGetChild(0).jjtAccept(this, data);
- Object right = node.jjtGetChild(1).jjtAccept(this, data);
- //Ignore functions in the query
- if (left instanceof FunctionResult || right instanceof FunctionResult)
- return null;
- decodeResults(left, right, fieldName, value);
- //We need to check to see if we are in a NOT context. If so,
- //then we need to reverse the negation.
- boolean negated = false;
- if (null != data && data instanceof EvaluationContext) {
- EvaluationContext ctx = (EvaluationContext) data;
- if (ctx.inNotContext)
- negated = !negated;
- }
- QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
- termsCopy.put(fieldName.toString(), term);
- //Get the terms from the global index
- //Remove the begin and end ' marks
- String termValue = null;
- if (((String) term.getValue()).startsWith("'") && ((String) term.getValue()).endsWith("'"))
- termValue = ((String) term.getValue()).substring(1, ((String) term.getValue()).length() - 1);
- else
- termValue = (String) term.getValue();
- //Get the values found in the index for this query term
- TermRange ranges = null;
- for (MapKey key : this.originalQueryValues.get(termValue)) {
- if (key.getFieldName().equalsIgnoreCase(fieldName.toString())) {
- ranges = this.globalIndexResults.get(key);
- if (log.isDebugEnabled())
- log.debug("Results for cached index ranges for key: " + key + " are " + ranges);
- }
- }
- //If no result for this field name and value, then add empty range
- if (null == ranges)
- ranges = new TermRange(fieldName.toString(), (String) term.getValue());
- if (null != data && data instanceof EvaluationContext) {
- EvaluationContext ctx = (EvaluationContext) data;
- ctx.lastRange = ranges;
- ctx.lastProcessedTerm = fieldName.toString();
- }
- return null;
- }
-
- @Override
- public Object visit(ASTNENode node, Object data) {
- StringBuilder fieldName = new StringBuilder();
- ObjectHolder value = new ObjectHolder();
- //Process both sides of this node.
- Object left = node.jjtGetChild(0).jjtAccept(this, data);
- Object right = node.jjtGetChild(1).jjtAccept(this, data);
- //Ignore functions in the query
- if (left instanceof FunctionResult || right instanceof FunctionResult)
- return null;
- decodeResults(left, right, fieldName, value);
- //We need to check to see if we are in a NOT context. If so,
- //then we need to reverse the negation.
- boolean negated = true;
- if (null != data && data instanceof EvaluationContext) {
- EvaluationContext ctx = (EvaluationContext) data;
- if (ctx.inNotContext)
- negated = !negated;
- }
- if (negated)
- negatedTerms.add(fieldName.toString());
- QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
- termsCopy.put(fieldName.toString(), term);
- //We can only use the global index for equality, put in fake results
- if (null != data && data instanceof EvaluationContext) {
- EvaluationContext ctx = (EvaluationContext) data;
- ctx.lastRange = new TermRange(fieldName.toString(), term.getValue());
- ctx.lastProcessedTerm = fieldName.toString();
- termCardinalities.put(fieldName.toString(), 0L);
- }
- return null;
- }
-
- @Override
- public Object visit(ASTLTNode node, Object data) {
- StringBuilder fieldName = new StringBuilder();
- ObjectHolder value = new ObjectHolder();
- //Process both sides of this node.
- Object left = node.jjtGetChild(0).jjtAccept(this, data);
- Object right = node.jjtGetChild(1).jjtAccept(this, data);
- //Ignore functions in the query
- if (left instanceof FunctionResult || right instanceof FunctionResult)
- return null;
- decodeResults(left, right, fieldName, value);
- //We need to check to see if we are in a NOT context. If so,
- //then we need to reverse the negation.
- boolean negated = false;
- if (null != data && data instanceof EvaluationContext) {
- EvaluationContext ctx = (EvaluationContext) data;
- if (ctx.inNotContext)
- negated = !negated;
- }
- QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
- termsCopy.put(fieldName.toString(), term);
- //Get the terms from the global index
- //Remove the begin and end ' marks
- String termValue = null;
- if (((String) term.getValue()).startsWith("'") && ((String) term.getValue()).endsWith("'"))
- termValue = ((String) term.getValue()).substring(1, ((String) term.getValue()).length() - 1);
- else
- termValue = (String) term.getValue();
- //Get the values found in the index for this query term
- TermRange ranges = null;
- for (MapKey key : this.originalQueryValues.get(termValue)) {
- if (key.getFieldName().equalsIgnoreCase(fieldName.toString())) {
- ranges = this.globalIndexResults.get(key);
- if (log.isDebugEnabled())
- log.debug("Results for cached index ranges for key: " + key + " are " + ranges);
- }
- }
- //If no result for this field name and value, then add empty range
- if (null == ranges)
- ranges = new TermRange(fieldName.toString(), (String) term.getValue());
- if (null != data && data instanceof EvaluationContext) {
- EvaluationContext ctx = (EvaluationContext) data;
- ctx.lastRange = ranges;
- ctx.lastProcessedTerm = fieldName.toString();
- }
- return null;
- }
-
- @Override
- public Object visit(ASTGTNode node, Object data) {
- StringBuilder fieldName = new StringBuilder();
- ObjectHolder value = new ObjectHolder();
- //Process both sides of this node.
- Object left = node.jjtGetChild(0).jjtAccept(this, data);
- Object right = node.jjtGetChild(1).jjtAccept(this, data);
- //Ignore functions in the query
- if (left instanceof FunctionResult || right instanceof FunctionResult)
- return null;
- decodeResults(left, right, fieldName, value);
- //We need to check to see if we are in a NOT context. If so,
- //then we need to reverse the negation.
- boolean negated = false;
- if (null != data && data instanceof EvaluationContext) {
- EvaluationContext ctx = (EvaluationContext) data;
- if (ctx.inNotContext)
- negated = !negated;
- }
- QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
- termsCopy.put(fieldName.toString(), term);
- //Get the terms from the global index
- //Remove the begin and end ' marks
- String termValue = null;
- if (((String) term.getValue()).startsWith("'") && ((String) term.getValue()).endsWith("'"))
- termValue = ((String) term.getValue()).substring(1, ((String) term.getValue()).length() - 1);
- else
- termValue = (String) term.getValue();
- //Get the values found in the index for this query term
- TermRange ranges = null;
- for (MapKey key : this.originalQueryValues.get(termValue)) {
- if (key.getFieldName().equalsIgnoreCase(fieldName.toString())) {
- ranges = this.globalIndexResults.get(key);
- if (log.isDebugEnabled())
- log.debug("Results for cached index ranges for key: " + key + " are " + ranges);
- }
- }
- //If no result for this field name and value, then add empty range
- if (null == ranges)
- ranges = new TermRange(fieldName.toString(), (String) term.getValue());
- if (null != data && data instanceof EvaluationContext) {
- EvaluationContext ctx = (EvaluationContext) data;
- ctx.lastRange = ranges;
- ctx.lastProcessedTerm = fieldName.toString();
- }
- return null;
- }
-
- @Override
- public Object visit(ASTLENode node, Object data) {
- StringBuilder fieldName = new StringBuilder();
- ObjectHolder value = new ObjectHolder();
- //Process both sides of this node.
- Object left = node.jjtGetChild(0).jjtAccept(this, data);
- Object right = node.jjtGetChild(1).jjtAccept(this, data);
- //Ignore functions in the query
- if (left instanceof FunctionResult || right instanceof FunctionResult)
- return null;
- decodeResults(left, right, fieldName, value);
- //We need to check to see if we are in a NOT context. If so,
- //then we need to reverse the negation.
- boolean negated = false;
- if (null != data && data instanceof EvaluationContext) {
- EvaluationContext ctx = (EvaluationContext) data;
- if (ctx.inNotContext)
- negated = !negated;
- }
- QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
- termsCopy.put(fieldName.toString(), term);
- //Get the terms from the global index
- //Remove the begin and end ' marks
- String termValue = null;
- if (((String) term.getValue()).startsWith("'") && ((String) term.getValue()).endsWith("'"))
- termValue = ((String) term.getValue()).substring(1, ((String) term.getValue()).length() - 1);
- else
- termValue = (String) term.getValue();
- //Get the values found in the index for this query term
- TermRange ranges = null;
- for (MapKey key : this.originalQueryValues.get(termValue)) {
- if (key.getFieldName().equalsIgnoreCase(fieldName.toString())) {
- ranges = this.globalIndexResults.get(key);
- if (log.isDebugEnabled())
- log.debug("Results for cached index ranges for key: " + key + " are " + ranges);
- }
- }
- //If no result for this field name and value, then add empty range
- if (null == ranges)
- ranges = new TermRange(fieldName.toString(), (String) term.getValue());
- if (null != data && data instanceof EvaluationContext) {
- EvaluationContext ctx = (EvaluationContext) data;
- ctx.lastRange = ranges;
- ctx.lastProcessedTerm = fieldName.toString();
- }
- return null;
- }
-
- @Override
- public Object visit(ASTGENode node, Object data) {
- StringBuilder fieldName = new StringBuilder();
- ObjectHolder value = new ObjectHolder();
- //Process both sides of this node.
- Object left = node.jjtGetChild(0).jjtAccept(this, data);
- Object right = node.jjtGetChild(1).jjtAccept(this, data);
- //Ignore functions in the query
- if (left instanceof FunctionResult || right instanceof FunctionResult)
- return null;
- decodeResults(left, right, fieldName, value);
- //We need to check to see if we are in a NOT context. If so,
- //then we need to reverse the negation.
- boolean negated = false;
- if (null != data && data instanceof EvaluationContext) {
- EvaluationContext ctx = (EvaluationContext) data;
- if (ctx.inNotContext)
- negated = !negated;
- }
- QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
- termsCopy.put(fieldName.toString(), term);
- //Get the terms from the global index
- //Remove the begin and end ' marks
- String termValue = null;
- if (((String) term.getValue()).startsWith("'") && ((String) term.getValue()).endsWith("'"))
- termValue = ((String) term.getValue()).substring(1, ((String) term.getValue()).length() - 1);
- else
- termValue = (String) term.getValue();
- //Get the values found in the index for this query term
- TermRange ranges = null;
- for (MapKey key : this.originalQueryValues.get(termValue)) {
- if (key.getFieldName().equalsIgnoreCase(fieldName.toString())) {
- ranges = this.globalIndexResults.get(key);
- if (log.isDebugEnabled())
- log.debug("Results for cached index ranges for key: " + key + " are " + ranges);
- }
- }
- //If no result for this field name and value, then add empty range
- if (null == ranges)
- ranges = new TermRange(fieldName.toString(), (String) term.getValue());
- if (null != data && data instanceof EvaluationContext) {
- EvaluationContext ctx = (EvaluationContext) data;
- ctx.lastRange = ranges;
- ctx.lastProcessedTerm = fieldName.toString();
- }
- return null;
- }
-
- @Override
- public Object visit(ASTERNode node, Object data) {
- StringBuilder fieldName = new StringBuilder();
- ObjectHolder value = new ObjectHolder();
- //Process both sides of this node.
- Object left = node.jjtGetChild(0).jjtAccept(this, data);
- Object right = node.jjtGetChild(1).jjtAccept(this, data);
- //Ignore functions in the query
- if (left instanceof FunctionResult || right instanceof FunctionResult)
- return null;
- decodeResults(left, right, fieldName, value);
- //We need to check to see if we are in a NOT context. If so,
- //then we need to reverse the negation.
- boolean negated = false;
- if (null != data && data instanceof EvaluationContext) {
- EvaluationContext ctx = (EvaluationContext) data;
- if (ctx.inNotContext)
- negated = !negated;
- }
- QueryTerm term = new QueryTerm(negated, JexlOperatorConstants.getOperator(node.getClass()), value.getObject());
- termsCopy.put(fieldName.toString(), term);
- //Get the terms from the global index
- //Remove the begin and end ' marks
- String termValue = null;
- if (((String) term.getValue()).startsWith("'") && ((String) term.getValue()).endsWith("'"))
- termValue = ((String) term.getValue()).substring(1, ((String) term.getValue()).length() - 1);
- else
- termValue = (String) term.getValue();
[... 318 lines stripped ...]