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

org.usergrid.persistence.query.ir.SliceNode Maven / Gradle / Ivy

There is a newer version: 0.0.27.1
Show newest version
/*******************************************************************************
 * Copyright 2012 Apigee Corporation
 * 
 * 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.usergrid.persistence.query.ir;

import static org.usergrid.persistence.cassandra.IndexUpdate.indexValueCode;
import static org.usergrid.persistence.cassandra.IndexUpdate.toIndexableValue;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.usergrid.persistence.query.ir.QuerySlice.RangeValue;
import org.usergrid.persistence.schema.CollectionInfo;

/**
 * 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
	 * 
	 * (x > 5 and x < 15 and z > 10 and z < 20) or (y > 10 and y < 20)
	 * 
	 * 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.
	 * 
	 * 
	 * @author tnine
	 * 
	 */
	// private class TreeContext {

	private Map pairs = new HashMap();

	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
	 * 
	 * @param id
	 */
	public SliceNode(int id) {
		this.id = id;
	}

	/**
	 * 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
	 * @param includeEnd
	 */
	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
	 * 
	 * @param fieldName
	 * @param value
	 * @param inclusive
	 */
	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
	 * 
	 * @param fieldName
	 * @return
	 */
	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
	 * 
	 * @param fieldName
	 * @return
	 */
	public QuerySlice getSlice(String fieldName) {
		return this.pairs.get(fieldName);
	}

	/**
	 * Remove this slice by name. Useful when using subkeys
	 * 
	 * @param fieldName
	 */
	public void removeSlice(String fieldName, CollectionInfo info) {
		this.pairs.remove(fieldName);

		// if we're the last prop slice pair to be removed, we want to add a
		// select all to get all results in the slice.

		if (this.pairs.size() == 0) {

			// we've removed everything due to row key joining. We need at least
			// one property to search with
			// so we'll arbitrarily choose the first one. TODO choose something
			// better
			String searchProp = info.getPropertiesIndexed().iterator().next();

			QuerySlice allSlice = new QuerySlice(searchProp, id++);
			allSlice.setStart(null);
			allSlice.setFinish(null);
			this.pairs.put(searchProp, allSlice);
		}

	}

	/**
	 * Get all slices in our context
	 * 
	 * @return
	 */
	public Collection getAllSlices() {
		return this.pairs.values();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.usergrid.persistence.query.ir.QueryNode#visit(org.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 + "]";
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy