blob: 046b51723b961e8cdd9aef2fe18e1007da2bad4e [file] [log] [blame]
/*
* 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.persistence.query.ir;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.usergrid.persistence.query.ir.QuerySlice.RangeValue;
import static org.apache.usergrid.persistence.cassandra.IndexUpdate.indexValueCode;
import static org.apache.usergrid.persistence.cassandra.IndexUpdate.toIndexableValue;
/**
* A node which has 1 or more query Slices that can be unioned together. I.E and && operation with either 1 or more
* children
*
* @author tnine
*/
public class SliceNode extends QueryNode {
/**
* A context within a tree to allow for operand and range scan optimizations. In the event that the user enters a
* query in the following way
* <p/>
* (x > 5 and x < 15 and z > 10 and z < 20) or (y > 10 and y < 20)
* <p/>
* You will have 2 contexts. The first is for (x > 5 and x < 15 and z > 10 and z < 20), the second is for (y > 10
* and y < 20). This allows us to compress these operations into a single range scan per context.
*/
// private class TreeContext {
private Map<String, QuerySlice> pairs = new LinkedHashMap<String, QuerySlice>();
private int id;
/**
* Set the id for construction. Just a counter. Used for creating tokens and things like tokens where the same
* property can be used in 2 different subtrees
*/
public SliceNode( int id ) {
this.id = id;
}
@Override
public int getCount() {
return pairs.size();
}
/**
* Set the start value. If the range pair doesn't exist, it's created
*
* @param start The start value. this will be processed and turned into an indexed value
*/
public void setStart( String fieldName, Object start, boolean inclusive ) {
QuerySlice slice = getOrCreateSlice( fieldName );
// if the value is null don't set the range on the slice
if ( start == null ) {
return;
}
RangeValue existingStart = slice.getStart();
Object indexedValue = toIndexableValue( start );
byte code = indexValueCode( indexedValue );
RangeValue newStart = new RangeValue( code, indexedValue, inclusive );
if ( existingStart == null ) {
slice.setStart( newStart );
return;
}
// check if we're before the currently set start in this
// context. If so set the value to increase the range scan size;
if ( existingStart != null && newStart == null || ( existingStart != null
&& existingStart.compareTo( newStart, false ) < 0 ) ) {
slice.setStart( newStart );
}
}
/** Set the finish. If finish value is greater than the existing, I.E. null or higher comparison, then */
public void setFinish( String fieldName, Object finish, boolean inclusive ) {
QuerySlice slice = getOrCreateSlice( fieldName );
// if the value is null don't set the range on the slice
if ( finish == null ) {
return;
}
RangeValue existingFinish = slice.getFinish();
Object indexedValue = toIndexableValue( finish );
byte code = indexValueCode( indexedValue );
RangeValue newFinish = new RangeValue( code, indexedValue, inclusive );
if ( existingFinish == null ) {
slice.setFinish( newFinish );
return;
}
// check if we're before the currently set start in this
// context. If so set the value to increase the range scan size;
if ( existingFinish != null && newFinish == null || ( existingFinish != null
&& existingFinish.compareTo( newFinish, false ) < 0 ) ) {
slice.setFinish( newFinish );
}
}
/** Lazy instanciate a field pair if required. Otherwise return the existing pair */
private QuerySlice getOrCreateSlice( String fieldName ) {
QuerySlice pair = this.pairs.get( fieldName );
if ( pair == null ) {
pair = new QuerySlice( fieldName, id );
this.pairs.put( fieldName, pair );
}
return pair;
}
/** Get the slice by field name if it exists. Null otherwise */
public QuerySlice getSlice( String fieldName ) {
return this.pairs.get( fieldName );
}
/** Get all slices in our context */
public Collection<QuerySlice> getAllSlices() {
return this.pairs.values();
}
/*
* (non-Javadoc)
*
* @see
* org.apache.usergrid.persistence.query.ir.QueryNode#visit(org.apache.usergrid.persistence
* .query.ir.NodeVisitor)
*/
@Override
public void visit( NodeVisitor visitor ) throws Exception {
visitor.visit( this );
}
@Override
public String toString() {
return "SliceNode [pairs=" + pairs + ", id=" + id + "]";
}
}