|  | @@ -16,10 +16,14 @@ import org.apache.lucene.search.ScoreDoc;
 | 
	
		
			
				|  |  |  import org.apache.lucene.search.ScoreMode;
 | 
	
		
			
				|  |  |  import org.apache.lucene.search.Sort;
 | 
	
		
			
				|  |  |  import org.apache.lucene.search.SortField;
 | 
	
		
			
				|  |  | +import org.apache.lucene.search.SortedNumericSortField;
 | 
	
		
			
				|  |  | +import org.apache.lucene.search.SortedSetSortField;
 | 
	
		
			
				|  |  |  import org.apache.lucene.search.TopDocsCollector;
 | 
	
		
			
				|  |  |  import org.apache.lucene.search.TopFieldCollectorManager;
 | 
	
		
			
				|  |  |  import org.apache.lucene.search.TopScoreDocCollectorManager;
 | 
	
		
			
				|  |  | +import org.apache.lucene.util.RamUsageEstimator;
 | 
	
		
			
				|  |  |  import org.elasticsearch.common.Strings;
 | 
	
		
			
				|  |  | +import org.elasticsearch.common.breaker.CircuitBreaker;
 | 
	
		
			
				|  |  |  import org.elasticsearch.compute.data.BlockFactory;
 | 
	
		
			
				|  |  |  import org.elasticsearch.compute.data.DocBlock;
 | 
	
		
			
				|  |  |  import org.elasticsearch.compute.data.DocVector;
 | 
	
	
		
			
				|  | @@ -44,7 +48,11 @@ import java.util.function.Function;
 | 
	
		
			
				|  |  |  import java.util.stream.Collectors;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /**
 | 
	
		
			
				|  |  | - * Source operator that builds Pages out of the output of a TopFieldCollector (aka TopN)
 | 
	
		
			
				|  |  | + * Source operator that builds Pages out of the output of a TopFieldCollector (aka TopN).
 | 
	
		
			
				|  |  | + * <p>
 | 
	
		
			
				|  |  | + *     Makes {@link Page}s of the shape {@code (docBlock)} or {@code (docBlock, score)}.
 | 
	
		
			
				|  |  | + *     Lucene loads the sort keys, but we don't read them from lucene. Yet. We should.
 | 
	
		
			
				|  |  | + * </p>
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  |  public final class LuceneTopNSourceOperator extends LuceneOperator {
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -52,6 +60,7 @@ public final class LuceneTopNSourceOperator extends LuceneOperator {
 | 
	
		
			
				|  |  |          private final List<? extends ShardContext> contexts;
 | 
	
		
			
				|  |  |          private final int maxPageSize;
 | 
	
		
			
				|  |  |          private final List<SortBuilder<?>> sorts;
 | 
	
		
			
				|  |  | +        private final long estimatedPerRowSortSize;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          public Factory(
 | 
	
		
			
				|  |  |              List<? extends ShardContext> contexts,
 | 
	
	
		
			
				|  | @@ -61,6 +70,7 @@ public final class LuceneTopNSourceOperator extends LuceneOperator {
 | 
	
		
			
				|  |  |              int maxPageSize,
 | 
	
		
			
				|  |  |              int limit,
 | 
	
		
			
				|  |  |              List<SortBuilder<?>> sorts,
 | 
	
		
			
				|  |  | +            long estimatedPerRowSortSize,
 | 
	
		
			
				|  |  |              boolean needsScore
 | 
	
		
			
				|  |  |          ) {
 | 
	
		
			
				|  |  |              super(
 | 
	
	
		
			
				|  | @@ -76,11 +86,22 @@ public final class LuceneTopNSourceOperator extends LuceneOperator {
 | 
	
		
			
				|  |  |              this.contexts = contexts;
 | 
	
		
			
				|  |  |              this.maxPageSize = maxPageSize;
 | 
	
		
			
				|  |  |              this.sorts = sorts;
 | 
	
		
			
				|  |  | +            this.estimatedPerRowSortSize = estimatedPerRowSortSize;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          @Override
 | 
	
		
			
				|  |  |          public SourceOperator get(DriverContext driverContext) {
 | 
	
		
			
				|  |  | -            return new LuceneTopNSourceOperator(contexts, driverContext.blockFactory(), maxPageSize, sorts, limit, sliceQueue, needsScore);
 | 
	
		
			
				|  |  | +            return new LuceneTopNSourceOperator(
 | 
	
		
			
				|  |  | +                contexts,
 | 
	
		
			
				|  |  | +                driverContext.breaker(),
 | 
	
		
			
				|  |  | +                driverContext.blockFactory(),
 | 
	
		
			
				|  |  | +                maxPageSize,
 | 
	
		
			
				|  |  | +                sorts,
 | 
	
		
			
				|  |  | +                estimatedPerRowSortSize,
 | 
	
		
			
				|  |  | +                limit,
 | 
	
		
			
				|  |  | +                sliceQueue,
 | 
	
		
			
				|  |  | +                needsScore
 | 
	
		
			
				|  |  | +            );
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          public int maxPageSize() {
 | 
	
	
		
			
				|  | @@ -104,10 +125,16 @@ public final class LuceneTopNSourceOperator extends LuceneOperator {
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    private final CircuitBreaker breaker;
 | 
	
		
			
				|  |  | +    private final List<SortBuilder<?>> sorts;
 | 
	
		
			
				|  |  | +    private final long estimatedPerRowSortSize;
 | 
	
		
			
				|  |  | +    private final int limit;
 | 
	
		
			
				|  |  | +    private final boolean needsScore;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      /**
 | 
	
		
			
				|  |  | -     * Collected docs. {@code null} until we're {@link #emit(boolean)}.
 | 
	
		
			
				|  |  | +     * Collected docs. {@code null} until we're ready to {@link #emit()}.
 | 
	
		
			
				|  |  |       */
 | 
	
		
			
				|  |  | -    private ScoreDoc[] scoreDocs;
 | 
	
		
			
				|  |  | +    private ScoreDoc[] topDocs;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      /**
 | 
	
		
			
				|  |  |       * {@link ShardRefCounted} for collected docs.
 | 
	
	
		
			
				|  | @@ -115,28 +142,30 @@ public final class LuceneTopNSourceOperator extends LuceneOperator {
 | 
	
		
			
				|  |  |      private ShardRefCounted shardRefCounted;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      /**
 | 
	
		
			
				|  |  | -     * The offset in {@link #scoreDocs} of the next page.
 | 
	
		
			
				|  |  | +     * The offset in {@link #topDocs} of the next page.
 | 
	
		
			
				|  |  |       */
 | 
	
		
			
				|  |  |      private int offset = 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      private PerShardCollector perShardCollector;
 | 
	
		
			
				|  |  | -    private final List<SortBuilder<?>> sorts;
 | 
	
		
			
				|  |  | -    private final int limit;
 | 
	
		
			
				|  |  | -    private final boolean needsScore;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public LuceneTopNSourceOperator(
 | 
	
		
			
				|  |  |          List<? extends ShardContext> contexts,
 | 
	
		
			
				|  |  | +        CircuitBreaker breaker,
 | 
	
		
			
				|  |  |          BlockFactory blockFactory,
 | 
	
		
			
				|  |  |          int maxPageSize,
 | 
	
		
			
				|  |  |          List<SortBuilder<?>> sorts,
 | 
	
		
			
				|  |  | +        long estimatedPerRowSortSize,
 | 
	
		
			
				|  |  |          int limit,
 | 
	
		
			
				|  |  |          LuceneSliceQueue sliceQueue,
 | 
	
		
			
				|  |  |          boolean needsScore
 | 
	
		
			
				|  |  |      ) {
 | 
	
		
			
				|  |  |          super(contexts, blockFactory, maxPageSize, sliceQueue);
 | 
	
		
			
				|  |  | +        this.breaker = breaker;
 | 
	
		
			
				|  |  |          this.sorts = sorts;
 | 
	
		
			
				|  |  | +        this.estimatedPerRowSortSize = estimatedPerRowSortSize;
 | 
	
		
			
				|  |  |          this.limit = limit;
 | 
	
		
			
				|  |  |          this.needsScore = needsScore;
 | 
	
		
			
				|  |  | +        breaker.addEstimateBytesAndMaybeBreak(reserveSize(), "esql lucene topn");
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @Override
 | 
	
	
		
			
				|  | @@ -147,7 +176,7 @@ public final class LuceneTopNSourceOperator extends LuceneOperator {
 | 
	
		
			
				|  |  |      @Override
 | 
	
		
			
				|  |  |      public void finish() {
 | 
	
		
			
				|  |  |          doneCollecting = true;
 | 
	
		
			
				|  |  | -        scoreDocs = null;
 | 
	
		
			
				|  |  | +        topDocs = null;
 | 
	
		
			
				|  |  |          shardRefCounted = null;
 | 
	
		
			
				|  |  |          assert isFinished();
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -160,7 +189,7 @@ public final class LuceneTopNSourceOperator extends LuceneOperator {
 | 
	
		
			
				|  |  |          long start = System.nanoTime();
 | 
	
		
			
				|  |  |          try {
 | 
	
		
			
				|  |  |              if (isEmitting()) {
 | 
	
		
			
				|  |  | -                return emit(false);
 | 
	
		
			
				|  |  | +                return emit();
 | 
	
		
			
				|  |  |              } else {
 | 
	
		
			
				|  |  |                  return collect();
 | 
	
		
			
				|  |  |              }
 | 
	
	
		
			
				|  | @@ -174,7 +203,8 @@ public final class LuceneTopNSourceOperator extends LuceneOperator {
 | 
	
		
			
				|  |  |          var scorer = getCurrentOrLoadNextScorer();
 | 
	
		
			
				|  |  |          if (scorer == null) {
 | 
	
		
			
				|  |  |              doneCollecting = true;
 | 
	
		
			
				|  |  | -            return emit(true);
 | 
	
		
			
				|  |  | +            startEmitting();
 | 
	
		
			
				|  |  | +            return emit();
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          try {
 | 
	
		
			
				|  |  |              if (scorer.tags().isEmpty() == false) {
 | 
	
	
		
			
				|  | @@ -193,32 +223,47 @@ public final class LuceneTopNSourceOperator extends LuceneOperator {
 | 
	
		
			
				|  |  |          if (scorer.isDone()) {
 | 
	
		
			
				|  |  |              var nextScorer = getCurrentOrLoadNextScorer();
 | 
	
		
			
				|  |  |              if (nextScorer == null || nextScorer.shardContext().index() != scorer.shardContext().index()) {
 | 
	
		
			
				|  |  | -                return emit(true);
 | 
	
		
			
				|  |  | +                startEmitting();
 | 
	
		
			
				|  |  | +                return emit();
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          return null;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      private boolean isEmitting() {
 | 
	
		
			
				|  |  | -        return scoreDocs != null && offset < scoreDocs.length;
 | 
	
		
			
				|  |  | +        return topDocs != null;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    private Page emit(boolean startEmitting) {
 | 
	
		
			
				|  |  | -        if (startEmitting) {
 | 
	
		
			
				|  |  | -            assert isEmitting() == false : "offset=" + offset + " score_docs=" + Arrays.toString(scoreDocs);
 | 
	
		
			
				|  |  | -            offset = 0;
 | 
	
		
			
				|  |  | -            if (perShardCollector != null) {
 | 
	
		
			
				|  |  | -                scoreDocs = perShardCollector.collector.topDocs().scoreDocs;
 | 
	
		
			
				|  |  | -                int shardId = perShardCollector.shardContext.index();
 | 
	
		
			
				|  |  | -                shardRefCounted = new ShardRefCounted.Single(shardId, shardContextCounters.get(shardId));
 | 
	
		
			
				|  |  | -            } else {
 | 
	
		
			
				|  |  | -                scoreDocs = new ScoreDoc[0];
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | +    private void startEmitting() {
 | 
	
		
			
				|  |  | +        assert isEmitting() == false : "offset=" + offset + " score_docs=" + Arrays.toString(topDocs);
 | 
	
		
			
				|  |  | +        offset = 0;
 | 
	
		
			
				|  |  | +        if (perShardCollector != null) {
 | 
	
		
			
				|  |  | +            /*
 | 
	
		
			
				|  |  | +             * Important note for anyone who looks at this and has bright ideas:
 | 
	
		
			
				|  |  | +             * There *is* a method in lucene to return topDocs with an offset
 | 
	
		
			
				|  |  | +             * and a limit. So you'd *think* you can scroll the top docs there.
 | 
	
		
			
				|  |  | +             * But you can't. It's expressly forbidden to call any of the `topDocs`
 | 
	
		
			
				|  |  | +             * methods more than once. You *must* call `topDocs` once and use the
 | 
	
		
			
				|  |  | +             * array.
 | 
	
		
			
				|  |  | +             */
 | 
	
		
			
				|  |  | +            topDocs = perShardCollector.collector.topDocs().scoreDocs;
 | 
	
		
			
				|  |  | +            int shardId = perShardCollector.shardContext.index();
 | 
	
		
			
				|  |  | +            shardRefCounted = new ShardRefCounted.Single(shardId, shardContextCounters.get(shardId));
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +            topDocs = new ScoreDoc[0];
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -        if (offset >= scoreDocs.length) {
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private void stopEmitting() {
 | 
	
		
			
				|  |  | +        topDocs = null;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private Page emit() {
 | 
	
		
			
				|  |  | +        if (offset >= topDocs.length) {
 | 
	
		
			
				|  |  | +            stopEmitting();
 | 
	
		
			
				|  |  |              return null;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -        int size = Math.min(maxPageSize, scoreDocs.length - offset);
 | 
	
		
			
				|  |  | +        int size = Math.min(maxPageSize, topDocs.length - offset);
 | 
	
		
			
				|  |  |          IntBlock shard = null;
 | 
	
		
			
				|  |  |          IntVector segments = null;
 | 
	
		
			
				|  |  |          IntVector docs = null;
 | 
	
	
		
			
				|  | @@ -234,14 +279,16 @@ public final class LuceneTopNSourceOperator extends LuceneOperator {
 | 
	
		
			
				|  |  |              offset += size;
 | 
	
		
			
				|  |  |              List<LeafReaderContext> leafContexts = perShardCollector.shardContext.searcher().getLeafContexts();
 | 
	
		
			
				|  |  |              for (int i = start; i < offset; i++) {
 | 
	
		
			
				|  |  | -                int doc = scoreDocs[i].doc;
 | 
	
		
			
				|  |  | +                int doc = topDocs[i].doc;
 | 
	
		
			
				|  |  |                  int segment = ReaderUtil.subIndex(doc, leafContexts);
 | 
	
		
			
				|  |  |                  currentSegmentBuilder.appendInt(segment);
 | 
	
		
			
				|  |  |                  currentDocsBuilder.appendInt(doc - leafContexts.get(segment).docBase); // the offset inside the segment
 | 
	
		
			
				|  |  |                  if (currentScoresBuilder != null) {
 | 
	
		
			
				|  |  | -                    float score = getScore(scoreDocs[i]);
 | 
	
		
			
				|  |  | +                    float score = getScore(topDocs[i]);
 | 
	
		
			
				|  |  |                      currentScoresBuilder.appendDouble(score);
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  | +                // Null the top doc so it can be GCed early, just in case.
 | 
	
		
			
				|  |  | +                topDocs[i] = null;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              int shardId = perShardCollector.shardContext.index();
 | 
	
	
		
			
				|  | @@ -298,9 +345,20 @@ public final class LuceneTopNSourceOperator extends LuceneOperator {
 | 
	
		
			
				|  |  |          sb.append(", sorts = [").append(notPrettySorts).append("]");
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    @Override
 | 
	
		
			
				|  |  | +    protected void additionalClose() {
 | 
	
		
			
				|  |  | +        Releasables.close(() -> breaker.addWithoutBreaking(-reserveSize()));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private long reserveSize() {
 | 
	
		
			
				|  |  | +        long perRowSize = FIELD_DOC_SIZE + estimatedPerRowSortSize;
 | 
	
		
			
				|  |  | +        return limit * perRowSize;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      abstract static class PerShardCollector {
 | 
	
		
			
				|  |  |          private final ShardContext shardContext;
 | 
	
		
			
				|  |  |          private final TopDocsCollector<?> collector;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          private int leafIndex;
 | 
	
		
			
				|  |  |          private LeafCollector leafCollector;
 | 
	
		
			
				|  |  |          private Thread currentThread;
 | 
	
	
		
			
				|  | @@ -366,4 +424,68 @@ public final class LuceneTopNSourceOperator extends LuceneOperator {
 | 
	
		
			
				|  |  |          sort = new Sort(l.toArray(SortField[]::new));
 | 
	
		
			
				|  |  |          return new ScoringPerShardCollector(context, new TopFieldCollectorManager(sort, limit, null, 0).newCollector());
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private static int perDocMemoryUsage(SortField[] sorts) {
 | 
	
		
			
				|  |  | +        int usage = FIELD_DOC_SIZE;
 | 
	
		
			
				|  |  | +        for (SortField sort : sorts) {
 | 
	
		
			
				|  |  | +            usage += perDocMemoryUsage(sort);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        return usage;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private static int perDocMemoryUsage(SortField sort) {
 | 
	
		
			
				|  |  | +        if (sort.getType() == SortField.Type.CUSTOM) {
 | 
	
		
			
				|  |  | +            return perDocMemoryUsageForCustom(sort);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        return perDocMemoryUsageByType(sort, sort.getType());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private static int perDocMemoryUsageByType(SortField sort, SortField.Type type) {
 | 
	
		
			
				|  |  | +        return switch (type) {
 | 
	
		
			
				|  |  | +            case SCORE, DOC ->
 | 
	
		
			
				|  |  | +                /* SCORE and DOC are always part of ScoreDoc/FieldDoc
 | 
	
		
			
				|  |  | +                 * So they are in FIELD_DOC_SIZE already.
 | 
	
		
			
				|  |  | +                 * And they can't be removed. */
 | 
	
		
			
				|  |  | +                0;
 | 
	
		
			
				|  |  | +            case DOUBLE, LONG ->
 | 
	
		
			
				|  |  | +                // 8 for the long, 8 for the long copied to the topDoc.
 | 
	
		
			
				|  |  | +                16;
 | 
	
		
			
				|  |  | +            case INT, FLOAT ->
 | 
	
		
			
				|  |  | +                // 4 for the int, 8 boxed object copied to topDoc.
 | 
	
		
			
				|  |  | +                12;
 | 
	
		
			
				|  |  | +            case STRING ->
 | 
	
		
			
				|  |  | +                /* `keyword`-like fields. Compares ordinals when possible, otherwise
 | 
	
		
			
				|  |  | +                 * the strings. Does a bunch of deduplication, but in the worst
 | 
	
		
			
				|  |  | +                 * case we end up with the string itself, plus two BytesRefs. Let's
 | 
	
		
			
				|  |  | +                 * presume short-ish strings. */
 | 
	
		
			
				|  |  | +                1024;
 | 
	
		
			
				|  |  | +            case STRING_VAL ->
 | 
	
		
			
				|  |  | +                /* Other string fields. Compares the string itself. Let's assume two
 | 
	
		
			
				|  |  | +                 * 2kb per string because they tend to be bigger than the keyword
 | 
	
		
			
				|  |  | +                 * versions. */
 | 
	
		
			
				|  |  | +                2048;
 | 
	
		
			
				|  |  | +            case CUSTOM -> throw new IllegalArgumentException("unsupported type " + sort.getClass() + ": " + sort);
 | 
	
		
			
				|  |  | +            case REWRITEABLE -> {
 | 
	
		
			
				|  |  | +                assert false : "rewriteable  " + sort.getClass() + ": " + sort;
 | 
	
		
			
				|  |  | +                yield 2048;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        };
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private static int perDocMemoryUsageForCustom(SortField sort) {
 | 
	
		
			
				|  |  | +        return switch (sort) {
 | 
	
		
			
				|  |  | +            case SortedNumericSortField f -> perDocMemoryUsageByType(f, f.getNumericType());
 | 
	
		
			
				|  |  | +            case SortedSetSortField f -> perDocMemoryUsageByType(f, SortField.Type.STRING);
 | 
	
		
			
				|  |  | +            default -> {
 | 
	
		
			
				|  |  | +                if (sort.getClass().getName().equals("org.apache.lucene.document.LatLonPointSortField")) {
 | 
	
		
			
				|  |  | +                    yield perDocMemoryUsageByType(sort, SortField.Type.DOUBLE);
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                assert false : "unknown type " + sort.getClass() + ": " + sort;
 | 
	
		
			
				|  |  | +                yield 2048;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        };
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private static final int FIELD_DOC_SIZE = Math.toIntExact(RamUsageEstimator.shallowSizeOf(FieldDoc.class));
 | 
	
		
			
				|  |  |  }
 |