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

com.googlecode.cqengine.index.unique.UniqueIndex 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.unique;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import com.googlecode.cqengine.attribute.Attribute;
import com.googlecode.cqengine.index.support.AbstractAttributeIndex;
import com.googlecode.cqengine.index.support.Factory;
import com.googlecode.cqengine.index.hash.HashIndex;
import com.googlecode.cqengine.query.Query;
import com.googlecode.cqengine.query.option.QueryOptions;
import com.googlecode.cqengine.query.simple.Equal;
import com.googlecode.cqengine.resultset.ResultSet;
import com.googlecode.cqengine.resultset.iterator.UnmodifiableIterator;

/**
 * An index backed by a {@link ConcurrentHashMap}, which can be more efficient than {@link HashIndex} when used with
 * (and only with) attributes which uniquely identify objects (primary key-type attributes).
 * 

* This type of index does not store a set of objects matching each attribute value, but instead stores only a * single object for each value. This results in faster query performance, and often lower memory usage, but has some * trade-offs. *

* This index will throw an exception if a duplicate object is detected for an existing attribute value. That condition * means however that inconsistencies might already have arisen between this and other indexes as a result of the * application's misuse of this index. *

* Trade-offs: {@code UniqueIndex} versus {@code HashIndex} *

    *
  • * {@code UniqueIndex} will always use less memory than a non-quantized {@code HashIndex} *
  • *
  • * {@code UniqueIndex} will not necessarily use less memory than a quantized {@code HashIndex}, i.e. * configured with a {@link com.googlecode.cqengine.quantizer.Quantizer} *
  • *
  • * In all cases, {@code UniqueIndex} will answer queries faster than a {@code HashIndex} *
  • *
  • * It is important that {@code UniqueIndex} only be used with attributes which uniquely identify objects *
  • *
*

* Supports query types: *

    *
  • * {@link Equal} *
  • *
* * @author Kinz Liu * @author Niall Gallagher */ public class UniqueIndex extends AbstractAttributeIndex { protected static final int INDEX_RETRIEVAL_COST = 25; protected final Factory> indexMapFactory; protected final ConcurrentMap indexMap; /** * Package-private constructor, used by static factory methods. Creates a new UniqueIndex initialized to index the * supplied attribute. * * @param indexMapFactory A factory used to create the main map-based data structure used by the index * @param attribute The attribute on which the index will be built */ protected UniqueIndex(Factory> indexMapFactory, Attribute attribute) { super(attribute, new HashSet>() {{ add(Equal.class); }}); this.indexMapFactory = indexMapFactory; this.indexMap = indexMapFactory.create(); } /** * {@inheritDoc} *

* This index is mutable. * * @return true */ @Override public boolean isMutable() { return true; } @Override public ResultSet retrieve(Query query, QueryOptions queryOptions) { Class queryClass = query.getClass(); if (queryClass.equals(Equal.class)) { final ConcurrentMap indexMap = this.indexMap; final Equal equal = (Equal) query; final O obj = indexMap.get(equal.getValue()); return new ResultSet() { @Override public Iterator iterator() { return new UnmodifiableIterator() { boolean hasNext = (obj != null); @Override public boolean hasNext() { return this.hasNext; } @Override public O next() { this.hasNext=false; return obj; } }; } @Override public boolean contains(O object) { return (object != null && obj != null && object.equals(obj)); } @Override public int size() { return obj == null ? 0 : 1; } @Override public int getRetrievalCost() { return INDEX_RETRIEVAL_COST; } @Override public int getMergeCost() { return obj == null ? 0 : 1; } @Override public void close() { // No op. } }; } throw new IllegalArgumentException("Unsupported query: " + query); } /** * {@inheritDoc} */ @Override public boolean addAll(Collection objects, QueryOptions queryOptions) { boolean modified = false; ConcurrentMap indexMap = this.indexMap; for (O object : objects) { Iterable attributeValues = getAttribute().getValues(object, queryOptions); for (A attributeValue : attributeValues) { O existingValue = indexMap.put(attributeValue, object); if (existingValue != null && !existingValue.equals(object)) { throw new UniqueConstraintViolatedException( "The application has attempted to add a duplicate object to the UniqueIndex on attribute '" + attribute.getAttributeName() + "', potentially causing inconsistencies between indexes. " + "UniqueIndex should not be used with attributes which do not uniquely identify objects. " + "Problematic attribute value: '" + attributeValue + "', " + "problematic duplicate object: " + object); } modified = true; } } return modified; } /** * {@inheritDoc} */ @Override public boolean removeAll(Collection objects, QueryOptions queryOptions) { boolean modified = false; ConcurrentMap indexMap = this.indexMap; for (O object : objects) { Iterable attributeValues = getAttribute().getValues(object, queryOptions); for (A attributeValue : attributeValues) { modified |= (indexMap.remove(attributeValue) != null); } } return modified; } /** * {@inheritDoc} */ @Override public void init(Set collection, QueryOptions queryOptions) { addAll(collection, queryOptions); } /** * {@inheritDoc} */ @Override public void clear(QueryOptions queryOptions) { this.indexMap.clear(); } public static class UniqueConstraintViolatedException extends RuntimeException { public UniqueConstraintViolatedException(String message) { super(message); } } /** * Creates an index map using default settings. */ public static class DefaultIndexMapFactory implements Factory> { @Override public ConcurrentMap create() { return new ConcurrentHashMap(); } } // ---------- Static factory methods to create UniqueIndexes ---------- /** * Creates a new {@link UniqueIndex} 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 UniqueIndex} on this attribute */ public static UniqueIndex onAttribute(Attribute attribute) { return onAttribute(new DefaultIndexMapFactory(), attribute); } /** * Creates a new {@link UniqueIndex} on the specified attribute. *

* @param indexMapFactory A factory used to create the main map-based data structure used by the index * @param attribute The attribute on which the index will be built * @param The type of the object containing the attribute * @return A {@link UniqueIndex} on this attribute */ public static UniqueIndex onAttribute(Factory> indexMapFactory, Attribute attribute) { return new UniqueIndex(indexMapFactory, attribute); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy