|
@@ -0,0 +1,455 @@
|
|
|
+/*
|
|
|
+ * 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.action.fieldstats;
|
|
|
+
|
|
|
+import org.apache.lucene.util.BytesRef;
|
|
|
+import org.elasticsearch.common.io.stream.StreamInput;
|
|
|
+import org.elasticsearch.common.io.stream.StreamOutput;
|
|
|
+import org.elasticsearch.common.io.stream.Streamable;
|
|
|
+import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
|
|
+import org.elasticsearch.common.joda.Joda;
|
|
|
+import org.elasticsearch.common.xcontent.ToXContent;
|
|
|
+import org.elasticsearch.common.xcontent.XContentBuilder;
|
|
|
+import org.elasticsearch.common.xcontent.XContentBuilderString;
|
|
|
+
|
|
|
+import java.io.IOException;
|
|
|
+
|
|
|
+public abstract class FieldStats<T> implements Streamable, ToXContent {
|
|
|
+
|
|
|
+ private byte type;
|
|
|
+ private long maxDoc;
|
|
|
+ private long docCount;
|
|
|
+ private long sumDocFreq;
|
|
|
+ private long sumTotalTermFreq;
|
|
|
+ protected T minValue;
|
|
|
+ protected T maxValue;
|
|
|
+
|
|
|
+ protected FieldStats() {
|
|
|
+ }
|
|
|
+
|
|
|
+ protected FieldStats(int type, long maxDoc, long docCount, long sumDocFreq, long sumTotalTermFreq) {
|
|
|
+ this.type = (byte) type;
|
|
|
+ this.maxDoc = maxDoc;
|
|
|
+ this.docCount = docCount;
|
|
|
+ this.sumDocFreq = sumDocFreq;
|
|
|
+ this.sumTotalTermFreq = sumTotalTermFreq;
|
|
|
+ }
|
|
|
+
|
|
|
+ byte getType() {
|
|
|
+ return type;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return the total number of documents.
|
|
|
+ *
|
|
|
+ * Note that, documents marked as deleted that haven't yet been merged way aren't taken into account.
|
|
|
+ */
|
|
|
+ public long getMaxDoc() {
|
|
|
+ return maxDoc;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return the number of documents that have at least one term for this field, or -1 if this measurement isn't available.
|
|
|
+ *
|
|
|
+ * Note that, documents marked as deleted that haven't yet been merged way aren't taken into account.
|
|
|
+ */
|
|
|
+ public long getDocCount() {
|
|
|
+ return docCount;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return The percentage of documents that have at least one value for this field.
|
|
|
+ *
|
|
|
+ * This is a derived statistic and is based on: 'doc_count / max_doc'
|
|
|
+ */
|
|
|
+ public int getDensity() {
|
|
|
+ if (docCount < 0 || maxDoc <= 0) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ return (int) (docCount * 100 / maxDoc);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return the sum of each term's document frequency in this field, or -1 if this measurement isn't available.
|
|
|
+ * Document frequency is the number of documents containing a particular term.
|
|
|
+ *
|
|
|
+ * Note that, documents marked as deleted that haven't yet been merged way aren't taken into account.
|
|
|
+ */
|
|
|
+ public long getSumDocFreq() {
|
|
|
+ return sumDocFreq;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return the sum of the term frequencies of all terms in this field across all documents, or -1 if this measurement
|
|
|
+ * isn't available. Term frequency is the total number of occurrences of a term in a particular document and field.
|
|
|
+ *
|
|
|
+ * Note that, documents marked as deleted that haven't yet been merged way aren't taken into account.
|
|
|
+ */
|
|
|
+ public long getSumTotalTermFreq() {
|
|
|
+ return sumTotalTermFreq;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return the lowest value in the field represented as a string.
|
|
|
+ *
|
|
|
+ * Note that, documents marked as deleted that haven't yet been merged way aren't taken into account.
|
|
|
+ */
|
|
|
+ public abstract String getMinValue();
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return the highest value in the field represented as a string.
|
|
|
+ *
|
|
|
+ * Note that, documents marked as deleted that haven't yet been merged way aren't taken into account.
|
|
|
+ */
|
|
|
+ public abstract String getMaxValue();
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Merges the provided stats into this stats instance.
|
|
|
+ */
|
|
|
+ public void append(FieldStats stats) {
|
|
|
+ this.maxDoc += stats.maxDoc;
|
|
|
+ if (stats.docCount == -1) {
|
|
|
+ this.docCount = -1;
|
|
|
+ } else if (this.docCount != -1) {
|
|
|
+ this.docCount += stats.docCount;
|
|
|
+ }
|
|
|
+ if (stats.sumDocFreq == -1) {
|
|
|
+ this.sumDocFreq = -1;
|
|
|
+ } else if (this.sumDocFreq != -1) {
|
|
|
+ this.sumDocFreq += stats.sumDocFreq;
|
|
|
+ }
|
|
|
+ if (stats.sumTotalTermFreq == -1) {
|
|
|
+ this.sumTotalTermFreq = -1;
|
|
|
+ } else if (this.sumTotalTermFreq != -1) {
|
|
|
+ this.sumTotalTermFreq += stats.sumTotalTermFreq;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
|
|
+ builder.startObject();
|
|
|
+ builder.field(Fields.MAX_DOC, maxDoc);
|
|
|
+ builder.field(Fields.DOC_COUNT, docCount);
|
|
|
+ builder.field(Fields.DENSITY, getDensity());
|
|
|
+ builder.field(Fields.SUM_DOC_FREQ, sumDocFreq);
|
|
|
+ builder.field(Fields.SUM_TOTAL_TERM_FREQ, sumTotalTermFreq);
|
|
|
+ toInnerXContent(builder);
|
|
|
+ builder.endObject();
|
|
|
+ return builder;
|
|
|
+ }
|
|
|
+
|
|
|
+ protected void toInnerXContent(XContentBuilder builder) throws IOException {
|
|
|
+ builder.field(Fields.MIN_VALUE, minValue);
|
|
|
+ builder.field(Fields.MAX_VALUE, maxValue);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void readFrom(StreamInput in) throws IOException {
|
|
|
+ maxDoc = in.readVLong();
|
|
|
+ docCount = in.readLong();
|
|
|
+ sumDocFreq = in.readLong();
|
|
|
+ sumTotalTermFreq = in.readLong();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void writeTo(StreamOutput out) throws IOException {
|
|
|
+ out.writeByte(type);
|
|
|
+ out.writeVLong(maxDoc);
|
|
|
+ out.writeLong(docCount);
|
|
|
+ out.writeLong(sumDocFreq);
|
|
|
+ out.writeLong(sumTotalTermFreq);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static class Long extends FieldStats<java.lang.Long> {
|
|
|
+
|
|
|
+ public Long() {
|
|
|
+ }
|
|
|
+
|
|
|
+ public Long(long maxDoc, long docCount, long sumDocFreq, long sumTotalTermFreq, long minValue, long maxValue) {
|
|
|
+ this(0, maxDoc, docCount, sumDocFreq, sumTotalTermFreq, minValue, maxValue);
|
|
|
+ }
|
|
|
+
|
|
|
+ protected Long(int type, long maxDoc, long docCount, long sumDocFreq, long sumTotalTermFreq, long minValue, long maxValue) {
|
|
|
+ super(type, maxDoc, docCount, sumDocFreq, sumTotalTermFreq);
|
|
|
+ this.minValue = minValue;
|
|
|
+ this.maxValue = maxValue;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String getMinValue() {
|
|
|
+ return String.valueOf(minValue.longValue());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String getMaxValue() {
|
|
|
+ return String.valueOf(maxValue.longValue());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void append(FieldStats stats) {
|
|
|
+ super.append(stats);
|
|
|
+ Long other = (Long) stats;
|
|
|
+ this.minValue = Math.min(other.minValue, minValue);
|
|
|
+ this.maxValue = Math.max(other.maxValue, maxValue);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void readFrom(StreamInput in) throws IOException {
|
|
|
+ super.readFrom(in);
|
|
|
+ minValue = in.readLong();
|
|
|
+ maxValue = in.readLong();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void writeTo(StreamOutput out) throws IOException {
|
|
|
+ super.writeTo(out);
|
|
|
+ out.writeLong(minValue);
|
|
|
+ out.writeLong(maxValue);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public static final class Float extends FieldStats<java.lang.Float> {
|
|
|
+
|
|
|
+ public Float() {
|
|
|
+ }
|
|
|
+
|
|
|
+ public Float(long maxDoc, long docCount, long sumDocFreq, long sumTotalTermFreq, float minValue, float maxValue) {
|
|
|
+ super(1, maxDoc, docCount, sumDocFreq, sumTotalTermFreq);
|
|
|
+ this.minValue = minValue;
|
|
|
+ this.maxValue = maxValue;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String getMinValue() {
|
|
|
+ return String.valueOf(minValue.floatValue());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String getMaxValue() {
|
|
|
+ return String.valueOf(maxValue.floatValue());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void append(FieldStats stats) {
|
|
|
+ super.append(stats);
|
|
|
+ Float other = (Float) stats;
|
|
|
+ this.minValue = Math.min(other.minValue, minValue);
|
|
|
+ this.maxValue = Math.max(other.maxValue, maxValue);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void readFrom(StreamInput in) throws IOException {
|
|
|
+ super.readFrom(in);
|
|
|
+ minValue = in.readFloat();
|
|
|
+ maxValue = in.readFloat();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void writeTo(StreamOutput out) throws IOException {
|
|
|
+ super.writeTo(out);
|
|
|
+ out.writeFloat(minValue);
|
|
|
+ out.writeFloat(maxValue);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public static final class Double extends FieldStats<java.lang.Double> {
|
|
|
+
|
|
|
+ public Double() {
|
|
|
+ }
|
|
|
+
|
|
|
+ public Double(long maxDoc, long docCount, long sumDocFreq, long sumTotalTermFreq, double minValue, double maxValue) {
|
|
|
+ super(2, maxDoc, docCount, sumDocFreq, sumTotalTermFreq);
|
|
|
+ this.minValue = minValue;
|
|
|
+ this.maxValue = maxValue;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String getMinValue() {
|
|
|
+ return String.valueOf(minValue.doubleValue());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String getMaxValue() {
|
|
|
+ return String.valueOf(maxValue.doubleValue());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void append(FieldStats stats) {
|
|
|
+ super.append(stats);
|
|
|
+ Double other = (Double) stats;
|
|
|
+ this.minValue = Math.min(other.minValue, minValue);
|
|
|
+ this.maxValue = Math.max(other.maxValue, maxValue);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void readFrom(StreamInput in) throws IOException {
|
|
|
+ super.readFrom(in);
|
|
|
+ minValue = in.readDouble();
|
|
|
+ maxValue = in.readDouble();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void writeTo(StreamOutput out) throws IOException {
|
|
|
+ super.writeTo(out);
|
|
|
+ out.writeDouble(minValue);
|
|
|
+ out.writeDouble(maxValue);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public static final class Text extends FieldStats<BytesRef> {
|
|
|
+
|
|
|
+ public Text() {
|
|
|
+ }
|
|
|
+
|
|
|
+ public Text(long maxDoc, long docCount, long sumDocFreq, long sumTotalTermFreq, BytesRef minValue, BytesRef maxValue) {
|
|
|
+ super(3, maxDoc, docCount, sumDocFreq, sumTotalTermFreq);
|
|
|
+ this.minValue = minValue;
|
|
|
+ this.maxValue = maxValue;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String getMinValue() {
|
|
|
+ return minValue.utf8ToString();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String getMaxValue() {
|
|
|
+ return maxValue.utf8ToString();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void append(FieldStats stats) {
|
|
|
+ super.append(stats);
|
|
|
+ Text other = (Text) stats;
|
|
|
+ if (other.minValue.compareTo(minValue) < 0) {
|
|
|
+ minValue = other.minValue;
|
|
|
+ }
|
|
|
+ if (other.maxValue.compareTo(maxValue) > 0) {
|
|
|
+ maxValue = other.maxValue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected void toInnerXContent(XContentBuilder builder) throws IOException {
|
|
|
+ builder.field(Fields.MIN_VALUE, getMinValue());
|
|
|
+ builder.field(Fields.MAX_VALUE, getMaxValue());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void readFrom(StreamInput in) throws IOException {
|
|
|
+ super.readFrom(in);
|
|
|
+ minValue = in.readBytesRef();
|
|
|
+ maxValue = in.readBytesRef();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void writeTo(StreamOutput out) throws IOException {
|
|
|
+ super.writeTo(out);
|
|
|
+ out.writeBytesRef(minValue);
|
|
|
+ out.writeBytesRef(maxValue);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public static final class Date extends Long {
|
|
|
+
|
|
|
+ private FormatDateTimeFormatter dateFormatter;
|
|
|
+
|
|
|
+ public Date() {
|
|
|
+ }
|
|
|
+
|
|
|
+ public Date(long maxDoc, long docCount, long sumDocFreq, long sumTotalTermFreq, long minValue, long maxValue, FormatDateTimeFormatter dateFormatter) {
|
|
|
+ super(4, maxDoc, docCount, sumDocFreq, sumTotalTermFreq, minValue, maxValue);
|
|
|
+ this.dateFormatter = dateFormatter;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String getMinValue() {
|
|
|
+ return dateFormatter.printer().print(minValue);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String getMaxValue() {
|
|
|
+ return dateFormatter.printer().print(maxValue);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected void toInnerXContent(XContentBuilder builder) throws IOException {
|
|
|
+ builder.field(Fields.MIN_VALUE, getMinValue());
|
|
|
+ builder.field(Fields.MAX_VALUE, getMaxValue());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void readFrom(StreamInput in) throws IOException {
|
|
|
+ super.readFrom(in);
|
|
|
+ dateFormatter = Joda.forPattern(in.readString());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void writeTo(StreamOutput out) throws IOException {
|
|
|
+ super.writeTo(out);
|
|
|
+ out.writeString(dateFormatter.format());
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public static FieldStats read(StreamInput in) throws IOException {
|
|
|
+ FieldStats stats;
|
|
|
+ byte type = in.readByte();
|
|
|
+ switch (type) {
|
|
|
+ case 0:
|
|
|
+ stats = new Long();
|
|
|
+ break;
|
|
|
+ case 1:
|
|
|
+ stats = new Float();
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ stats = new Double();
|
|
|
+ break;
|
|
|
+ case 3:
|
|
|
+ stats = new Text();
|
|
|
+ break;
|
|
|
+ case 4:
|
|
|
+ stats = new Date();
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ throw new IllegalArgumentException("Illegal type [" + type + "]");
|
|
|
+ }
|
|
|
+ stats.type = type;
|
|
|
+ stats.readFrom(in);
|
|
|
+ return stats;
|
|
|
+ }
|
|
|
+
|
|
|
+ private final static class Fields {
|
|
|
+
|
|
|
+ final static XContentBuilderString MAX_DOC = new XContentBuilderString("max_doc");
|
|
|
+ final static XContentBuilderString DOC_COUNT = new XContentBuilderString("doc_count");
|
|
|
+ final static XContentBuilderString DENSITY = new XContentBuilderString("density");
|
|
|
+ final static XContentBuilderString SUM_DOC_FREQ = new XContentBuilderString("sum_doc_freq");
|
|
|
+ final static XContentBuilderString SUM_TOTAL_TERM_FREQ = new XContentBuilderString("sum_total_term_freq");
|
|
|
+ final static XContentBuilderString MIN_VALUE = new XContentBuilderString("min_value");
|
|
|
+ final static XContentBuilderString MAX_VALUE = new XContentBuilderString("max_value");
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|