org.elasticsearch.index.fielddata.plain.ParentChildIntersectTermsEnum Maven / Gradle / Ivy
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you 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 org.elasticsearch.index.fielddata.plain;
import com.carrotsearch.hppc.IntArrayList;
import org.apache.lucene.index.*;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.BytesRef;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
/**
* Intersects the terms and unions the doc ids for terms enum of multiple fields.
*
* @elasticsearch.internal
*/
final class ParentChildIntersectTermsEnum extends TermsEnum {
private final Comparator comparator;
private final List states;
private final IntArrayList stateSlots;
private BytesRef current;
ParentChildIntersectTermsEnum(AtomicReader atomicReader, String... fields) throws IOException {
List fieldEnums = new ArrayList();
for (String field : fields) {
Terms terms = atomicReader.terms(field);
if (terms != null) {
fieldEnums.add(terms.iterator(null));
}
}
this.comparator = fieldEnums.get(0).getComparator();
states = new ArrayList(fieldEnums.size());
for (TermsEnum tEnum : fieldEnums) {
states.add(new TermsEnumState(tEnum));
}
stateSlots = new IntArrayList(states.size());
}
@Override
public Comparator getComparator() {
return comparator;
}
@Override
public BytesRef term() throws IOException {
return current;
}
@Override
public DocsEnum docs(Bits liveDocs, DocsEnum reuse, int flags) throws IOException {
int size = stateSlots.size();
assert size > 0;
if (size == 1) {
// Can't use 'reuse' since we don't know to which previous TermsEnum it belonged to.
return states.get(stateSlots.get(0)).termsEnum.docs(liveDocs, null, flags);
} else {
List docsEnums = new ArrayList(stateSlots.size());
for (int i = 0; i < stateSlots.size(); i++) {
docsEnums.add(states.get(stateSlots.get(i)).termsEnum.docs(liveDocs, null, flags));
}
return new CompoundDocsEnum(docsEnums);
}
}
@Override
public BytesRef next() throws IOException {
if (states.isEmpty()) {
return null;
}
if (current == null) {
// unpositioned
for (TermsEnumState state : states) {
state.initialize();
}
} else {
int removed = 0;
for (int i = 0; i < stateSlots.size(); i++) {
int stateSlot = stateSlots.get(i);
if (states.get(stateSlot - removed).next() == null) {
states.remove(stateSlot - removed);
removed++;
}
}
if (states.isEmpty()) {
return null;
}
stateSlots.clear();
}
BytesRef lowestTerm = states.get(0).term;
stateSlots.add(0);
for (int i = 1; i < states.size(); i++) {
TermsEnumState state = states.get(i);
int cmp = lowestTerm.compareTo(state.term);
if (cmp > 0) {
lowestTerm = state.term;
stateSlots.clear();
stateSlots.add(i);
} else if (cmp == 0) {
stateSlots.add(i);
}
}
return current = lowestTerm;
}
@Override
public SeekStatus seekCeil(BytesRef text) throws IOException {
if (states.isEmpty()) {
return SeekStatus.END;
}
boolean found = false;
if (current == null) {
// unpositioned
Iterator iterator = states.iterator();
while (iterator.hasNext()) {
SeekStatus seekStatus = iterator.next().seekCeil(text);
if (seekStatus == SeekStatus.END) {
iterator.remove();
} else if (seekStatus == SeekStatus.FOUND) {
found = true;
}
}
} else {
int removed = 0;
for (int i = 0; i < stateSlots.size(); i++) {
int stateSlot = stateSlots.get(i);
SeekStatus seekStatus = states.get(stateSlot - removed).seekCeil(text);
if (seekStatus == SeekStatus.END) {
states.remove(stateSlot - removed);
removed++;
} else if (seekStatus == SeekStatus.FOUND) {
found = true;
}
}
}
if (states.isEmpty()) {
return SeekStatus.END;
}
stateSlots.clear();
if (found) {
for (int i = 0; i < states.size(); i++) {
if (states.get(i).term.equals(text)) {
stateSlots.add(i);
}
}
current = text;
return SeekStatus.FOUND;
} else {
BytesRef lowestTerm = states.get(0).term;
stateSlots.add(0);
for (int i = 1; i < states.size(); i++) {
TermsEnumState state = states.get(i);
int cmp = lowestTerm.compareTo(state.term);
if (cmp > 0) {
lowestTerm = state.term;
stateSlots.clear();
stateSlots.add(i);
} else if (cmp == 0) {
stateSlots.add(i);
}
}
current = lowestTerm;
return SeekStatus.NOT_FOUND;
}
}
class TermsEnumState {
final TermsEnum termsEnum;
BytesRef term;
SeekStatus lastSeekStatus;
TermsEnumState(TermsEnum termsEnum) {
this.termsEnum = termsEnum;
}
void initialize() throws IOException {
term = termsEnum.next();
}
BytesRef next() throws IOException {
return term = termsEnum.next();
}
SeekStatus seekCeil(BytesRef text) throws IOException {
lastSeekStatus = termsEnum.seekCeil(text);
if (lastSeekStatus != SeekStatus.END) {
term = termsEnum.term();
}
return lastSeekStatus;
}
}
class CompoundDocsEnum extends DocsEnum {
final List states;
int current = -1;
CompoundDocsEnum(List docsEnums) {
this.states = new ArrayList(docsEnums.size());
for (DocsEnum docsEnum : docsEnums) {
states.add(new State(docsEnum));
}
}
@Override
public int freq() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public int docID() {
return current;
}
@Override
public int nextDoc() throws IOException {
if (states.isEmpty()) {
return current = NO_MORE_DOCS;
}
if (current == -1) {
for (State state : states) {
state.initialize();
}
}
int lowestIndex = 0;
int lowestDocId = states.get(0).current;
for (int i = 1; i < states.size(); i++) {
State state = states.get(i);
if (lowestDocId > state.current) {
lowestDocId = state.current;
lowestIndex = i;
}
}
if (states.get(lowestIndex).next() == DocsEnum.NO_MORE_DOCS) {
states.remove(lowestIndex);
}
return current = lowestDocId;
}
@Override
public int advance(int target) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public long cost() {
throw new UnsupportedOperationException();
}
class State {
final DocsEnum docsEnum;
int current = -1;
State(DocsEnum docsEnum) {
this.docsEnum = docsEnum;
}
void initialize() throws IOException {
current = docsEnum.nextDoc();
}
int next() throws IOException {
return current = docsEnum.nextDoc();
}
}
}
@Override
public long ord() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public void seekExact(long ord) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public int docFreq() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public long totalTermFreq() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) throws IOException {
throw new UnsupportedOperationException();
}
}