Skip to content

Commit 738976e

Browse files
committed
Block loaders for MV_MIN and MV_MAX for keywords
Adds special purpose `BlockLoader` implementations for the `MV_MIN` and `MV_MAX` functions for `keyword` fields with doc values. These are a noop for single valued keywords but should be *much* faster for multivalued keywords. These aren't plugged in yet. We can plug them in and performance test them in elastic#137382. And they give us two more functions we can use to demonstrate elastic#137382.
1 parent 071d549 commit 738976e

File tree

9 files changed

+758
-280
lines changed

9 files changed

+758
-280
lines changed
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.index.mapper.blockloader.docvalues;
11+
12+
import org.apache.lucene.index.DocValues;
13+
import org.apache.lucene.index.LeafReaderContext;
14+
import org.apache.lucene.index.SortedDocValues;
15+
import org.apache.lucene.index.SortedSetDocValues;
16+
import org.apache.lucene.util.BytesRef;
17+
18+
import java.io.IOException;
19+
20+
/**
21+
* Loads {@code keyword} style fields that are stored as a lookup table.
22+
*/
23+
public abstract class AbstractBytesRefsFromOrdsBlockLoader extends BlockDocValuesReader.DocValuesBlockLoader {
24+
protected final String fieldName;
25+
26+
public AbstractBytesRefsFromOrdsBlockLoader(String fieldName) {
27+
this.fieldName = fieldName;
28+
}
29+
30+
@Override
31+
public BytesRefBuilder builder(BlockFactory factory, int expectedCount) {
32+
return factory.bytesRefs(expectedCount);
33+
}
34+
35+
@Override
36+
public AllReader reader(LeafReaderContext context) throws IOException {
37+
SortedSetDocValues docValues = context.reader().getSortedSetDocValues(fieldName);
38+
if (docValues != null) {
39+
SortedDocValues singleton = DocValues.unwrapSingleton(docValues);
40+
if (singleton != null) {
41+
return singletonReader(singleton);
42+
}
43+
return sortedSetReader(docValues);
44+
}
45+
SortedDocValues singleton = context.reader().getSortedDocValues(fieldName);
46+
if (singleton != null) {
47+
return singletonReader(singleton);
48+
}
49+
return new ConstantNullsReader();
50+
}
51+
52+
protected abstract AllReader singletonReader(SortedDocValues docValues);
53+
54+
protected abstract AllReader sortedSetReader(SortedSetDocValues docValues);
55+
56+
protected static class Singleton extends BlockDocValuesReader {
57+
private final SortedDocValues ordinals;
58+
59+
Singleton(SortedDocValues ordinals) {
60+
this.ordinals = ordinals;
61+
}
62+
63+
private Block readSingleDoc(BlockFactory factory, int docId) throws IOException {
64+
if (ordinals.advanceExact(docId)) {
65+
BytesRef v = ordinals.lookupOrd(ordinals.ordValue());
66+
// the returned BytesRef can be reused
67+
return factory.constantBytes(BytesRef.deepCopyOf(v), 1);
68+
} else {
69+
return factory.constantNulls(1);
70+
}
71+
}
72+
73+
@Override
74+
public Block read(BlockFactory factory, Docs docs, int offset, boolean nullsFiltered) throws IOException {
75+
if (docs.count() - offset == 1) {
76+
return readSingleDoc(factory, docs.get(offset));
77+
}
78+
if (ordinals instanceof OptionalColumnAtATimeReader direct) {
79+
Block block = direct.tryRead(factory, docs, offset, nullsFiltered, null, false);
80+
if (block != null) {
81+
return block;
82+
}
83+
}
84+
try (var builder = factory.singletonOrdinalsBuilder(ordinals, docs.count() - offset, false)) {
85+
for (int i = offset; i < docs.count(); i++) {
86+
int doc = docs.get(i);
87+
if (ordinals.advanceExact(doc)) {
88+
builder.appendOrd(ordinals.ordValue());
89+
} else {
90+
builder.appendNull();
91+
}
92+
}
93+
return builder.build();
94+
}
95+
}
96+
97+
@Override
98+
public void read(int docId, StoredFields storedFields, Builder builder) throws IOException {
99+
if (ordinals.advanceExact(docId)) {
100+
((BytesRefBuilder) builder).appendBytesRef(ordinals.lookupOrd(ordinals.ordValue()));
101+
} else {
102+
builder.appendNull();
103+
}
104+
}
105+
106+
@Override
107+
public int docId() {
108+
return ordinals.docID();
109+
}
110+
111+
@Override
112+
public String toString() {
113+
return "BytesRefsFromOrds.Singleton";
114+
}
115+
}
116+
117+
protected static class SortedSet extends BlockDocValuesReader {
118+
private final SortedSetDocValues ordinals;
119+
120+
SortedSet(SortedSetDocValues ordinals) {
121+
this.ordinals = ordinals;
122+
}
123+
124+
@Override
125+
public Block read(BlockFactory factory, Docs docs, int offset, boolean nullsFiltered) throws IOException {
126+
if (docs.count() - offset == 1) {
127+
return readSingleDoc(factory, docs.get(offset));
128+
}
129+
try (var builder = factory.sortedSetOrdinalsBuilder(ordinals, docs.count() - offset)) {
130+
for (int i = offset; i < docs.count(); i++) {
131+
int doc = docs.get(i);
132+
if (doc < ordinals.docID()) {
133+
throw new IllegalStateException("docs within same block must be in order");
134+
}
135+
if (ordinals.advanceExact(doc) == false) {
136+
builder.appendNull();
137+
continue;
138+
}
139+
int count = ordinals.docValueCount();
140+
if (count == 1) {
141+
builder.appendOrd(Math.toIntExact(ordinals.nextOrd()));
142+
} else {
143+
builder.beginPositionEntry();
144+
for (int c = 0; c < count; c++) {
145+
builder.appendOrd(Math.toIntExact(ordinals.nextOrd()));
146+
}
147+
builder.endPositionEntry();
148+
}
149+
}
150+
return builder.build();
151+
}
152+
}
153+
154+
@Override
155+
public void read(int docId, StoredFields storedFields, Builder builder) throws IOException {
156+
read(docId, (BytesRefBuilder) builder);
157+
}
158+
159+
private Block readSingleDoc(BlockFactory factory, int docId) throws IOException {
160+
if (ordinals.advanceExact(docId) == false) {
161+
return factory.constantNulls(1);
162+
}
163+
int count = ordinals.docValueCount();
164+
if (count == 1) {
165+
BytesRef v = ordinals.lookupOrd(ordinals.nextOrd());
166+
return factory.constantBytes(BytesRef.deepCopyOf(v), 1);
167+
}
168+
try (var builder = factory.bytesRefsFromDocValues(count)) {
169+
builder.beginPositionEntry();
170+
for (int c = 0; c < count; c++) {
171+
BytesRef v = ordinals.lookupOrd(ordinals.nextOrd());
172+
builder.appendBytesRef(v);
173+
}
174+
builder.endPositionEntry();
175+
return builder.build();
176+
}
177+
}
178+
179+
private void read(int docId, BytesRefBuilder builder) throws IOException {
180+
if (false == ordinals.advanceExact(docId)) {
181+
builder.appendNull();
182+
return;
183+
}
184+
int count = ordinals.docValueCount();
185+
if (count == 1) {
186+
builder.appendBytesRef(ordinals.lookupOrd(ordinals.nextOrd()));
187+
return;
188+
}
189+
builder.beginPositionEntry();
190+
for (int v = 0; v < count; v++) {
191+
builder.appendBytesRef(ordinals.lookupOrd(ordinals.nextOrd()));
192+
}
193+
builder.endPositionEntry();
194+
}
195+
196+
@Override
197+
public int docId() {
198+
return ordinals.docID();
199+
}
200+
201+
@Override
202+
public String toString() {
203+
return "BytesRefsFromOrds.SortedSet";
204+
}
205+
}
206+
}

0 commit comments

Comments
 (0)