package org.apache.solr.search.field;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.
*/
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.CharsRef;
import org.apache.lucene.util.UnicodeUtil;
import org.apache.solr.core.HS;
import org.apache.solr.search.function.ValueSourceScorer;
import org.apache.solr.search.mutable.MutableValue;
import org.apache.solr.search.mutable.MutableValueStr;
import java.io.IOException;
public abstract class StrLeafValues extends LeafValues {
protected StrFieldStats stats;
public StrLeafValues(FieldValues fieldValues, StrFieldStats stats) {
super(fieldValues);
this.stats = stats;
}
@Override
public StrFieldStats getFieldStats() {
return stats;
}
@Override
public boolean exists(int doc) {
return ordVal(doc) >= 0;
}
@Override
public boolean boolVal(int doc) {
return exists(doc);
}
@Override
public abstract int ordVal(int doc); // TODO: single-valued fields will never have ords > 2B (because num_ords <= maxDoc)
public abstract long termToOrd(BytesRef term);
public abstract void ordToTerm(long ord, BytesRef target);
// TODO: can all subclasses support this?
public abstract long ordToTermPointer(long ord);
public abstract int termPointerToOrd(long termPointer);
@Override
public abstract boolean bytesVal(int doc, BytesRef target);
@Override
public String strVal(int doc) {
BytesRef spare = new BytesRef();
boolean exists = bytesVal(doc, spare);
if (!exists) return null; // TODO: return null or empty string? If this is changed, objectVal will also need to be changed
CharsRef spareChars = new CharsRef();
UnicodeUtil.UTF8toUTF16(spare, spareChars);
return spareChars.toString();
}
@Override
public Object objectVal(int doc) {
return strVal(doc); // strVal currently returns null if the value does not exist
}
@Override
public ValueSourceScorer getRangeScorer(AtomicReaderContext readerContext, String lowerVal, String upperVal, boolean includeLower, boolean includeUpper, boolean matchMissing) {
int lower = matchMissing ? -1 : 0;
if (lowerVal != null) {
lower = (int)termToOrd(new BytesRef(lowerVal));
if (lower < 0) {
lower = -lower - 1;
} else if (!includeLower) {
lower++;
}
}
int upper = Integer.MAX_VALUE;
if (upperVal != null) {
upper = (int)termToOrd(new BytesRef(upperVal));
if (upper < 0) {
upper = -upper - 2;
} else if (!includeUpper) {
upper--;
}
}
final int ll = lower;
final int uu = upper;
return new ValueSourceScorer(readerContext, this) {
@Override
public boolean matchesValue(int doc) {
int ord = ordVal(doc);
return ord >= ll && ord <= uu;
}
};
}
@Override
public ValueFiller getValueFiller() {
return new ValueFiller() {
private final MutableValueStr mval = new MutableValueStr();
@Override
public MutableValue getValue() {
return mval;
}
@Override
public void fillValue(int doc) {
mval.exists = bytesVal(doc, mval.value);
}
};
}
}
class StrArrLeafValues extends StrLeafValues {
private final LongArray ords; // contains 1-numOrd, with 0 meaning "missing"... subtract 1 to get the "real" ord
private final LongArray ordToOffset; // indexed by ord (so 0 is first real value...)
private final long termBytes; // offset 0 is first real value
// offset = avg_term_length * ord + adjustment;
public StrArrLeafValues(FieldValues fieldValues, LongArray ords, LongArray offsets, long termBytes, StrFieldStats stats) {
super(fieldValues, stats);
this.ords = ords;
this.ordToOffset = offsets;
this.termBytes = termBytes;
}
// testing methods, subject to change with implementation
public LongArray _getDocToOrdArray() { return ords; }
public LongArray _getOrdToOffsetArray() { return ordToOffset; }
public long _getTermBytes() {return termBytes; }
public long ordToTermPointer(long ord) {
long offset = ordToOffset.getLong((int)ord);
assert offset >= 0 && offset < HS.arraySizeBytes(termBytes);
return termBytes + offset;
}
public int termPointerToOrd(long termPointer) {
int termLen = HS.getTermLength(termPointer);
long termPointerBytes = termPointer + ((termLen <= 0x7f) ? 1 : 2);
int low = 0;
int high = (int)(ordToOffset.getSize() - 1);
while (low <= high) {
int mid = (low + high) >>> 1;
long midPointer = ordToTermPointer(mid);
int cmp = HS.compareLengthPrefixBytes(termLen, termPointerBytes, midPointer);
if (cmp < 0) {
high = mid - 1;
} else if (cmp > 0) {
low = mid + 1;
} else {
return mid; // key found
}
}
return -(low + 1); // key not found.
}
@Override
public long termToOrd(BytesRef key) {
int low = 0;
int high = (int)(ordToOffset.getSize() - 1);
while (low <= high) {
int mid = (low + high) >>> 1;
long midPointer = ordToTermPointer(mid);
int cmp = HS.compareLengthPrefixBytes(midPointer, key);
if (cmp < 0) {
low = mid + 1;
} else if (cmp > 0) {
high = mid - 1;
} else {
return mid; // key found
}
}
return -(low + 1); // key not found.
}
@Override
public int ordVal(int doc) {
return ords.getInt(doc) - 1;
}
@Override
public void ordToTerm(long ord, BytesRef target) {
long offset = ordToOffset.getLong((int)ord);
HS.copyLengthPrefixBytes(termBytes, offset, target);
}
@Override
public boolean bytesVal(int doc, BytesRef target) {
int ord = ordVal(doc); // ord for single valued field will be limited to an int
if (ord < 0) {
target.length = 0; // TODO should not be needed...
return false;
}
ordToTerm(ord, target);
return true;
}
@Override
public long getSizeInBytes() {
return ords.memSize() + ordToOffset.memSize() + HS.arraySizeBytes(termBytes);
}
@Override
protected void free() {
try {
HS.freeArray(termBytes);
ords.close();
ordToOffset.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
class Str0Values extends StrLeafValues {
// offset = avg_term_length * ord + adjustment;
public Str0Values(FieldValues fieldValues, StrFieldStats stats) {
super(fieldValues, stats);
}
@Override
public int ordVal(int doc) {
return -1;
}
@Override
public void ordToTerm(long ord, BytesRef target) {
// is this defined for "missing"?
}
@Override
public long ordToTermPointer(long ord) {
return 0; // should never be called?
}
@Override
public int termPointerToOrd(long termPointer) {
return -1;
}
@Override
public long termToOrd(BytesRef term) {
return -1;
}
@Override
public boolean bytesVal(int doc, BytesRef target) {
target.length = 0; // TODO: should not be needed
return false;
}
@Override
public long getSizeInBytes() {
return 0;
}
@Override
protected void free() {
}
}