diff --git a/src/main/java/org/elasticsearch/index/fielddata/IndexFieldDataService.java b/src/main/java/org/elasticsearch/index/fielddata/IndexFieldDataService.java index bf1775735f6..2032e59a725 100644 --- a/src/main/java/org/elasticsearch/index/fielddata/IndexFieldDataService.java +++ b/src/main/java/org/elasticsearch/index/fielddata/IndexFieldDataService.java @@ -45,18 +45,20 @@ public class IndexFieldDataService extends AbstractIndexComponent { static { buildersByType = MapBuilder.newMapBuilder() .put("string", new ConcreteBytesRefIndexFieldData.Builder()) + .put("float", new FloatArrayIndexFieldData.Builder()) .put("double", new DoubleArrayIndexFieldData.Builder()) + .put("int", new IntArrayIndexFieldData.Builder()) .put("long", new LongArrayIndexFieldData.Builder()) .put("geo_point", new GeoPointDoubleArrayIndexFieldData.Builder()) - .put("int", new IntArrayIndexFieldData.Builder()) .immutableMap(); buildersByTypeAndFormat = MapBuilder., IndexFieldData.Builder>newMapBuilder() .put(Tuple.tuple("string", "concrete_bytes"), new ConcreteBytesRefIndexFieldData.Builder()) + .put(Tuple.tuple("float", "array"), new FloatArrayIndexFieldData.Builder()) .put(Tuple.tuple("double", "array"), new DoubleArrayIndexFieldData.Builder()) + .put(Tuple.tuple("int", "array"), new IntArrayIndexFieldData.Builder()) .put(Tuple.tuple("long", "array"), new LongArrayIndexFieldData.Builder()) .put(Tuple.tuple("geo_point", "array"), new GeoPointDoubleArrayIndexFieldData.Builder()) - .put(Tuple.tuple("int", "array"), new IntArrayIndexFieldData.Builder()) .immutableMap(); } diff --git a/src/main/java/org/elasticsearch/index/fielddata/StringValues.java b/src/main/java/org/elasticsearch/index/fielddata/StringValues.java index 56f1a5e08a4..3178e25665a 100644 --- a/src/main/java/org/elasticsearch/index/fielddata/StringValues.java +++ b/src/main/java/org/elasticsearch/index/fielddata/StringValues.java @@ -20,6 +20,8 @@ package org.elasticsearch.index.fielddata; import org.elasticsearch.ElasticSearchIllegalStateException; +import org.elasticsearch.index.fielddata.util.FloatArrayRef; +import org.elasticsearch.index.fielddata.util.IntArrayRef; import org.elasticsearch.index.fielddata.util.LongArrayRef; import org.elasticsearch.index.fielddata.util.StringArrayRef; @@ -101,6 +103,100 @@ public interface StringValues { } } + public static class IntBased implements StringValues { + + private final IntValues values; + + private final StringArrayRef arrayScratch = new StringArrayRef(new String[1], 1); + private final ValuesIter valuesIter = new ValuesIter(); + private final Proc proc = new Proc(); + + public IntBased(IntValues values) { + this.values = values; + } + + @Override + public boolean isMultiValued() { + return values.isMultiValued(); + } + + @Override + public boolean hasValue(int docId) { + return values.hasValue(docId); + } + + @Override + public String getValue(int docId) { + if (!values.hasValue(docId)) { + return null; + } + return Integer.toString(values.getValue(docId)); + } + + @Override + public StringArrayRef getValues(int docId) { + IntArrayRef arrayRef = values.getValues(docId); + int size = arrayRef.size(); + if (size == 0) return StringArrayRef.EMPTY; + + arrayScratch.reset(size); + for (int i = arrayRef.start; i < arrayRef.end; i++) { + arrayScratch.values[arrayScratch.end++] = Integer.toString(arrayRef.values[i]); + } + return arrayScratch; + } + + @Override + public Iter getIter(int docId) { + return valuesIter.reset(values.getIter(docId)); + } + + @Override + public void forEachValueInDoc(int docId, ValueInDocProc proc) { + values.forEachValueInDoc(docId, this.proc.reset(proc)); + } + + static class ValuesIter implements Iter { + + private IntValues.Iter iter; + + private ValuesIter reset(IntValues.Iter iter) { + this.iter = iter; + return this; + } + + @Override + public boolean hasNext() { + return iter.hasNext(); + } + + @Override + public String next() { + return Integer.toString(iter.next()); + } + } + + static class Proc implements IntValues.ValueInDocProc { + + private ValueInDocProc proc; + + private Proc reset(ValueInDocProc proc) { + this.proc = proc; + return this; + } + + @Override + public void onValue(int docId, int value) { + proc.onValue(docId, Integer.toString(value)); + } + + @Override + public void onMissing(int docId) { + proc.onMissing(docId); + } + } + } + public static class LongBased implements StringValues { private final LongValues values; @@ -194,4 +290,99 @@ public interface StringValues { } } } + + public static class FloatBased implements StringValues { + + private final FloatValues values; + + private final StringArrayRef arrayScratch = new StringArrayRef(new String[1], 1); + private final ValuesIter valuesIter = new ValuesIter(); + private final Proc proc = new Proc(); + + public FloatBased(FloatValues values) { + this.values = values; + } + + @Override + public boolean isMultiValued() { + return values.isMultiValued(); + } + + @Override + public boolean hasValue(int docId) { + return values.hasValue(docId); + } + + @Override + public String getValue(int docId) { + if (!values.hasValue(docId)) { + return null; + } + return Float.toString(values.getValue(docId)); + } + + @Override + public StringArrayRef getValues(int docId) { + FloatArrayRef arrayRef = values.getValues(docId); + int size = arrayRef.size(); + if (size == 0) return StringArrayRef.EMPTY; + + arrayScratch.reset(size); + for (int i = arrayRef.start; i < arrayRef.end; i++) { + arrayScratch.values[arrayScratch.end++] = Float.toString(arrayRef.values[i]); + } + return arrayScratch; + } + + @Override + public Iter getIter(int docId) { + return valuesIter.reset(values.getIter(docId)); + } + + @Override + public void forEachValueInDoc(int docId, ValueInDocProc proc) { + values.forEachValueInDoc(docId, this.proc.reset(proc)); + } + + static class ValuesIter implements Iter { + + private FloatValues.Iter iter; + + private ValuesIter reset(FloatValues.Iter iter) { + this.iter = iter; + return this; + } + + @Override + public boolean hasNext() { + return iter.hasNext(); + } + + @Override + public String next() { + return Float.toString(iter.next()); + } + } + + static class Proc implements FloatValues.ValueInDocProc { + + private ValueInDocProc proc; + + private Proc reset(ValueInDocProc proc) { + this.proc = proc; + return this; + } + + @Override + public void onValue(int docId, float value) { + proc.onValue(docId, Float.toString(value)); + } + + @Override + public void onMissing(int docId) { + proc.onMissing(docId); + } + } + } + } diff --git a/src/main/java/org/elasticsearch/index/fielddata/plain/FloatArrayAtomicFieldData.java b/src/main/java/org/elasticsearch/index/fielddata/plain/FloatArrayAtomicFieldData.java new file mode 100644 index 00000000000..765a967ecdc --- /dev/null +++ b/src/main/java/org/elasticsearch/index/fielddata/plain/FloatArrayAtomicFieldData.java @@ -0,0 +1,925 @@ +/* + * Licensed to ElasticSearch and Shay Banon 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 org.apache.lucene.util.FixedBitSet; +import org.elasticsearch.common.RamUsage; +import org.elasticsearch.index.fielddata.*; +import org.elasticsearch.index.fielddata.ordinals.Ordinals; +import org.elasticsearch.index.fielddata.util.*; + +/** + */ +public abstract class FloatArrayAtomicFieldData implements AtomicNumericFieldData { + + protected final float[] values; + private final int numDocs; + + protected long size = -1; + + public FloatArrayAtomicFieldData(float[] values, int numDocs) { + this.values = values; + this.numDocs = numDocs; + } + + @Override + public int getNumDocs() { + return numDocs; + } + + public static class WithOrdinals extends FloatArrayAtomicFieldData { + + private final Ordinals ordinals; + + public WithOrdinals(float[] values, int numDocs, Ordinals ordinals) { + super(values, numDocs); + this.ordinals = ordinals; + } + + @Override + public boolean isMultiValued() { + return ordinals.isMultiValued(); + } + + @Override + public boolean isValuesOrdered() { + return true; + } + + @Override + public long getMemorySizeInBytes() { + if (size == -1) { + size = RamUsage.NUM_BYTES_INT/*size*/ + RamUsage.NUM_BYTES_INT/*numDocs*/ + RamUsage.NUM_BYTES_ARRAY_HEADER + (values.length * RamUsage.NUM_BYTES_FLOAT) + ordinals.getMemorySizeInBytes(); + } + return size; + } + + @Override + public BytesValues getBytesValues() { + return new BytesValues.StringBased(getStringValues()); + } + + @Override + public HashedBytesValues getHashedBytesValues() { + return new HashedBytesValues.StringBased(getStringValues()); + } + + @Override + public StringValues getStringValues() { + return new StringValues.FloatBased(getFloatValues()); + } + + @Override + public ScriptDocValues getScriptValues() { + return new ScriptDocValues.NumericDouble(getDoubleValues()); + } + + @Override + public ByteValues getByteValues() { + return new ByteValues.LongBased(getLongValues()); + } + + @Override + public ShortValues getShortValues() { + return new ShortValues.LongBased(getLongValues()); + } + + @Override + public IntValues getIntValues() { + return new IntValues.LongBased(getLongValues()); + } + + @Override + public LongValues getLongValues() { + return new LongValues(values, ordinals.ordinals()); + } + + @Override + public FloatValues getFloatValues() { + return new FloatValues(values, ordinals.ordinals()); + } + + @Override + public DoubleValues getDoubleValues() { + return new DoubleValues(values, ordinals.ordinals()); + } + + static class FloatValues implements org.elasticsearch.index.fielddata.FloatValues { + + private final float[] values; + private final Ordinals.Docs ordinals; + + private final FloatArrayRef arrayScratch = new FloatArrayRef(new float[1], 1); + private final ValuesIter iter; + + FloatValues(float[] values, Ordinals.Docs ordinals) { + this.values = values; + this.ordinals = ordinals; + this.iter = new ValuesIter(values); + } + + @Override + public boolean isMultiValued() { + return ordinals.isMultiValued(); + } + + @Override + public boolean hasValue(int docId) { + return ordinals.getOrd(docId) != 0; + } + + @Override + public float getValue(int docId) { + return values[ordinals.getOrd(docId)]; + } + + @Override + public float getValueMissing(int docId, float missingValue) { + int ord = ordinals.getOrd(docId); + if (ord == 0) { + return missingValue; + } else { + return values[ord]; + } + } + + @Override + public FloatArrayRef getValues(int docId) { + IntArrayRef ords = ordinals.getOrds(docId); + int size = ords.size(); + if (size == 0) return FloatArrayRef.EMPTY; + + arrayScratch.reset(size); + for (int i = ords.start; i < ords.end; i++) { + arrayScratch.values[arrayScratch.end++] = values[ords.values[i]]; + } + return arrayScratch; + } + + @Override + public Iter getIter(int docId) { + return iter.reset(ordinals.getIter(docId)); + } + + @Override + public void forEachValueInDoc(int docId, ValueInDocProc proc) { + Ordinals.Docs.Iter iter = ordinals.getIter(docId); + int ord = iter.next(); + if (ord == 0) { + proc.onMissing(docId); + return; + } + do { + proc.onValue(docId, values[ord]); + } while ((ord = iter.next()) != 0); + } + + static class ValuesIter implements Iter { + + private final float[] values; + private Ordinals.Docs.Iter ordsIter; + private int ord; + + ValuesIter(float[] values) { + this.values = values; + } + + public ValuesIter reset(Ordinals.Docs.Iter ordsIter) { + this.ordsIter = ordsIter; + this.ord = ordsIter.next(); + return this; + } + + @Override + public boolean hasNext() { + return ord != 0; + } + + @Override + public float next() { + float value = values[ord]; + ord = ordsIter.next(); + return value; + } + } + } + + static class LongValues implements org.elasticsearch.index.fielddata.LongValues { + + private final float[] values; + private final Ordinals.Docs ordinals; + + private final LongArrayRef arrayScratch = new LongArrayRef(new long[1], 1); + private final ValuesIter iter; + + LongValues(float[] values, Ordinals.Docs ordinals) { + this.values = values; + this.ordinals = ordinals; + this.iter = new ValuesIter(values); + } + + @Override + public boolean isMultiValued() { + return ordinals.isMultiValued(); + } + + @Override + public boolean hasValue(int docId) { + return ordinals.getOrd(docId) != 0; + } + + @Override + public long getValue(int docId) { + return (long) values[ordinals.getOrd(docId)]; + } + + @Override + public long getValueMissing(int docId, long missingValue) { + int ord = ordinals.getOrd(docId); + if (ord == 0) { + return missingValue; + } else { + return (long) values[ord]; + } + } + + @Override + public LongArrayRef getValues(int docId) { + IntArrayRef ords = ordinals.getOrds(docId); + int size = ords.size(); + if (size == 0) return LongArrayRef.EMPTY; + + arrayScratch.reset(size); + for (int i = ords.start; i < ords.end; i++) { + arrayScratch.values[arrayScratch.end++] = (long) values[ords.values[i]]; + } + return arrayScratch; + } + + @Override + public Iter getIter(int docId) { + return iter.reset(ordinals.getIter(docId)); + } + + @Override + public void forEachValueInDoc(int docId, ValueInDocProc proc) { + Ordinals.Docs.Iter iter = ordinals.getIter(docId); + int ord = iter.next(); + if (ord == 0) { + proc.onMissing(docId); + return; + } + do { + proc.onValue(docId, (long) values[ord]); + } while ((ord = iter.next()) != 0); + } + + static class ValuesIter implements Iter { + + private final float[] values; + private Ordinals.Docs.Iter ordsIter; + private int ord; + + ValuesIter(float[] values) { + this.values = values; + } + + public ValuesIter reset(Ordinals.Docs.Iter ordsIter) { + this.ordsIter = ordsIter; + this.ord = ordsIter.next(); + return this; + } + + @Override + public boolean hasNext() { + return ord != 0; + } + + @Override + public long next() { + float value = values[ord]; + ord = ordsIter.next(); + return (long) value; + } + } + } + + static class DoubleValues implements org.elasticsearch.index.fielddata.DoubleValues { + + private final float[] values; + private final Ordinals.Docs ordinals; + + private final DoubleArrayRef arrayScratch = new DoubleArrayRef(new double[1], 1); + private final ValuesIter iter; + + DoubleValues(float[] values, Ordinals.Docs ordinals) { + this.values = values; + this.ordinals = ordinals; + this.iter = new ValuesIter(values); + } + + @Override + public boolean isMultiValued() { + return ordinals.isMultiValued(); + } + + @Override + public boolean hasValue(int docId) { + return ordinals.getOrd(docId) != 0; + } + + @Override + public double getValue(int docId) { + return (double) values[ordinals.getOrd(docId)]; + } + + @Override + public double getValueMissing(int docId, double missingValue) { + int ord = ordinals.getOrd(docId); + if (ord == 0) { + return missingValue; + } else { + return (double) values[ord]; + } + } + + @Override + public DoubleArrayRef getValues(int docId) { + IntArrayRef ords = ordinals.getOrds(docId); + int size = ords.size(); + if (size == 0) return DoubleArrayRef.EMPTY; + + arrayScratch.reset(size); + for (int i = ords.start; i < ords.end; i++) { + arrayScratch.values[arrayScratch.end++] = (double) values[ords.values[i]]; + } + return arrayScratch; + } + + @Override + public Iter getIter(int docId) { + return iter.reset(ordinals.getIter(docId)); + } + + @Override + public void forEachValueInDoc(int docId, ValueInDocProc proc) { + Ordinals.Docs.Iter iter = ordinals.getIter(docId); + int ord = iter.next(); + if (ord == 0) { + proc.onMissing(docId); + return; + } + do { + proc.onValue(docId, (double) values[ord]); + } while ((ord = iter.next()) != 0); + } + + static class ValuesIter implements Iter { + + private final float[] values; + private Ordinals.Docs.Iter ordsIter; + private int ord; + + ValuesIter(float[] values) { + this.values = values; + } + + public ValuesIter reset(Ordinals.Docs.Iter ordsIter) { + this.ordsIter = ordsIter; + this.ord = ordsIter.next(); + return this; + } + + @Override + public boolean hasNext() { + return ord != 0; + } + + @Override + public double next() { + float value = values[ord]; + ord = ordsIter.next(); + return value; + } + } + } + } + + /** + * A single valued case, where not all values are "set", so we have a FixedBitSet that + * indicates which values have an actual value. + */ + public static class SingleFixedSet extends FloatArrayAtomicFieldData { + + private final FixedBitSet set; + + public SingleFixedSet(float[] values, int numDocs, FixedBitSet set) { + super(values, numDocs); + this.set = set; + } + + @Override + public boolean isMultiValued() { + return false; + } + + @Override + public boolean isValuesOrdered() { + return false; + } + + @Override + public long getMemorySizeInBytes() { + if (size == -1) { + size = RamUsage.NUM_BYTES_ARRAY_HEADER + (values.length * RamUsage.NUM_BYTES_FLOAT) + (set.getBits().length * RamUsage.NUM_BYTES_LONG); + } + return size; + } + + @Override + public ScriptDocValues getScriptValues() { + return new ScriptDocValues.NumericDouble(getDoubleValues()); + } + + @Override + public BytesValues getBytesValues() { + return new BytesValues.StringBased(getStringValues()); + } + + @Override + public HashedBytesValues getHashedBytesValues() { + return new HashedBytesValues.StringBased(getStringValues()); + } + + @Override + public StringValues getStringValues() { + return new StringValues.FloatBased(getFloatValues()); + } + + @Override + public ByteValues getByteValues() { + return new ByteValues.LongBased(getLongValues()); + } + + @Override + public ShortValues getShortValues() { + return new ShortValues.LongBased(getLongValues()); + } + + @Override + public IntValues getIntValues() { + return new IntValues.LongBased(getLongValues()); + } + + @Override + public LongValues getLongValues() { + return new LongValues(values, set); + } + + @Override + public FloatValues getFloatValues() { + return new FloatValues(values, set); + } + + @Override + public DoubleValues getDoubleValues() { + return new DoubleValues(values, set); + } + + static class FloatValues implements org.elasticsearch.index.fielddata.FloatValues { + + private final float[] values; + private final FixedBitSet set; + + private final FloatArrayRef arrayScratch = new FloatArrayRef(new float[1], 1); + private final Iter.Single iter = new Iter.Single(); + + FloatValues(float[] values, FixedBitSet set) { + this.values = values; + this.set = set; + } + + @Override + public boolean isMultiValued() { + return false; + } + + @Override + public boolean hasValue(int docId) { + return set.get(docId); + } + + @Override + public float getValue(int docId) { + return values[docId]; + } + + @Override + public float getValueMissing(int docId, float missingValue) { + if (set.get(docId)) { + return values[docId]; + } else { + return missingValue; + } + } + + @Override + public FloatArrayRef getValues(int docId) { + if (set.get(docId)) { + arrayScratch.values[0] = values[docId]; + return arrayScratch; + } else { + return FloatArrayRef.EMPTY; + } + } + + @Override + public Iter getIter(int docId) { + if (set.get(docId)) { + return iter.reset(values[docId]); + } else { + return Iter.Empty.INSTANCE; + } + } + + @Override + public void forEachValueInDoc(int docId, ValueInDocProc proc) { + if (set.get(docId)) { + proc.onValue(docId, values[docId]); + } + } + } + + static class LongValues implements org.elasticsearch.index.fielddata.LongValues { + + private final float[] values; + private final FixedBitSet set; + + private final LongArrayRef arrayScratch = new LongArrayRef(new long[1], 1); + private final Iter.Single iter = new Iter.Single(); + + LongValues(float[] values, FixedBitSet set) { + this.values = values; + this.set = set; + } + + @Override + public boolean isMultiValued() { + return false; + } + + @Override + public boolean hasValue(int docId) { + return set.get(docId); + } + + @Override + public long getValue(int docId) { + return (long) values[docId]; + } + + @Override + public long getValueMissing(int docId, long missingValue) { + if (set.get(docId)) { + return (long) values[docId]; + } else { + return missingValue; + } + } + + @Override + public LongArrayRef getValues(int docId) { + if (set.get(docId)) { + arrayScratch.values[0] = (long) values[docId]; + return arrayScratch; + } else { + return LongArrayRef.EMPTY; + } + } + + @Override + public Iter getIter(int docId) { + if (set.get(docId)) { + return iter.reset((long) values[docId]); + } else { + return Iter.Empty.INSTANCE; + } + } + + @Override + public void forEachValueInDoc(int docId, ValueInDocProc proc) { + if (set.get(docId)) { + proc.onValue(docId, (long) values[docId]); + } + } + } + + static class DoubleValues implements org.elasticsearch.index.fielddata.DoubleValues { + + private final float[] values; + private final FixedBitSet set; + + private final DoubleArrayRef arrayScratch = new DoubleArrayRef(new double[1], 1); + private final Iter.Single iter = new Iter.Single(); + + DoubleValues(float[] values, FixedBitSet set) { + this.values = values; + this.set = set; + } + + @Override + public boolean isMultiValued() { + return false; + } + + @Override + public boolean hasValue(int docId) { + return set.get(docId); + } + + @Override + public double getValue(int docId) { + return (double) values[docId]; + } + + @Override + public double getValueMissing(int docId, double missingValue) { + if (set.get(docId)) { + return (double) values[docId]; + } else { + return missingValue; + } + } + + @Override + public DoubleArrayRef getValues(int docId) { + if (set.get(docId)) { + arrayScratch.values[0] = (double) values[docId]; + return arrayScratch; + } else { + return DoubleArrayRef.EMPTY; + } + } + + @Override + public Iter getIter(int docId) { + if (set.get(docId)) { + return iter.reset((double) values[docId]); + } else { + return Iter.Empty.INSTANCE; + } + } + + @Override + public void forEachValueInDoc(int docId, ValueInDocProc proc) { + if (set.get(docId)) { + proc.onValue(docId, (double) values[docId]); + } + } + } + + } + + /** + * Assumes all the values are "set", and docId is used as the index to the value array. + */ + public static class Single extends FloatArrayAtomicFieldData { + + /** + * Note, here, we assume that there is no offset by 1 from docId, so position 0 + * is the value for docId 0. + */ + public Single(float[] values, int numDocs) { + super(values, numDocs); + } + + @Override + public boolean isMultiValued() { + return false; + } + + @Override + public boolean isValuesOrdered() { + return false; + } + + @Override + public long getMemorySizeInBytes() { + if (size == -1) { + size = RamUsage.NUM_BYTES_ARRAY_HEADER + (values.length * RamUsage.NUM_BYTES_FLOAT); + } + return size; + } + + @Override + public ScriptDocValues getScriptValues() { + return new ScriptDocValues.NumericDouble(getDoubleValues()); + } + + @Override + public BytesValues getBytesValues() { + return new BytesValues.StringBased(getStringValues()); + } + + @Override + public HashedBytesValues getHashedBytesValues() { + return new HashedBytesValues.StringBased(getStringValues()); + } + + @Override + public StringValues getStringValues() { + return new StringValues.FloatBased(getFloatValues()); + } + + @Override + public ByteValues getByteValues() { + return new ByteValues.LongBased(getLongValues()); + } + + @Override + public ShortValues getShortValues() { + return new ShortValues.LongBased(getLongValues()); + } + + @Override + public IntValues getIntValues() { + return new IntValues.LongBased(getLongValues()); + } + + @Override + public LongValues getLongValues() { + return new LongValues(values); + } + + @Override + public FloatValues getFloatValues() { + return new FloatValues(values); + } + + @Override + public DoubleValues getDoubleValues() { + return new DoubleValues(values); + } + + static class FloatValues implements org.elasticsearch.index.fielddata.FloatValues { + + private final float[] values; + + private final FloatArrayRef arrayScratch = new FloatArrayRef(new float[1], 1); + private final Iter.Single iter = new Iter.Single(); + + FloatValues(float[] values) { + this.values = values; + } + + @Override + public boolean isMultiValued() { + return false; + } + + @Override + public boolean hasValue(int docId) { + return true; + } + + @Override + public float getValue(int docId) { + return values[docId]; + } + + @Override + public float getValueMissing(int docId, float missingValue) { + return values[docId]; + } + + @Override + public FloatArrayRef getValues(int docId) { + arrayScratch.values[0] = values[docId]; + return arrayScratch; + } + + @Override + public Iter getIter(int docId) { + return iter.reset(values[docId]); + } + + @Override + public void forEachValueInDoc(int docId, ValueInDocProc proc) { + proc.onValue(docId, values[docId]); + } + } + + static class LongValues implements org.elasticsearch.index.fielddata.LongValues { + + private final float[] values; + + private final LongArrayRef arrayScratch = new LongArrayRef(new long[1], 1); + private final Iter.Single iter = new Iter.Single(); + + LongValues(float[] values) { + this.values = values; + } + + @Override + public boolean isMultiValued() { + return false; + } + + @Override + public boolean hasValue(int docId) { + return true; + } + + @Override + public long getValue(int docId) { + return (long) values[docId]; + } + + @Override + public long getValueMissing(int docId, long missingValue) { + return (long) values[docId]; + } + + @Override + public LongArrayRef getValues(int docId) { + arrayScratch.values[0] = (long) values[docId]; + return arrayScratch; + } + + @Override + public Iter getIter(int docId) { + return iter.reset((long) values[docId]); + } + + @Override + public void forEachValueInDoc(int docId, ValueInDocProc proc) { + proc.onValue(docId, (long) values[docId]); + } + } + + static class DoubleValues implements org.elasticsearch.index.fielddata.DoubleValues { + + private final float[] values; + + private final DoubleArrayRef arrayScratch = new DoubleArrayRef(new double[1], 1); + private final Iter.Single iter = new Iter.Single(); + + DoubleValues(float[] values) { + this.values = values; + } + + @Override + public boolean isMultiValued() { + return false; + } + + @Override + public boolean hasValue(int docId) { + return true; + } + + @Override + public double getValue(int docId) { + return (double) values[docId]; + } + + @Override + public double getValueMissing(int docId, double missingValue) { + return (double) values[docId]; + } + + @Override + public DoubleArrayRef getValues(int docId) { + arrayScratch.values[0] = (double) values[docId]; + return arrayScratch; + } + + @Override + public Iter getIter(int docId) { + return iter.reset((double) values[docId]); + } + + @Override + public void forEachValueInDoc(int docId, ValueInDocProc proc) { + proc.onValue(docId, (double) values[docId]); + } + } + } +} diff --git a/src/main/java/org/elasticsearch/index/fielddata/plain/FloatArrayIndexFieldData.java b/src/main/java/org/elasticsearch/index/fielddata/plain/FloatArrayIndexFieldData.java new file mode 100644 index 00000000000..6a921b8a7c1 --- /dev/null +++ b/src/main/java/org/elasticsearch/index/fielddata/plain/FloatArrayIndexFieldData.java @@ -0,0 +1,157 @@ +/* + * Licensed to ElasticSearch and Shay Banon 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 gnu.trove.list.array.TFloatArrayList; +import org.apache.lucene.index.*; +import org.apache.lucene.search.FieldCache; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.FixedBitSet; +import org.elasticsearch.ElasticSearchException; +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.index.Index; +import org.elasticsearch.index.fielddata.*; +import org.elasticsearch.index.fielddata.fieldcomparator.DoubleValuesComparatorSource; +import org.elasticsearch.index.fielddata.ordinals.MultiFlatArrayOrdinals; +import org.elasticsearch.index.mapper.FieldMapper; +import org.elasticsearch.index.settings.IndexSettings; + +import java.util.ArrayList; + +/** + */ +public class FloatArrayIndexFieldData extends AbstractIndexFieldData implements IndexNumericFieldData { + + public static class Builder implements IndexFieldData.Builder { + + @Override + public IndexFieldData build(Index index, @IndexSettings Settings indexSettings, FieldMapper.Names fieldNames, FieldDataType type, IndexFieldDataCache cache) { + return new FloatArrayIndexFieldData(index, indexSettings, fieldNames, type, cache); + } + } + + public FloatArrayIndexFieldData(Index index, @IndexSettings Settings indexSettings, FieldMapper.Names fieldNames, FieldDataType fieldDataType, IndexFieldDataCache cache) { + super(index, indexSettings, fieldNames, fieldDataType, cache); + } + + @Override + public NumericType getNumericType() { + return NumericType.FLOAT; + } + + @Override + public boolean valuesOrdered() { + // because we might have single values? we can dynamically update a flag to reflect that + // based on the atomic field data loaded + return false; + } + + @Override + public FloatArrayAtomicFieldData load(AtomicReaderContext context) { + try { + return cache.load(context, this); + } catch (Throwable e) { + if (e instanceof ElasticSearchException) { + throw (ElasticSearchException) e; + } else { + throw new ElasticSearchException(e.getMessage(), e); + } + } + } + + @Override + public FloatArrayAtomicFieldData loadDirect(AtomicReaderContext context) throws Exception { + AtomicReader reader = context.reader(); + + Terms terms = reader.terms(getFieldNames().indexName()); + if (terms == null) { + return new FloatArrayAtomicFieldData.Single(new float[0], 0); + } + + // TODO: how can we guess the number of terms? numerics end up creating more terms per value... + final TFloatArrayList values = new TFloatArrayList(); + ArrayList ordinals = new ArrayList(); + int[] idx = new int[reader.maxDoc()]; + ordinals.add(new int[reader.maxDoc()]); + + values.add(0); // first "t" indicates null value + int termOrd = 1; // current term number + + TermsEnum termsEnum = terms.iterator(null); + try { + DocsEnum docsEnum = null; + for (BytesRef term = termsEnum.next(); term != null; term = termsEnum.next()) { + values.add(FieldCache.NUMERIC_UTILS_FLOAT_PARSER.parseFloat(term)); + docsEnum = termsEnum.docs(reader.getLiveDocs(), docsEnum, 0); + for (int docId = docsEnum.nextDoc(); docId != DocsEnum.NO_MORE_DOCS; docId = docsEnum.nextDoc()) { + int[] ordinal; + if (idx[docId] >= ordinals.size()) { + ordinal = new int[reader.maxDoc()]; + ordinals.add(ordinal); + } else { + ordinal = ordinals.get(idx[docId]); + } + ordinal[docId] = termOrd; + idx[docId]++; + } + termOrd++; + } + } catch (RuntimeException e) { + if (e.getClass().getName().endsWith("StopFillCacheException")) { + // all is well, in case numeric parsers are used. + } else { + throw e; + } + } + + if (ordinals.size() == 1) { + int[] nativeOrdinals = ordinals.get(0); + FixedBitSet set = new FixedBitSet(reader.maxDoc()); + float[] sValues = new float[reader.maxDoc()]; + boolean allHaveValue = true; + for (int i = 0; i < nativeOrdinals.length; i++) { + int nativeOrdinal = nativeOrdinals[i]; + if (nativeOrdinal == 0) { + allHaveValue = false; + } else { + set.set(i); + sValues[i] = values.get(nativeOrdinal); + } + } + if (allHaveValue) { + return new FloatArrayAtomicFieldData.Single(sValues, reader.maxDoc()); + } else { + return new FloatArrayAtomicFieldData.SingleFixedSet(sValues, reader.maxDoc(), set); + } + } else { + int[][] nativeOrdinals = new int[ordinals.size()][]; + for (int i = 0; i < nativeOrdinals.length; i++) { + nativeOrdinals[i] = ordinals.get(i); + } + return new FloatArrayAtomicFieldData.WithOrdinals(values.toArray(new float[values.size()]), reader.maxDoc(), new MultiFlatArrayOrdinals(nativeOrdinals, termOrd)); + } + } + + @Override + public XFieldComparatorSource comparatorSource(@Nullable Object missingValue) { + return new DoubleValuesComparatorSource(this, missingValue); + } +} diff --git a/src/main/java/org/elasticsearch/index/fielddata/plain/IntArrayAtomicFieldData.java b/src/main/java/org/elasticsearch/index/fielddata/plain/IntArrayAtomicFieldData.java index 774faefb941..7c01d8a3df1 100644 --- a/src/main/java/org/elasticsearch/index/fielddata/plain/IntArrayAtomicFieldData.java +++ b/src/main/java/org/elasticsearch/index/fielddata/plain/IntArrayAtomicFieldData.java @@ -85,7 +85,7 @@ public abstract class IntArrayAtomicFieldData implements AtomicNumericFieldData @Override public StringValues getStringValues() { - return new StringValues.LongBased(getLongValues()); + return new StringValues.IntBased(getIntValues()); } @Override diff --git a/src/main/java/org/elasticsearch/index/mapper/core/FloatFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/core/FloatFieldMapper.java index 0b7148a5db5..3f3253b9af0 100644 --- a/src/main/java/org/elasticsearch/index/mapper/core/FloatFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/core/FloatFieldMapper.java @@ -131,7 +131,7 @@ public class FloatFieldMapper extends NumberFieldMapper { @Override public org.elasticsearch.index.fielddata.FieldDataType fieldDataType2() { - throw new ElasticSearchIllegalArgumentException("not implemented"); + return new org.elasticsearch.index.fielddata.FieldDataType("float"); } @Override diff --git a/src/test/java/org/elasticsearch/test/unit/index/fielddata/FloatFieldDataTests.java b/src/test/java/org/elasticsearch/test/unit/index/fielddata/FloatFieldDataTests.java new file mode 100644 index 00000000000..70db22655ea --- /dev/null +++ b/src/test/java/org/elasticsearch/test/unit/index/fielddata/FloatFieldDataTests.java @@ -0,0 +1,127 @@ +/* + * Licensed to ElasticSearch and Shay Banon 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.test.unit.index.fielddata; + +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field; +import org.apache.lucene.document.FloatField; +import org.apache.lucene.document.StringField; +import org.elasticsearch.index.fielddata.FieldDataType; + +/** + */ +public class FloatFieldDataTests extends NumericFieldDataTests { + + @Override + protected FieldDataType getFieldDataType() { + return new FieldDataType("float"); + } + + protected String one() { + return "1.0"; + } + + protected String two() { + return "2.0"; + } + + protected String three() { + return "3.0"; + } + + protected String four() { + return "4.0"; + } + + + @Override + protected void fillSingleValueAllSet() throws Exception { + Document d = new Document(); + d.add(new StringField("_id", "1", Field.Store.NO)); + d.add(new FloatField("value", 2.0f, Field.Store.NO)); + writer.addDocument(d); + + d = new Document(); + d.add(new StringField("_id", "2", Field.Store.NO)); + d.add(new FloatField("value", 1.0f, Field.Store.NO)); + writer.addDocument(d); + + d = new Document(); + d.add(new StringField("_id", "3", Field.Store.NO)); + d.add(new FloatField("value", 3.0f, Field.Store.NO)); + writer.addDocument(d); + } + + @Override + protected void fillSingleValueWithMissing() throws Exception { + Document d = new Document(); + d.add(new StringField("_id", "1", Field.Store.NO)); + d.add(new FloatField("value", 2.0f, Field.Store.NO)); + writer.addDocument(d); + + d = new Document(); + d.add(new StringField("_id", "2", Field.Store.NO)); + //d.add(new StringField("value", one(), Field.Store.NO)); // MISSING.... + writer.addDocument(d); + + d = new Document(); + d.add(new StringField("_id", "3", Field.Store.NO)); + d.add(new FloatField("value", 3.0f, Field.Store.NO)); + writer.addDocument(d); + } + + @Override + protected void fillMultiValueAllSet() throws Exception { + Document d = new Document(); + d.add(new StringField("_id", "1", Field.Store.NO)); + d.add(new FloatField("value", 2.0f, Field.Store.NO)); + d.add(new FloatField("value", 4.0f, Field.Store.NO)); + writer.addDocument(d); + + d = new Document(); + d.add(new StringField("_id", "2", Field.Store.NO)); + d.add(new FloatField("value", 1.0f, Field.Store.NO)); + writer.addDocument(d); + + d = new Document(); + d.add(new StringField("_id", "3", Field.Store.NO)); + d.add(new FloatField("value", 3.0f, Field.Store.NO)); + writer.addDocument(d); + } + + @Override + protected void fillMultiValueWithMissing() throws Exception { + Document d = new Document(); + d.add(new StringField("_id", "1", Field.Store.NO)); + d.add(new FloatField("value", 2.0f, Field.Store.NO)); + d.add(new FloatField("value", 4.0f, Field.Store.NO)); + writer.addDocument(d); + + d = new Document(); + d.add(new StringField("_id", "2", Field.Store.NO)); + //d.add(new StringField("value", one(), Field.Store.NO)); // MISSING + writer.addDocument(d); + + d = new Document(); + d.add(new StringField("_id", "3", Field.Store.NO)); + d.add(new FloatField("value", 3.0f, Field.Store.NO)); + writer.addDocument(d); + } +}