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

org.elassandra.index.search.TokenRangesService Maven / Gradle / Ivy

There is a newer version: 6.2.3.31
Show newest version
package org.elassandra.index.search;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;

import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.Token;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Query;
import org.elassandra.cluster.routing.AbstractSearchStrategy;
import org.elassandra.index.mapper.internal.TokenFieldMapper;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.EsExecutors;
import org.elasticsearch.index.mapper.NumberFieldMapper;

import java.util.Collection;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;

public class TokenRangesService extends AbstractComponent {

    Queue tokenRangesQueryListeners = new ConcurrentLinkedQueue();

    @Inject
    public TokenRangesService(Settings settings) {
        super(settings);
    }
    
    public interface TokenRangesQueryListener {
        public void onRemoveQuery(Query query);
    }
    
    public void register(TokenRangesQueryListener listener) {
        tokenRangesQueryListeners.add(listener);
    }
    
    public void unregister(TokenRangesQueryListener listener) {
        tokenRangesQueryListeners.remove(listener);
    }
    
    Cache>, Query> tokenRangesQueryCache = CacheBuilder.newBuilder()
            .concurrencyLevel(EsExecutors.numberOfProcessors(settings))
            .expireAfterAccess(Integer.getInteger(ClusterService.SETTING_SYSTEM_TOKEN_RANGES_QUERY_EXPIRE, 5), TimeUnit.MINUTES)
            .removalListener(new RemovalListener>, Query>() {
                @Override
                public void onRemoval(RemovalNotification>, Query> notification) {
                    if (logger.isTraceEnabled())
                        logger.trace("remove tokenRangeQuery={}, cause={}", notification, notification.getCause());
                    for(TokenRangesQueryListener listener : tokenRangesQueryListeners)
                        listener.onRemoveQuery(notification.getValue());
                }
            }).build();
    
    public Query getTokenRangesQuery(Collection> tokenRanges) {
        Query tokenRangesQuery = null;
        if (tokenRanges != null) {
            switch(tokenRanges.size()) {
                case 0:
                    return null;
                case 1:
                    Range unique_range = tokenRanges.iterator().next();
                    if (unique_range.left.equals(AbstractSearchStrategy.TOKEN_MIN) && unique_range.right.equals(AbstractSearchStrategy.TOKEN_MAX))
                        // full search range, so don't add any filter.
                        return null;
                    
                    if (unique_range.left.equals(unique_range.right)) {
                        // partition key search, not cached.
                        return NumberFieldMapper.NumberType.LONG.termQuery(TokenFieldMapper.NAME,unique_range.left);
                    }
                    tokenRangesQuery = tokenRangesQueryCache.getIfPresent(tokenRanges);
                    if (tokenRangesQuery == null) {
                        tokenRangesQuery = newNumericRangesQuery(unique_range);
                        tokenRangesQueryCache.put(tokenRanges, tokenRangesQuery);
                    }
                    if (logger.isTraceEnabled())
                        logger.trace("tokenRangeQuery={}", tokenRangesQuery);
                    return tokenRangesQuery;
                default:
                    tokenRangesQuery = tokenRangesQueryCache.getIfPresent(tokenRanges);
                    if (tokenRangesQuery == null) {
                        BooleanQuery.Builder bq2 = new BooleanQuery.Builder();
                        boolean hasSingleton = false;
                        for (Range range : tokenRanges) {
                            bq2.add(newNumericRangesQuery(range), Occur.SHOULD);
                            if (range.left.equals(range.right))
                                hasSingleton = true;
                        }
                        bq2.setMinimumNumberShouldMatch(1);
                        tokenRangesQuery = bq2.build();
                        if (!hasSingleton)
                            tokenRangesQueryCache.put(tokenRanges, tokenRangesQuery);
                    }
                    if (logger.isTraceEnabled())
                        logger.trace("tokenRangeQuery={}", tokenRangesQuery);
                    return tokenRangesQuery;
            }
        }
        return null;
    }
    
    Query newNumericRangesQuery(Range range) {
        Long left =  (Long) range.left.getTokenValue();
        Long right = (Long) range.right.getTokenValue();
        return (left.equals(right)) ?
            NumberFieldMapper.NumberType.LONG.termQuery(TokenFieldMapper.NAME,left) :
            NumberFieldMapper.NumberType.LONG.rangeQuery(TokenFieldMapper.NAME, 
                left == Long.MIN_VALUE ? null : left,
                right == Long.MAX_VALUE ? null : right,
                false, true, true);
    }
    
    public static boolean tokenRangesIntersec(Collection> shardTokenRanges, Range requestTokenRange) {
        if (requestTokenRange.left.equals(requestTokenRange.right))
            return tokenRangesContains(shardTokenRanges, requestTokenRange.left);
        
        for(Range shardRange : shardTokenRanges) {
            if (shardRange.intersects(requestTokenRange)) 
                return true;
        }
        return false;
    }
    
    // requestTokenRanges may contains singleton
    public static boolean tokenRangesIntersec(Collection> shardTokenRanges, Collection> requestTokenRanges) {
        for(Range requestTokenRange : requestTokenRanges) {
            if (tokenRangesIntersec(shardTokenRanges, requestTokenRange)) 
                return true;
        }
        return false;
    }
    
    public static boolean tokenRangesContains(Collection> shardTokenRanges, Token token) {
        for(Range shardRange : shardTokenRanges) {
            if (shardRange.contains(token)) 
                return true;
        }
        return false;
    }

    // for tests
    public void remove(Query query) {
        tokenRangesQueryCache.invalidate(query);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy