support dates in range facet

This commit is contained in:
kimchy 2010-08-02 10:35:21 +03:00
parent 4bbbf186b6
commit 76cfbe6a44
23 changed files with 193 additions and 90 deletions

View File

@ -123,17 +123,13 @@ public interface FieldMapper<T> {
*/
T value(Fieldable field);
T valueFromString(String value);
/**
* Returns the actual value of the field as string.
*/
String valueAsString(Fieldable field);
/**
* Parses the string back into the type of the field (should be comparable!) in a similar
* manner {@link #valueForSearch(org.apache.lucene.document.Fieldable)} does with fields.
*/
Object valueFromTerm(String term);
/**
* Returns the indexed value.
*/

View File

@ -138,6 +138,10 @@ public class XContentAllFieldMapper extends XContentFieldMapper<Void> implements
return null;
}
@Override public Void valueFromString(String value) {
return null;
}
@Override public String valueAsString(Fieldable field) {
return null;
}

View File

@ -70,6 +70,10 @@ public class XContentBinaryFieldMapper extends XContentFieldMapper<byte[]> {
return field.getBinaryValue();
}
@Override public byte[] valueFromString(String value) {
return null;
}
@Override public String valueAsString(Fieldable field) {
return null;
}

View File

@ -120,6 +120,10 @@ public class XContentBooleanFieldMapper extends XContentFieldMapper<Boolean> {
return field.stringValue().charAt(0) == 'T' ? Boolean.TRUE : Boolean.FALSE;
}
@Override public Boolean valueFromString(String value) {
return value.charAt(0) == 'T' ? Boolean.TRUE : Boolean.FALSE;
}
@Override public String valueAsString(Fieldable field) {
return field.stringValue().charAt(0) == 'T' ? "true" : "false";
}

View File

@ -106,16 +106,12 @@ public class XContentBoostFieldMapper extends XContentNumberFieldMapper<Float> i
return Numbers.bytesToFloat(value);
}
@Override public String indexedValue(String value) {
return NumericUtils.floatToPrefixCoded(Float.parseFloat(value));
@Override public Float valueFromString(String value) {
return Float.parseFloat(value);
}
@Override public Object valueFromTerm(String term) {
final int shift = term.charAt(0) - NumericUtils.SHIFT_START_INT;
if (shift > 0 && shift <= 31) {
return null;
}
return NumericUtils.prefixCodedToFloat(term);
@Override public String indexedValue(String value) {
return NumericUtils.floatToPrefixCoded(Float.parseFloat(value));
}
@Override public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {

View File

@ -130,6 +130,10 @@ public class XContentDateFieldMapper extends XContentNumberFieldMapper<Long> {
return Numbers.bytesToLong(value);
}
@Override public Long valueFromString(String value) {
return dateTimeFormatter.parser().parseMillis(value);
}
/**
* Dates should return as a string, delegates to {@link #valueAsString(org.apache.lucene.document.Fieldable)}.
*/
@ -149,14 +153,6 @@ public class XContentDateFieldMapper extends XContentNumberFieldMapper<Long> {
return NumericUtils.longToPrefixCoded(dateTimeFormatter.parser().parseMillis(value));
}
@Override public Object valueFromTerm(String term) {
final int shift = term.charAt(0) - NumericUtils.SHIFT_START_LONG;
if (shift > 0 && shift <= 63) {
return null;
}
return NumericUtils.prefixCodedToLong(term);
}
@Override public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
return NumericRangeQuery.newLongRange(names.indexName(), precisionStep,
lowerTerm == null ? null : dateTimeFormatter.parser().parseMillis(lowerTerm),

View File

@ -118,16 +118,12 @@ public class XContentDoubleFieldMapper extends XContentNumberFieldMapper<Double>
return Numbers.bytesToDouble(value);
}
@Override public String indexedValue(String value) {
return NumericUtils.doubleToPrefixCoded(Double.parseDouble(value));
@Override public Double valueFromString(String value) {
return Double.valueOf(value);
}
@Override public Object valueFromTerm(String term) {
final int shift = term.charAt(0) - NumericUtils.SHIFT_START_LONG;
if (shift > 0 && shift <= 63) {
return null;
}
return NumericUtils.prefixCodedToDouble(term);
@Override public String indexedValue(String value) {
return NumericUtils.doubleToPrefixCoded(Double.parseDouble(value));
}
@Override public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {

View File

@ -304,13 +304,6 @@ public abstract class XContentFieldMapper<T> implements FieldMapper<T>, XContent
return valueAsString(field);
}
/**
* Simply returns the same string.
*/
@Override public Object valueFromTerm(String term) {
return term;
}
@Override public String indexedValue(String value) {
return value;
}

View File

@ -117,16 +117,12 @@ public class XContentFloatFieldMapper extends XContentNumberFieldMapper<Float> {
return Numbers.bytesToFloat(value);
}
@Override public String indexedValue(String value) {
return NumericUtils.floatToPrefixCoded(Float.parseFloat(value));
@Override public Float valueFromString(String value) {
return Float.parseFloat(value);
}
@Override public Object valueFromTerm(String term) {
final int shift = term.charAt(0) - NumericUtils.SHIFT_START_INT;
if (shift > 0 && shift <= 31) {
return null;
}
return NumericUtils.prefixCodedToFloat(term);
@Override public String indexedValue(String value) {
return NumericUtils.floatToPrefixCoded(Float.parseFloat(value));
}
@Override public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {

View File

@ -86,6 +86,10 @@ public class XContentIdFieldMapper extends XContentFieldMapper<String> implement
return field.stringValue();
}
@Override public String valueFromString(String value) {
return value;
}
@Override public String valueAsString(Fieldable field) {
return value(field);
}

View File

@ -117,16 +117,12 @@ public class XContentIntegerFieldMapper extends XContentNumberFieldMapper<Intege
return Numbers.bytesToInt(value);
}
@Override public String indexedValue(String value) {
return NumericUtils.intToPrefixCoded(Integer.parseInt(value));
@Override public Integer valueFromString(String value) {
return Integer.parseInt(value);
}
@Override public Object valueFromTerm(String term) {
final int shift = term.charAt(0) - NumericUtils.SHIFT_START_INT;
if (shift > 0 && shift <= 31) {
return null;
}
return NumericUtils.prefixCodedToInt(term);
@Override public String indexedValue(String value) {
return NumericUtils.intToPrefixCoded(Integer.parseInt(value));
}
@Override public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {

View File

@ -117,16 +117,12 @@ public class XContentLongFieldMapper extends XContentNumberFieldMapper<Long> {
return Numbers.bytesToLong(value);
}
@Override public String indexedValue(String value) {
return NumericUtils.longToPrefixCoded(Long.parseLong(value));
@Override public Long valueFromString(String value) {
return Long.valueOf(value);
}
@Override public Object valueFromTerm(String term) {
final int shift = term.charAt(0) - NumericUtils.SHIFT_START_LONG;
if (shift > 0 && shift <= 63) {
return null;
}
return NumericUtils.prefixCodedToLong(term);
@Override public String indexedValue(String value) {
return NumericUtils.longToPrefixCoded(Long.parseLong(value));
}
@Override public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {

View File

@ -155,8 +155,6 @@ public abstract class XContentNumberFieldMapper<T extends Number> extends XConte
return num == null ? null : num.toString();
}
@Override public abstract Object valueFromTerm(String term);
@Override public void merge(XContentMapper mergeWith, MergeContext mergeContext) throws MergeMappingException {
super.merge(mergeWith, mergeContext);
if (!this.getClass().equals(mergeWith.getClass())) {

View File

@ -117,16 +117,12 @@ public class XContentShortFieldMapper extends XContentNumberFieldMapper<Short> {
return Numbers.bytesToShort(value);
}
@Override public String indexedValue(String value) {
return NumericUtils.intToPrefixCoded(Short.parseShort(value));
@Override public Short valueFromString(String value) {
return Short.valueOf(value);
}
@Override public Object valueFromTerm(String term) {
final int shift = term.charAt(0) - NumericUtils.SHIFT_START_INT;
if (shift > 0 && shift <= 31) {
return null;
}
return NumericUtils.prefixCodedToInt(term);
@Override public String indexedValue(String value) {
return NumericUtils.intToPrefixCoded(Short.parseShort(value));
}
@Override public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {

View File

@ -100,6 +100,10 @@ public class XContentSourceFieldMapper extends XContentFieldMapper<byte[]> imple
return field.getBinaryValue();
}
@Override public byte[] valueFromString(String value) {
return null;
}
@Override public String valueAsString(Fieldable field) {
throw new UnsupportedOperationException();
}

View File

@ -111,6 +111,10 @@ public class XContentStringFieldMapper extends XContentFieldMapper<String> imple
return field.stringValue();
}
@Override public String valueFromString(String value) {
return value;
}
@Override public String valueAsString(Fieldable field) {
return value(field);
}

View File

@ -86,6 +86,10 @@ public class XContentTypeFieldMapper extends XContentFieldMapper<String> impleme
return field.stringValue();
}
@Override public String valueFromString(String value) {
return value;
}
@Override public String valueAsString(Fieldable field) {
return value(field);
}

View File

@ -84,6 +84,10 @@ public class XContentUidFieldMapper extends XContentFieldMapper<Uid> implements
return Uid.createUid(field.stringValue());
}
@Override public Uid valueFromString(String value) {
return Uid.createUid(value);
}
@Override public String valueAsString(Fieldable field) {
return field.stringValue();
}

View File

@ -128,7 +128,18 @@ public class InternalRangeDistanceFacet implements RangeFacet, InternalFacet {
valueFieldName = in.readUTF();
entries = new Entry[in.readVInt()];
for (int i = 0; i < entries.length; i++) {
entries[i] = new Entry(in.readDouble(), in.readDouble(), in.readVLong(), in.readDouble());
Entry entry = new Entry();
entry.from = in.readDouble();
entry.to = in.readDouble();
if (in.readBoolean()) {
entry.fromAsString = in.readUTF();
}
if (in.readBoolean()) {
entry.toAsString = in.readUTF();
}
entry.count = in.readVLong();
entry.total = in.readDouble();
entries[i] = entry;
}
}
@ -140,6 +151,18 @@ public class InternalRangeDistanceFacet implements RangeFacet, InternalFacet {
for (Entry entry : entries) {
out.writeDouble(entry.from);
out.writeDouble(entry.to);
if (entry.fromAsString == null) {
out.writeBoolean(false);
} else {
out.writeBoolean(true);
out.writeUTF(entry.fromAsString);
}
if (entry.toAsString == null) {
out.writeBoolean(false);
} else {
out.writeBoolean(true);
out.writeUTF(entry.toAsString);
}
out.writeVLong(entry.count);
out.writeDouble(entry.total);
}
@ -156,9 +179,15 @@ public class InternalRangeDistanceFacet implements RangeFacet, InternalFacet {
if (!Double.isInfinite(entry.from)) {
builder.field("from", entry.from);
}
if (entry.fromAsString != null) {
builder.field("from_str", entry.fromAsString);
}
if (!Double.isInfinite(entry.to)) {
builder.field("to", entry.to);
}
if (entry.toAsString != null) {
builder.field("to_str", entry.fromAsString);
}
builder.field("count", entry.count());
builder.field("total", entry.total());
builder.field("mean", entry.mean());

View File

@ -64,6 +64,10 @@ public interface RangeFacet extends Facet, Iterable<RangeFacet.Entry> {
double to = Double.POSITIVE_INFINITY;
String fromAsString;
String toAsString;
long count;
double total;
@ -71,13 +75,6 @@ public interface RangeFacet extends Facet, Iterable<RangeFacet.Entry> {
Entry() {
}
public Entry(double from, double to, long count, double total) {
this.from = from;
this.to = to;
this.count = count;
this.total = total;
}
public double from() {
return this.from;
}
@ -86,6 +83,17 @@ public interface RangeFacet extends Facet, Iterable<RangeFacet.Entry> {
return from();
}
public String fromAsString() {
if (fromAsString != null) {
return fromAsString;
}
return Double.toString(from);
}
public String getFromAsString() {
return fromAsString();
}
public double to() {
return this.to;
}
@ -94,6 +102,17 @@ public interface RangeFacet extends Facet, Iterable<RangeFacet.Entry> {
return to();
}
public String toAsString() {
if (toAsString != null) {
return toAsString;
}
return Double.toString(to);
}
public String getToAsString() {
return toAsString();
}
public long count() {
return this.count;
}

View File

@ -89,6 +89,11 @@ public class RangeFacetBuilder extends AbstractFacetBuilder {
return this;
}
public RangeFacetBuilder addRange(String from, String to) {
entries.add(new Entry(from, to));
return this;
}
/**
* Adds a range entry with explicit from and unbounded to.
*
@ -99,6 +104,11 @@ public class RangeFacetBuilder extends AbstractFacetBuilder {
return this;
}
public RangeFacetBuilder addUnboundedTo(String from) {
entries.add(new Entry(from, null));
return this;
}
/**
* Adds a range entry with explicit to and unbounded from.
*
@ -109,6 +119,11 @@ public class RangeFacetBuilder extends AbstractFacetBuilder {
return this;
}
public RangeFacetBuilder addUnboundedFrom(String to) {
entries.add(new Entry(null, to));
return this;
}
/**
* Should the facet run in global mode (not bounded by the search query) or not (bounded by
* the search query). Defaults to <tt>false</tt>.
@ -148,10 +163,14 @@ public class RangeFacetBuilder extends AbstractFacetBuilder {
builder.startArray("ranges");
for (Entry entry : entries) {
builder.startObject();
if (!Double.isInfinite(entry.from)) {
if (entry.fromAsString != null) {
builder.field("from", entry.fromAsString);
} else if (!Double.isInfinite(entry.from)) {
builder.field("from", entry.from);
}
if (!Double.isInfinite(entry.to)) {
if (entry.toAsString != null) {
builder.field("to", entry.toAsString);
} else if (!Double.isInfinite(entry.to)) {
builder.field("to", entry.to);
}
builder.endObject();
@ -172,11 +191,19 @@ public class RangeFacetBuilder extends AbstractFacetBuilder {
builder.endObject();
}
private static class Entry {
final double from;
final double to;
static class Entry {
double from = Double.NEGATIVE_INFINITY;
double to = Double.POSITIVE_INFINITY;
private Entry(double from, double to) {
String fromAsString;
String toAsString;
Entry(String fromAsString, String toAsString) {
this.fromAsString = fromAsString;
this.toAsString = toAsString;
}
Entry(double from, double to) {
this.from = from;
this.to = to;
}

View File

@ -21,6 +21,7 @@ package org.elasticsearch.search.facets.range;
import org.elasticsearch.common.collect.Lists;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.search.facets.FacetPhaseExecutionException;
import org.elasticsearch.search.facets.collector.FacetCollector;
import org.elasticsearch.search.facets.collector.FacetCollectorParser;
@ -61,20 +62,25 @@ public class RangeFacetCollectorParser implements FacetCollectorParser {
// { "from" : "12.5" }
// ]
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
double from = Double.NEGATIVE_INFINITY;
double to = Double.POSITIVE_INFINITY;
RangeFacet.Entry entry = new RangeFacet.Entry();
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
fieldName = parser.currentName();
} else if (token == XContentParser.Token.VALUE_STRING) {
if ("from".equals(fieldName)) {
entry.fromAsString = parser.text();
} else if ("to".equals(fieldName)) {
entry.toAsString = parser.text();
}
} else if (token.isValue()) {
if ("from".equals(fieldName)) {
from = parser.doubleValue();
entry.from = parser.doubleValue();
} else if ("to".equals(fieldName)) {
to = parser.doubleValue();
entry.to = parser.doubleValue();
}
}
}
entries.add(new RangeFacet.Entry(from, to, 0, 0));
entries.add(entry);
}
}
} else if (token == XContentParser.Token.START_OBJECT) {
@ -102,6 +108,22 @@ public class RangeFacetCollectorParser implements FacetCollectorParser {
RangeFacet.Entry[] rangeEntries = entries.toArray(new RangeFacet.Entry[entries.size()]);
// fix the range entries if needed
if (keyField != null) {
FieldMapper mapper = context.mapperService().smartNameFieldMapper(keyField);
if (mapper == null) {
throw new FacetPhaseExecutionException(facetName, "No mapping found for key_field [" + keyField + "]");
}
for (RangeFacet.Entry entry : rangeEntries) {
if (entry.fromAsString != null) {
entry.from = ((Number) mapper.valueFromString(entry.fromAsString)).doubleValue();
}
if (entry.toAsString != null) {
entry.to = ((Number) mapper.valueFromString(entry.toAsString)).doubleValue();
}
}
}
if (keyScript != null && valueScript != null) {
return new ScriptRangeFacetCollector(facetName, keyScript, valueScript, params, rangeEntries, context.scriptService(), context.fieldDataCache(), context.mapperService());
}

View File

@ -453,6 +453,7 @@ public class SimpleFacetsTests extends AbstractNodesTests {
client.prepareIndex("test", "type1").setSource(jsonBuilder().startObject()
.field("num", 1055)
.field("value", 1)
.field("date", "1970-01-01T00:00:00")
.startArray("multi_num").value(13.0f).value(23.f).endArray()
.startArray("multi_value").value(10).value(11).endArray()
.endObject()).execute().actionGet();
@ -461,6 +462,7 @@ public class SimpleFacetsTests extends AbstractNodesTests {
client.prepareIndex("test", "type1").setSource(jsonBuilder().startObject()
.field("num", 1065)
.field("value", 2)
.field("date", "1970-01-01T00:00:25")
.startArray("multi_num").value(15.0f).value(31.0f).endArray()
.startArray("multi_value").value(20).value(21).endArray()
.endObject()).execute().actionGet();
@ -469,6 +471,7 @@ public class SimpleFacetsTests extends AbstractNodesTests {
client.prepareIndex("test", "type1").setSource(jsonBuilder().startObject()
.field("num", 1175)
.field("value", 3)
.field("date", "1970-01-01T00:00:52")
.startArray("multi_num").value(17.0f).value(25.0f).endArray()
.startArray("multi_value").value(30).value(31).endArray()
.endObject()).execute().actionGet();
@ -481,6 +484,7 @@ public class SimpleFacetsTests extends AbstractNodesTests {
.addFacet(rangeFacet("range3").keyField("num").valueField("multi_value").addUnboundedFrom(1056).addRange(1000, 1170).addUnboundedTo(1170))
.addFacet(rangeFacet("range4").keyField("multi_num").valueField("value").addUnboundedFrom(16).addRange(10, 26).addUnboundedTo(20))
.addFacet(rangeScriptFacet("range5").keyScript("doc['num'].value").valueScript("doc['value'].value").addUnboundedFrom(1056).addRange(1000, 1170).addUnboundedTo(1170))
.addFacet(rangeFacet("range6").field("date").addUnboundedFrom("1970-01-01T00:00:26").addRange("1970-01-01T00:00:15", "1970-01-01T00:00:53").addUnboundedTo("1970-01-01T00:00:26"))
.execute().actionGet();
if (searchResponse.failedShards() > 0) {
@ -560,5 +564,16 @@ public class SimpleFacetsTests extends AbstractNodesTests {
assertThat(facet.entries().get(2).from(), closeTo(1170, 0.000001));
assertThat(facet.entries().get(2).count(), equalTo(1l));
assertThat(facet.entries().get(2).total(), closeTo(3, 0.000001));
facet = searchResponse.facets().facet("range6");
assertThat(facet.name(), equalTo("range6"));
assertThat(facet.entries().size(), equalTo(3));
assertThat(facet.entries().get(0).count(), equalTo(2l));
assertThat(facet.entries().get(0).toAsString(), equalTo("1970-01-01T00:00:26"));
assertThat(facet.entries().get(1).count(), equalTo(2l));
assertThat(facet.entries().get(1).fromAsString(), equalTo("1970-01-01T00:00:15"));
assertThat(facet.entries().get(1).toAsString(), equalTo("1970-01-01T00:00:53"));
assertThat(facet.entries().get(2).count(), equalTo(1l));
assertThat(facet.entries().get(2).fromAsString(), equalTo("1970-01-01T00:00:26"));
}
}