/* * 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 org.apache.usergrid.mq; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.commons.collections.comparators.ComparatorChain; import org.apache.usergrid.mq.Query.FilterOperator; import org.apache.usergrid.mq.Query.FilterPredicate; import org.apache.usergrid.mq.Query.SortPredicate; import org.apache.usergrid.persistence.Entity; import org.apache.usergrid.utils.NumberUtils; import static org.apache.usergrid.mq.Query.SortDirection.DESCENDING; public class QueryProcessor { private static final Logger logger = LoggerFactory.getLogger( QueryProcessor.class ); Query query; String cursor; List<QuerySlice> slices; List<FilterPredicate> filters; List<SortPredicate> sorts; public QueryProcessor( Query query ) { this.query = query; cursor = query.getCursor(); filters = query.getFilterPredicates(); sorts = query.getSortPredicates(); process(); } public Query getQuery() { return query; } public String getCursor() { return cursor; } public List<QuerySlice> getSlices() { return slices; } public List<FilterPredicate> getFilters() { return filters; } public List<SortPredicate> getSorts() { return sorts; } private void process() { slices = new ArrayList<QuerySlice>(); throw new UnsupportedOperationException( "query not supported" ); // // consolidate all the filters into a set of ranges // Set<String> names = getFilterPropertyNames(); // for ( String name : names ) { // FilterOperator operator = null; // Object value = null; // RangeValue start = null; // RangeValue finish = null; // for ( FilterPredicate f : filters ) { // if ( f.getPropertyName().equals( name ) ) { // operator = f.getOperator(); // value = f.getValue(); // RangePair r = getRangeForFilter( f ); // if ( r.start != null ) { // if ( ( start == null ) || ( r.start.compareTo( start, false ) < 0 ) ) { // start = r.start; // } // } // if ( r.finish != null ) { // if ( ( finish == null ) || ( r.finish.compareTo( finish, true ) > 0 ) ) { // finish = r.finish; // } // } // } // } // slices.add( new QuerySlice( name, operator, value, start, finish, null, false ) ); // } // // // process sorts // if ( ( slices.size() == 0 ) && ( sorts.size() > 0 ) ) { // // if no filters, turn first filter into a sort // SortPredicate sort = ListUtils.dequeue( sorts ); // slices.add( new QuerySlice( sort.getPropertyName(), null, null, null, null, null, // sort.getDirection() == DESCENDING ) ); // } // else if ( sorts.size() > 0 ) { // // match up sorts with existing filters // for ( ListIterator<SortPredicate> iter = sorts.listIterator(); iter.hasNext(); ) { // SortPredicate sort = iter.next(); // QuerySlice slice = getSliceForProperty( sort.getPropertyName() ); // if ( slice != null ) { // slice.reversed = sort.getDirection() == DESCENDING; // iter.remove(); // } // } // } // // // attach cursors to slices // if ( ( cursor != null ) && ( cursor.indexOf( ':' ) >= 0 ) ) { // String[] cursors = split( cursor, '|' ); // for ( String c : cursors ) { // String[] parts = split( c, ':' ); // if ( parts.length == 2 ) { // int cursorHashCode = parseInt( parts[0] ); // for ( QuerySlice slice : slices ) { // int sliceHashCode = slice.hashCode(); // logger.info( "Comparing cursor hashcode " + cursorHashCode + " to " + sliceHashCode ); // if ( sliceHashCode == cursorHashCode ) { // if ( isNotBlank( parts[1] ) ) { // ByteBuffer cursorBytes = ByteBuffer.wrap( decodeBase64( parts[1] ) ); // slice.setCursor( cursorBytes ); // } // } // } // } // } // } } @SuppressWarnings("unchecked") public List<Entity> sort( List<Entity> entities ) { throw new UnsupportedOperationException( "Unsupported" ); // // if ( ( entities != null ) && ( sorts.size() > 0 ) ) { // // Performing in memory sort // logger.info( "Performing in-memory sort of {} entities", entities.size() ); // ComparatorChain chain = new ComparatorChain(); // for ( SortPredicate sort : sorts ) { // chain.addComparator( // new EntityPropertyComparator( sort.getPropertyName(), sort.getDirection() == DESCENDING ) ); // } // Collections.sort( entities, chain ); // } // return entities; } private Set<String> getFilterPropertyNames() { Set<String> names = new LinkedHashSet<String>(); for ( FilterPredicate f : filters ) { names.add( f.getPropertyName() ); } return names; } public QuerySlice getSliceForProperty( String name ) { for ( QuerySlice s : slices ) { if ( s.propertyName.equals( name ) ) { return s; } } return null; } public static class RangeValue { byte code; Object value; boolean inclusive; public RangeValue( byte code, Object value, boolean inclusive ) { this.code = code; this.value = value; this.inclusive = inclusive; } public byte getCode() { return code; } public void setCode( byte code ) { this.code = code; } public Object getValue() { return value; } public void setValue( Object value ) { this.value = value; } public boolean isInclusive() { return inclusive; } public void setInclusive( boolean inclusive ) { this.inclusive = inclusive; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + code; result = prime * result + ( inclusive ? 1231 : 1237 ); result = prime * result + ( ( value == null ) ? 0 : value.hashCode() ); return result; } @Override public boolean equals( Object obj ) { if ( this == obj ) { return true; } if ( obj == null ) { return false; } if ( getClass() != obj.getClass() ) { return false; } RangeValue other = ( RangeValue ) obj; if ( code != other.code ) { return false; } if ( inclusive != other.inclusive ) { return false; } if ( value == null ) { if ( other.value != null ) { return false; } } else if ( !value.equals( other.value ) ) { return false; } return true; } public int compareTo( RangeValue other, boolean finish ) { if ( other == null ) { return 1; } if ( code != other.code ) { return NumberUtils.sign( code - other.code ); } @SuppressWarnings({ "unchecked", "rawtypes" }) int c = ( ( Comparable ) value ).compareTo( other.value ); if ( c != 0 ) { return c; } if ( finish ) { // for finish values, inclusive means <= which is greater than < if ( inclusive != other.inclusive ) { return inclusive ? 1 : -1; } } else { // for start values, inclusive means >= which is lest than > if ( inclusive != other.inclusive ) { return inclusive ? -1 : 1; } } return 0; } public static int compare( RangeValue v1, RangeValue v2, boolean finish ) { if ( v1 == null ) { if ( v2 == null ) { return 0; } return -1; } return v1.compareTo( v2, finish ); } } public static class RangePair { RangeValue start; RangeValue finish; public RangePair( RangeValue start, RangeValue finish ) { this.start = start; this.finish = finish; } public RangeValue getStart() { return start; } public void setStart( RangeValue start ) { this.start = start; } public RangeValue getFinish() { return finish; } public void setFinish( RangeValue finish ) { this.finish = finish; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ( ( finish == null ) ? 0 : finish.hashCode() ); result = prime * result + ( ( start == null ) ? 0 : start.hashCode() ); return result; } @Override public boolean equals( Object obj ) { if ( this == obj ) { return true; } if ( obj == null ) { return false; } if ( getClass() != obj.getClass() ) { return false; } RangePair other = ( RangePair ) obj; if ( finish == null ) { if ( other.finish != null ) { return false; } } else if ( !finish.equals( other.finish ) ) { return false; } if ( start == null ) { if ( other.start != null ) { return false; } } else if ( !start.equals( other.start ) ) { return false; } return true; } } public static class QuerySlice { String propertyName; FilterOperator operator; Object value; RangeValue start; RangeValue finish; ByteBuffer cursor; boolean reversed; QuerySlice( String propertyName, FilterOperator operator, Object value, RangeValue start, RangeValue finish, ByteBuffer cursor, boolean reversed ) { this.propertyName = propertyName; this.operator = operator; this.value = value; this.start = start; this.finish = finish; this.cursor = cursor; this.reversed = reversed; } public String getPropertyName() { return propertyName; } public void setPropertyName( String propertyName ) { this.propertyName = propertyName; } public RangeValue getStart() { return start; } public void setStart( RangeValue start ) { this.start = start; } public RangeValue getFinish() { return finish; } public void setFinish( RangeValue finish ) { this.finish = finish; } public Object getValue() { return value; } public void setValue( Object value ) { this.value = value; } public ByteBuffer getCursor() { return cursor; } public void setCursor( ByteBuffer cursor ) { this.cursor = cursor; } public boolean isReversed() { return reversed; } public void setReversed( boolean reversed ) { this.reversed = reversed; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ( ( finish == null ) ? 0 : finish.hashCode() ); result = prime * result + ( ( propertyName == null ) ? 0 : propertyName.hashCode() ); result = prime * result + ( ( start == null ) ? 0 : start.hashCode() ); //NOTE. We have explicitly left out direction. According to IndexTest:testCollectionOrdering, // a cursor can be used and change direction //of the ordering. return result; } @Override public boolean equals( Object obj ) { if ( this == obj ) { return true; } if ( obj == null ) { return false; } if ( getClass() != obj.getClass() ) { return false; } QuerySlice other = ( QuerySlice ) obj; if ( finish == null ) { if ( other.finish != null ) { return false; } } else if ( !finish.equals( other.finish ) ) { return false; } if ( propertyName == null ) { if ( other.propertyName != null ) { return false; } } else if ( !propertyName.equals( other.propertyName ) ) { return false; } if ( start == null ) { if ( other.start != null ) { return false; } } else if ( !start.equals( other.start ) ) { return false; } return true; } } }