Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
net.sf.ehcache.store.BruteForceSearchManager Maven / Gradle / Ivy
/**
* Copyright Terracotta, Inc.
*
* 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 net.sf.ehcache.store;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.config.ConfigurationHelper;
import net.sf.ehcache.config.SearchAttribute;
import net.sf.ehcache.config.Searchable;
import net.sf.ehcache.search.Attribute;
import net.sf.ehcache.search.Results;
import net.sf.ehcache.search.SearchException;
import net.sf.ehcache.search.aggregator.AggregatorInstance;
import net.sf.ehcache.search.attribute.AttributeExtractor;
import net.sf.ehcache.search.attribute.AttributeExtractorException;
import net.sf.ehcache.search.attribute.AttributeType;
import net.sf.ehcache.search.attribute.DynamicAttributesExtractor;
import net.sf.ehcache.search.expression.Criteria;
import net.sf.ehcache.search.impl.AggregateOnlyResult;
import net.sf.ehcache.search.impl.BaseResult;
import net.sf.ehcache.search.impl.DynamicSearchChecker;
import net.sf.ehcache.search.impl.GroupedResultImpl;
import net.sf.ehcache.search.impl.OrderComparator;
import net.sf.ehcache.search.impl.ResultImpl;
import net.sf.ehcache.search.impl.ResultsImpl;
import net.sf.ehcache.search.impl.SearchManager;
import net.sf.ehcache.transaction.SoftLockID;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import static net.sf.ehcache.search.expression.BaseCriteria.getExtractor;
/**
* Brute force search implementation
*
* @author teck
*/
public class BruteForceSearchManager implements SearchManager {
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
/**
* account for all search attributes
*/
private final Set searchAttributes = new CopyOnWriteArraySet();
private final Ehcache cache;
private BruteForceSource bruteForceSource;
/**
* Create a BruteForceSearchManager
* @param cache
*/
public BruteForceSearchManager(Ehcache cache) {
this.cache = cache;
}
/**
* Concrete search result with relevant inputs to aggregate functions (if any)
*/
private static final class ResultHolder implements Comparable {
private final BaseResult result;
private final List aggregatorInputs;
private final OrderComparator comp;
private ResultHolder(BaseResult res, List values, OrderComparator cmp) {
result = res;
aggregatorInputs = values;
comp = cmp;
}
@Override
public int compareTo(ResultHolder other) {
return comp.compare(this.result, other.result);
}
}
@Override
public Results executeQuery(StoreQuery query, Map extractors, DynamicAttributesExtractor
dynIndexer) {
Criteria c = query.getCriteria();
List> aggregators = query.getAggregatorInstances();
final Set> groupByAttributes = query.groupByAttributes();
final boolean isGroupBy = !groupByAttributes.isEmpty();
boolean includeResults = query.requestsKeys() || query.requestsValues() || !query.requestedAttributes().isEmpty() || isGroupBy;
boolean hasOrder = !query.getOrdering().isEmpty();
final Map, ResultHolder> groupByResults = new HashMap, ResultHolder>();
final Map>> groupByAggregators = new HashMap>>();
Collection matches = new LinkedList();
Map> eltExtractors = new HashMap>();
for (Element element : bruteForceSource.elements()) {
Map extractorSuperset = getCombinedExtractors(extractors, dynIndexer, element);
eltExtractors.put(element.getObjectKey(), extractorSuperset);
if (c.execute(element, extractorSuperset)) {
if (!isGroupBy && !hasOrder && query.maxResults() >= 0 && matches.size() == query.maxResults()) {
break;
}
matches.add(element);
}
}
Collection results = isGroupBy ? groupByResults.values() : new ArrayList();
boolean anyMatches = !matches.isEmpty();
OrderComparator comp = new OrderComparator(query.getOrdering());
for (Element element : matches) {
Map extractorSuperset = eltExtractors.get(element.getObjectKey());
List resultAggs = new ArrayList(aggregators.size());
for (AggregatorInstance> agg: aggregators) {
Attribute aggrAttr = agg.getAttribute();
// placeholder input for count
Object val = aggrAttr != null ?
getExtractor(aggrAttr.getAttributeName(), extractorSuperset).attributeFor(element, aggrAttr.getAttributeName()) : null;
resultAggs.add(val);
}
Map attributes = getAttributeValues(query.requestedAttributes(), extractorSuperset, element);
Object[] sortAttributes = getSortAttributes(query, extractorSuperset, element);
if (!isGroupBy) {
results.add(new ResultHolder(new ResultImpl(element.getObjectKey(), element.getObjectValue(), query, attributes, sortAttributes),
resultAggs, comp));
} else {
Map groupByValues = getAttributeValues(groupByAttributes, extractorSuperset, element);
Set> groupId = new HashSet(groupByValues.values());
List> groupAggrs = groupByAggregators.get(groupId);
if (groupAggrs == null) {
groupAggrs = new ArrayList>(aggregators.size());
for (AggregatorInstance> aggr : aggregators) {
groupAggrs.add(aggr.createClone());
}
groupByAggregators.put(groupId, groupAggrs);
}
int i = 0;
for (AggregatorInstance> inst: groupAggrs) {
inst.accept(resultAggs.get(i++));
}
ResultHolder group = groupByResults.get(groupId);
if (group == null) {
group = new ResultHolder(new GroupedResultImpl(query, attributes, sortAttributes, Collections.emptyList(),
groupByValues), Collections.emptyList(), comp);
groupByResults.put(groupId, group);
}
}
}
if (hasOrder || isGroupBy) {
if (isGroupBy) {
results = new ArrayList(results);
}
if (hasOrder) {
Collections.sort((List)results);
}
// trim results to max length if necessary
int max = query.maxResults();
if (max >= 0 && (results.size() > max)) {
results = ((List)results).subList(0, max);
}
}
if (!aggregators.isEmpty()) {
for (ResultHolder rh : results) {
if (isGroupBy) {
GroupedResultImpl group = (GroupedResultImpl)rh.result;
Set> groupId = new HashSet(group.getGroupByValues().values());
aggregators = groupByAggregators.get(groupId);
setResultAggregators(aggregators, group);
} else {
int i = 0;
for (Object val: rh.aggregatorInputs) {
aggregators.get(i++).accept(val);
}
}
}
if (includeResults && !isGroupBy) {
// Set the same aggregate values for each result
for (ResultHolder rh: results) {
setResultAggregators(aggregators, rh.result);
}
}
}
List output;
if (!isGroupBy && anyMatches && !includeResults && !aggregators.isEmpty()) {
// add one row in the results if the only thing included was aggregators and anything matched
BaseResult aggOnly = new AggregateOnlyResult(query);
setResultAggregators(aggregators, aggOnly);
output = Collections.singletonList(aggOnly);
} else {
output = new ArrayList(results.size());
for (ResultHolder rh: results) {
output.add(rh.result);
}
}
return new ResultsImpl(output, query.requestsKeys(), query.requestsValues(), !query.requestedAttributes().isEmpty(), anyMatches
&& !aggregators.isEmpty());
}
private void setResultAggregators(List> aggregators, BaseResult result)
{
List aggregateResults = new ArrayList();
for (AggregatorInstance> aggregator : aggregators) {
aggregateResults.add(aggregator.aggregateResult());
}
if (!aggregateResults.isEmpty()) {
result.setAggregateResults(aggregateResults);
}
}
private Map getAttributeValues(Set> attributes, Map extractors, Element element) {
final Map values;
if (attributes.isEmpty()) {
values = Collections.emptyMap();
} else {
values = new HashMap();
for (Attribute attribute : attributes) {
String name = attribute.getAttributeName();
values.put(name, getExtractor(name, extractors).attributeFor(element, name));
}
}
return values;
}
private Map getCombinedExtractors(Map configExtractors, DynamicAttributesExtractor
dynIndexer, Element element) {
Map combinedExtractors = new HashMap();
combinedExtractors.putAll(configExtractors);
if (dynIndexer != null) {
Map dynamic = DynamicSearchChecker.getSearchAttributes(element, configExtractors.keySet(),
dynIndexer);
for (final Map.Entry entry: dynamic.entrySet()) {
AttributeExtractor old = combinedExtractors.put(entry.getKey(), new AttributeExtractor() {
@Override
public Object attributeFor(Element element, String attributeName) throws AttributeExtractorException {
if (!attributeName.equals(entry.getKey())) {
throw new AttributeExtractorException(String.format("Expected attribute name %s but got %s", entry.getKey(),
attributeName));
}
return entry.getValue();
}
});
if (old != null) {
throw new AttributeExtractorException(String.format("Attribute name %s already used by configured extractors",
entry.getKey()));
}
}
}
return combinedExtractors;
}
private Object[] getSortAttributes(StoreQuery query, Map extractors, Element element) {
Object[] sortAttributes;
List orderings = query.getOrdering();
if (orderings.isEmpty()) {
sortAttributes = BruteForceSearchManager.EMPTY_OBJECT_ARRAY;
} else {
sortAttributes = new Object[orderings.size()];
for (int i = 0; i < sortAttributes.length; i++) {
String name = orderings.get(i).getAttribute().getAttributeName();
sortAttributes[i] = getExtractor(name, extractors).attributeFor(element, name);
}
}
return sortAttributes;
}
@Override
public void clear(String cacheName, int segmentId) {
throw new UnsupportedOperationException();
}
@Override
public void put(String cacheName, int segmentId, Element element, byte[] key, Map extractors,
DynamicAttributesExtractor dynamicIndexer) {
if (extractors.isEmpty() && dynamicIndexer == null) {
return;
}
boolean isXa = element.getObjectValue() instanceof SoftLockID;
if (isXa) {
SoftLockID sl = (SoftLockID) element.getObjectValue();
element = sl.getOldElement();
// No previous value committed - do not index
if (element == null) { return; }
}
element = bruteForceSource.transformForIndexing(element);
// Handle dynamic attribute extractor, if any
Map dynAttrs = DynamicSearchChecker.getSearchAttributes(element, extractors.keySet(),
dynamicIndexer);
Set> attrs = new HashSet>(dynAttrs.size());
for (Map.Entry attr : dynAttrs.entrySet()) {
if (!AttributeType.isSupportedType(attr.getValue())) {
throw new CacheException(String.format("Unsupported attribute type specified %s for dynamically extracted attribute %s",
attr.getClass().getName(), attr.getKey()));
}
attrs.add(new Attribute(attr.getKey()));
}
Searchable config = bruteForceSource.getSearchable();
if (config == null) { return; }
for (Map.Entry entry : extractors.entrySet()) {
String name = entry.getKey();
SearchAttribute sa = config.getSearchAttributes().get(name);
Class> c = ConfigurationHelper.getSearchAttributeType(sa, cache.getCacheConfiguration().getClassLoader());
if (c == null) { continue; }
AttributeExtractor extractor = entry.getValue();
Object av = extractor.attributeFor(element, name);
AttributeType schemaType = AttributeType.typeFor(c);
AttributeType type = AttributeType.typeFor(name, av);
String schemaTypeName = c.isEnum() ? c.getName() : schemaType.name();
String typeName = AttributeType.ENUM == type ? ((Enum) av).getDeclaringClass().getName() : type.name();
if (!typeName.equals(schemaTypeName)) { throw new SearchException(
String
.format("Expecting a %s value for attribute [%s] but was %s",
schemaTypeName, name, typeName));
}
}
searchAttributes.addAll(attrs);
}
@Override
public void remove(String cacheName, Object key, int segmentId, boolean isRemoval) {
throw new UnsupportedOperationException();
}
@Override
public Set getSearchAttributes(String cacheName) {
return searchAttributes;
}
/**
* Sets the BruteForceSource to be used for search
*
* @param bruteForceSource the source
*/
public void setBruteForceSource(BruteForceSource bruteForceSource) {
this.bruteForceSource = bruteForceSource;
}
/**
* Add search attributes
*
* @param attributeSet the search attributes to add
*/
void addSearchAttributes(Set> attributeSet) {
searchAttributes.addAll(attributeSet);
}
}