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

com.googlecode.cqengine.index.suffix.SuffixTreeIndex Maven / Gradle / Ivy

Go to download

Collection Query Engine: NoSQL indexing and query engine for Java collections with ultra-low latency

There is a newer version: 3.6.0
Show newest version
/**
 * Copyright 2012-2015 Niall Gallagher
 *
 * 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 com.googlecode.cqengine.index.suffix;

import com.googlecode.concurrenttrees.radix.node.concrete.DefaultCharArrayNodeFactory;
import com.googlecode.concurrenttrees.suffix.ConcurrentSuffixTree;
import com.googlecode.concurrenttrees.suffix.SuffixTree;
import com.googlecode.cqengine.attribute.Attribute;
import com.googlecode.cqengine.attribute.SimpleAttribute;
import com.googlecode.cqengine.index.support.AbstractAttributeIndex;
import com.googlecode.cqengine.query.Query;
import com.googlecode.cqengine.query.option.DeduplicationOption;
import com.googlecode.cqengine.query.option.QueryOptions;
import com.googlecode.cqengine.query.simple.Equal;
import com.googlecode.cqengine.query.simple.StringContains;
import com.googlecode.cqengine.query.simple.StringEndsWith;
import com.googlecode.cqengine.resultset.ResultSet;
import com.googlecode.cqengine.resultset.connective.ResultSetUnion;
import com.googlecode.cqengine.resultset.connective.ResultSetUnionAll;
import com.googlecode.cqengine.resultset.stored.StoredResultSet;
import com.googlecode.cqengine.resultset.stored.StoredSetBasedResultSet;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * An index backed by a {@link ConcurrentSuffixTree}.
 * 

* Supports query types: *

    *
  • * {@link Equal} *
  • *
  • * {@link StringEndsWith} *
  • *
  • * {@link StringContains} *
  • *
* * @author Niall Gallagher */ public class SuffixTreeIndex extends AbstractAttributeIndex { private static final int INDEX_RETRIEVAL_COST = 53; private volatile SuffixTree> tree = new ConcurrentSuffixTree>(new DefaultCharArrayNodeFactory()); /** * Package-private constructor, used by static factory methods. Creates a new SuffixTreeIndex initialized to * index the supplied attribute. * * @param attribute The attribute on which the index will be built */ protected SuffixTreeIndex(Attribute attribute) { super(attribute, new HashSet>() {{ add(Equal.class); add(StringEndsWith.class); add(StringContains.class); }}); } @Override public boolean isMutable() { return true; } @Override public ResultSet retrieve(Query query, final QueryOptions queryOptions) { final SuffixTree> tree = this.tree; Class queryClass = query.getClass(); if (queryClass.equals(Equal.class)) { final Equal equal = (Equal) query; return new ResultSet() { @Override public Iterator iterator() { ResultSet rs = tree.getValueForExactKey(equal.getValue()); return rs == null ? Collections.emptySet().iterator() : rs.iterator(); } @Override public boolean contains(O object) { ResultSet rs = tree.getValueForExactKey(equal.getValue()); return rs != null && rs.contains(object); } @Override public int size() { ResultSet rs = tree.getValueForExactKey(equal.getValue()); return rs == null ? 0 : rs.size(); } @Override public int getRetrievalCost() { return INDEX_RETRIEVAL_COST; } @Override public int getMergeCost() { // Return size of entire stored set as merge cost... ResultSet rs = tree.getValueForExactKey(equal.getValue()); return rs == null ? 0 : rs.size(); } @Override public void close() { // No op. } }; } else if (queryClass.equals(StringEndsWith.class)) { final StringEndsWith stringEndsWith = (StringEndsWith) query; return new ResultSet() { @Override public Iterator iterator() { Iterable> resultSets = tree.getValuesForKeysEndingWith(stringEndsWith.getValue()); ResultSet rs = unionResultSets(resultSets, queryOptions); return rs.iterator(); } @Override public boolean contains(O object) { Iterable> resultSets = tree.getValuesForKeysEndingWith(stringEndsWith.getValue()); ResultSet rs = unionResultSets(resultSets, queryOptions); return rs.contains(object); } @Override public int size() { Iterable> resultSets = tree.getValuesForKeysEndingWith(stringEndsWith.getValue()); ResultSet rs = unionResultSets(resultSets, queryOptions); return rs.size(); } @Override public int getRetrievalCost() { return INDEX_RETRIEVAL_COST; } @Override public int getMergeCost() { Iterable> resultSets = tree.getValuesForKeysEndingWith(stringEndsWith.getValue()); ResultSet rs = unionResultSets(resultSets, queryOptions); return rs.getMergeCost(); } @Override public void close() { // No op. } }; } else if (queryClass.equals(StringContains.class)) { final StringContains stringContains = (StringContains) query; return new ResultSet() { @Override public Iterator iterator() { Iterable> resultSets = tree.getValuesForKeysContaining(stringContains.getValue()); ResultSet rs = unionResultSets(resultSets, queryOptions); return rs.iterator(); } @Override public boolean contains(O object) { Iterable> resultSets = tree.getValuesForKeysContaining(stringContains.getValue()); ResultSet rs = unionResultSets(resultSets, queryOptions); return rs.contains(object); } @Override public int size() { Iterable> resultSets = tree.getValuesForKeysContaining(stringContains.getValue()); ResultSet rs = unionResultSets(resultSets, queryOptions); return rs.size(); } @Override public int getRetrievalCost() { return INDEX_RETRIEVAL_COST; } @Override public int getMergeCost() { Iterable> resultSets = tree.getValuesForKeysContaining(stringContains.getValue()); ResultSet rs = unionResultSets(resultSets, queryOptions); return rs.getMergeCost(); } @Override public void close() { // No op. } }; } else { throw new IllegalArgumentException("Unsupported query: " + query); } } /** * If a query option specifying logical deduplication was supplied, wrap the given result sets in * {@link ResultSetUnion}, otherwise wrap in {@link ResultSetUnionAll}. *

* An exception is if the index is built on a SimpleAttribute, we can avoid deduplication and always use * {@link ResultSetUnionAll}, because the same object could not exist in more than one {@link StoredResultSet}. * * @param results Provides the result sets to union * @param queryOptions Specifies whether or not logical deduplication is required * @return A union view over the given result sets */ ResultSet unionResultSets(Iterable> results, QueryOptions queryOptions) { if (DeduplicationOption.isLogicalElimination(queryOptions) && !(getAttribute() instanceof SimpleAttribute)) { return new ResultSetUnion(results, queryOptions) { @Override public int getRetrievalCost() { return INDEX_RETRIEVAL_COST; } }; } else { return new ResultSetUnionAll(results) { @Override public int getRetrievalCost() { return INDEX_RETRIEVAL_COST; } }; } } /** * @return A {@link StoredSetBasedResultSet} based on a set backed by {@link ConcurrentHashMap}, as created via * {@link java.util.Collections#newSetFromMap(java.util.Map)} */ public StoredResultSet createValueSet() { return new StoredSetBasedResultSet(Collections.newSetFromMap(new ConcurrentHashMap())); } /** * {@inheritDoc} */ @Override public boolean addAll(Collection objects, QueryOptions queryOptions) { boolean modified = false; final SuffixTree> tree = this.tree; for (O object : objects) { Iterable attributeValues = getAttribute().getValues(object, queryOptions); for (A attributeValue : attributeValues) { // Look up StoredResultSet for the value... StoredResultSet valueSet = tree.getValueForExactKey(attributeValue); if (valueSet == null) { // No StoredResultSet, create and add one... valueSet = createValueSet(); StoredResultSet existingValueSet = tree.putIfAbsent(attributeValue, valueSet); if (existingValueSet != null) { // Another thread won race to add new value set, use that one... valueSet = existingValueSet; } } // Add the object to the StoredResultSet for this value... modified |= valueSet.add(object); } } return modified; } /** * {@inheritDoc} */ @Override public boolean removeAll(Collection objects, QueryOptions queryOptions) { boolean modified = false; final SuffixTree> tree = this.tree; for (O object : objects) { Iterable attributeValues = getAttribute().getValues(object, queryOptions); for (A attributeValue : attributeValues) { StoredResultSet valueSet = tree.getValueForExactKey(attributeValue); if (valueSet == null) { continue; } modified |= valueSet.remove(object); if (valueSet.isEmpty()) { tree.remove(attributeValue); } } } return modified; } /** * {@inheritDoc} */ @Override public void init(Set collection, QueryOptions queryOptions) { addAll(collection, queryOptions); } /** * {@inheritDoc} */ @Override public void clear(QueryOptions queryOptions) { this.tree = new ConcurrentSuffixTree>(new DefaultCharArrayNodeFactory()); } // ---------- Static factory methods to create SuffixTreeIndexes ---------- /** * Creates a new {@link SuffixTreeIndex} on the specified attribute. *

* @param attribute The attribute on which the index will be built * @param The type of the object containing the attribute * @return A {@link SuffixTreeIndex} on this attribute */ public static SuffixTreeIndex onAttribute(Attribute attribute) { return new SuffixTreeIndex(attribute); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy