org.elasticsearch.index.fielddata.plain.ParentChildIntersectTermsEnum Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of elasticsearch Show documentation
Show all versions of elasticsearch Show documentation
Elasticsearch subproject :server
/*
* 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.search.DocIdSetIterator;
import org.apache.lucene.util.BytesRef;
import java.io.IOException;
import java.util.ArrayList;
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 List states;
private final IntArrayList stateSlots;
private BytesRef current;
ParentChildIntersectTermsEnum(LeafReader 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());
}
}
states = new ArrayList<>(fieldEnums.size());
for (TermsEnum tEnum : fieldEnums) {
states.add(new TermsEnumState(tEnum));
}
stateSlots = new IntArrayList(states.size());
}
@Override
public BytesRef term() throws IOException {
return current;
}
@Override
public PostingsEnum postings(PostingsEnum 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.postings(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.postings(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 PostingsEnum {
final List states;
int current = -1;
CompoundDocsEnum(List docsEnums) {
this.states = new ArrayList<>(docsEnums.size());
for (PostingsEnum 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() == DocIdSetIterator.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();
}
@Override
public int endOffset() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public BytesRef getPayload() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public int nextPosition() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public int startOffset() throws IOException {
throw new UnsupportedOperationException();
}
class State {
final PostingsEnum docsEnum;
int current = -1;
State(PostingsEnum 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();
}
}