/** * Licensed 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 org.deephacks.confit.internal.hbase; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.filter.FilterBase; import org.apache.hadoop.io.WritableUtils; import org.deephacks.confit.internal.hbase.BytesUtils.Reference; import org.deephacks.confit.internal.hbase.BytesUtils.ReferenceList; import org.deephacks.confit.internal.hbase.HBeanKeyValue.HBeanReader; import org.deephacks.confit.internal.hbase.query.RestrictionBuilder; import org.deephacks.confit.internal.hbase.query.RestrictionBuilder.HBaseBetween; import org.deephacks.confit.internal.hbase.query.RestrictionBuilder.HBaseEquals; import org.deephacks.confit.internal.hbase.query.RestrictionBuilder.HBaseGreaterThan; import org.deephacks.confit.internal.hbase.query.RestrictionBuilder.HBaseHas; import org.deephacks.confit.internal.hbase.query.RestrictionBuilder.HBaseIn; import org.deephacks.confit.internal.hbase.query.RestrictionBuilder.HBaseLessThan; import org.deephacks.confit.internal.hbase.query.RestrictionBuilder.HBaseNot; import org.deephacks.confit.internal.hbase.query.RestrictionBuilder.HBaseStringContains; import org.deephacks.confit.internal.hbase.query.RestrictionBuilder.LogicalRestriction; import org.deephacks.confit.internal.hbase.query.RestrictionBuilder.PropertyQualifierRestriction; import org.deephacks.confit.internal.hbase.query.RestrictionBuilder.QualifierRestriction; import org.deephacks.confit.internal.hbase.query.RestrictionType; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * A custom HBase filter that enforce queries and pagination. * * This filter runs on the server side, inside the region server. Hence, this maven project * jar must be deployed on every region server. The easiest way to do this is to put the * jar in the lib directory, or append the jar to HBASE_CLASSPATH. * */ public class MultiKeyValueComparisonFilter extends FilterBase { private static final Log log = LogFactory.getLog(MultiKeyValueComparisonFilter.class); private byte[] sid; private boolean filterRow = true; private boolean filterAllRemaining = false; private List<QualifierRestriction> restrictions = new ArrayList<>(); private int maxResults = Integer.MAX_VALUE; private int matchedRows; private boolean foundColumn = false; public MultiKeyValueComparisonFilter() { } public MultiKeyValueComparisonFilter(byte[] sid) { this.sid = sid; } public void add(QualifierRestriction restriction) { if (restriction instanceof PropertyQualifierRestriction) { PropertyQualifierRestriction propertyRestriction = ((PropertyQualifierRestriction) restriction); int id = propertyRestriction.getId(); if (restriction instanceof HBaseEquals) { restrictions.add(restriction); } else if (restriction instanceof HBaseStringContains) { restrictions.add(restriction); } else if (restriction instanceof HBaseBetween) { restrictions.add(restriction); } else if (restriction instanceof HBaseGreaterThan) { restrictions.add(restriction); } else if (restriction instanceof HBaseLessThan) { restrictions.add(restriction); } else if (restriction instanceof HBaseHas) { restrictions.add(restriction); } else if (restriction instanceof HBaseIn) { HBaseIn in = ((HBaseIn) restriction); List<Object> values = in.getValues(); for (Object value : values) { PropertyQualifierRestriction equal = (PropertyQualifierRestriction) RestrictionBuilder.equal(id, value); if(in.isNot()) { equal.setNot(); } add(equal); } } else { throw new IllegalArgumentException("Could not identify restriction: " + restriction); } } else if(restriction instanceof LogicalRestriction) { if (restriction instanceof HBaseNot) { // support only one logical NOT statement at the moment PropertyQualifierRestriction not = (PropertyQualifierRestriction) ((HBaseNot) restriction).getRestrictions().get(0); not.setNot(); add(not); } throw new UnsupportedOperationException("Not implemented yet"); } else { throw new UnsupportedOperationException("Could not identify restriction: " + restriction); } } @Override public boolean filterAllRemaining() { return (matchedRows - 1) > maxResults || filterAllRemaining; } @Override public boolean filterRow() { return filterRow; } @Override public boolean filterRowKey(byte[] buffer, int offset, int length) { return false; } @Override public ReturnCode filterKeyValue(KeyValue kv) { if (foundColumn) { return ReturnCode.NEXT_ROW; } if(!Arrays.equals(HBeanKeyValue.BEAN_COLUMN_FAMILY, kv.getFamily())) { return ReturnCode.NEXT_COL; } try { HBeanReader hbean = new HBeanReader(kv.getValue()); if (filter(hbean)) { filterRow = true; return ReturnCode.NEXT_ROW; } matchedRows++; foundColumn = true; filterRow = false; return ReturnCode.INCLUDE; } catch (Exception e) { if(log.isErrorEnabled()) { log.error(e.getMessage(), e); } filterAllRemaining = true; filterRow = true; return ReturnCode.NEXT_ROW; } } private boolean filter(HBeanReader hbean) { for (QualifierRestriction restriction : restrictions) { if(restriction instanceof PropertyQualifierRestriction) { PropertyQualifierRestriction propertyRestriction = (PropertyQualifierRestriction) restriction; Object target = hbean.getValue(propertyRestriction.getId()); if (target instanceof Reference) { Reference ref = (Reference) target; if(!propertyRestriction.evaluate(ref.getInstance())) { return true; } } if (target instanceof ReferenceList) { ReferenceList list = (ReferenceList) target; if(!propertyRestriction.evaluate(list.getInstances())) { return true; } } else { if(!propertyRestriction.evaluate(target)) { return true; } } } } return false; } @Override public void reset() { foundColumn = false; filterRow = false; } @Override public void write(DataOutput output) throws IOException { WritableUtils.writeVInt(output, sid.length); output.write(sid); WritableUtils.writeVInt(output, maxResults); WritableUtils.writeVInt(output, restrictions.size()); for (QualifierRestriction restriction : restrictions) { int val = RestrictionType.valueOf(restriction).ordinal(); WritableUtils.writeVInt(output, val); restriction.write(output); } } @Override public void readFields(DataInput input) throws IOException { int sidSize = WritableUtils.readVInt(input); sid = new byte[sidSize]; input.readFully(sid); maxResults = WritableUtils.readVInt(input); int restrictionLength = WritableUtils.readVInt(input); for (int i = 0; i < restrictionLength; i++) { QualifierRestriction restriction = RestrictionType.values()[WritableUtils.readVInt(input)].newInstance(); restriction.readFields(input); restrictions.add(restriction); } } public void setMaxResults(int maxResults) { this.maxResults = maxResults; } }