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

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

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

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.lucene.index.IndexReaderContext;
import org.apache.lucene.index.LeafReader;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.ReaderUtil;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.FilteredDocIdSetIterator;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Scorer;
import org.apache.lucene.search.Weight;
import org.apache.lucene.search.join.BitSetProducer;
import org.apache.lucene.util.Accountable;
import org.apache.lucene.util.BitSet;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.RamUsageEstimator;

import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.stream.Collectors;

/**
 * A {@link BitSetProducer} that wraps a query and caches matching
 * {@link BitSet}s per segment.
 */
public class TokenRangesBitsetProducer implements BitSetProducer, Accountable {
    private static final Logger logger = LogManager.getLogger(TokenRangesBitsetProducer.class);
    
    //memory usage of a simple term query
    static final long QUERY_DEFAULT_RAM_BYTES_USED = 192;

    static final long HASHTABLE_RAM_BYTES_PER_ENTRY =
     2 * RamUsageEstimator.NUM_BYTES_OBJECT_REF // key + value
     * 2; // hash tables need to be oversized to avoid collisions, assume 2x capacity

    static class Value implements Accountable {
        int tombestones;
        BitSet bitset;
        
        Value(int tombestones, BitSet bitset) {
            this.tombestones = tombestones;
            this.bitset = bitset;
        }

        @Override
        public long ramBytesUsed() {
            return HASHTABLE_RAM_BYTES_PER_ENTRY + (bitset==null ? 0 : bitset.ramBytesUsed());
        }
    
        @Override
        public Collection getChildResources() {
            return null;
        }
  }
  
  private final TokenRangesBitsetFilterCache bitsetFilterCache;
  private final Query query;
  private final Map leafCache;

  
  /** Wraps another query's result and caches it into bitsets.
   * @param query Query to cache results of
   */
  public TokenRangesBitsetProducer(TokenRangesBitsetFilterCache bitsetFilterCache, Query query) {
    this.bitsetFilterCache = bitsetFilterCache;
    this.query = query;
    this.leafCache = Collections.synchronizedMap(new WeakHashMap());
                    //new ConcurrentReferenceHashMap(10, 0.9f, 1, ReferenceType.WEAK);
    this.bitsetFilterCache.listener.onCache(this.bitsetFilterCache.shardId, this);
  }

  /**
   * Gets the contained query.
   * @return the contained query.
   */
  public Query getQuery() {
    return query;
  }
  
  public void remove(Object coreCacheKey) {
      if (logger.isTraceEnabled())
          logger.trace("query={} coreCacheKey={} removed", query, coreCacheKey);
      Value value = leafCache.remove(coreCacheKey);
      if (value != null) 
          this.bitsetFilterCache.listener.onRemoval(this.bitsetFilterCache.shardId, value);
  }
  
  public void clear() {
      for(Value value : leafCache.values())
          this.bitsetFilterCache.listener.onRemoval(this.bitsetFilterCache.shardId, value);
      leafCache.clear();
  }
  
  @Override
  public BitSet getBitSet(LeafReaderContext context) throws IOException {
    final LeafReader reader = context.reader();
    final Object key = reader.getCoreCacheKey();

    Value value = leafCache.get(key);
    BitSet bitset = value == null ? null : value.bitset;
    if (value == null || value.tombestones < reader.numDeletedDocs()) {
      final IndexReaderContext topLevelContext = ReaderUtil.getTopLevelContext(context);
      final IndexSearcher searcher = new IndexSearcher(topLevelContext);
      searcher.setQueryCache(null);
      final Weight weight = searcher.createNormalizedWeight(query, false);
      final Scorer s = weight.scorer(context);
      int tombestones = 0;
      if (s != null) {
          final DocIdSetIterator it = s.iterator();
          final Bits liveDocs = reader.getLiveDocs();
          if (liveDocs == null) {
              // visible docs = query result
              bitset = BitSet.of(it, reader.maxDoc());
              if (logger.isTraceEnabled())
                  logger.trace("no tombstone, query={} coreCacheKey={} segment={} cardinality={}", query, key, reader, bitset.cardinality());
          } else  {
              // visible docs = query result AND liveDocs.
              tombestones = reader.numDeletedDocs();
              DocIdSetIterator fit = new FilteredDocIdSetIterator(it) {
                @Override
                protected boolean match(int doc) {
                    return liveDocs.get(doc);
                }
              };
              bitset = BitSet.of(fit, reader.maxDoc());
              if (logger.isTraceEnabled())
                  logger.trace("has tombstones, query={} coreCacheKey={} segment={} cardinality={}", query, key, reader, bitset.cardinality());
          }
      } else {
           bitset = null; // no visible docs.
           if (logger.isTraceEnabled())
              logger.trace("no matching doc, query={} coreCacheKey={} segment={} cardinality=0 ", query, key, reader);
      }
      Value newValue = new Value(tombestones, bitset);
      Value oldValue = leafCache.put(key, newValue);
      if (oldValue != null)
          this.bitsetFilterCache.listener.onRemoval(this.bitsetFilterCache.shardId, oldValue);
      this.bitsetFilterCache.listener.onCache(this.bitsetFilterCache.shardId, newValue);
    }
    return bitset;
  }
  
  @Override
  public String toString() {
    return "TokenRangesBitsetProducer("+query.toString()+":"+leafCache+")";
  }

  @Override
  public boolean equals(Object o) {
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    final TokenRangesBitsetProducer other = (TokenRangesBitsetProducer) o;
    return this.query.equals(other.query);
  }

    @Override
    public int hashCode() {
      return 31 * getClass().hashCode() + query.hashCode();
    }

    
    @Override
    public long ramBytesUsed() {
        return QUERY_DEFAULT_RAM_BYTES_USED + leafCache.values().stream().collect(Collectors.summingLong(m -> m.ramBytesUsed()));
    }
    
    @Override
    public Collection getChildResources() {
        return null;
    }

}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy