From 839d4f3898ca8ab5eec61879843d696c8556b54c Mon Sep 17 00:00:00 2001 From: Artur Konczak Date: Mon, 10 Jun 2013 11:21:50 +0100 Subject: [PATCH 1/2] DATAES-10 added support for term and range facet --- .../core/ElasticsearchTemplate.java | 1 + .../elasticsearch/core/MappingBuilder.java | 2 +- .../AbstractFacetRequest.java | 2 +- .../elasticsearch/core/facet/FacetMapper.java | 30 ++- .../core/{query => facet}/FacetRequest.java | 2 +- .../request}/NativeFacetRequest.java | 7 +- .../core/facet/request/RangeFacetRequest.java | 72 +++++++ .../request/RangeFacetRequestBuilder.java | 52 +++++ .../core/facet/request/TermFacetOrder.java | 10 + .../core/facet/request/TermFacetRequest.java | 93 +++++++++ .../request/TermFacetRequestBuilder.java | 76 ++++++++ .../core/facet/result/Range.java | 46 +++++ .../core/facet/result/RangeResult.java | 29 +++ .../core/facet/{ => result}/Term.java | 2 +- .../core/facet/{ => result}/TermResult.java | 5 +- .../core/query/NativeSearchQuery.java | 1 + .../core/query/NativeSearchQueryBuilder.java | 1 + .../elasticsearch/core/query/SearchQuery.java | 1 + .../core/query/TermFacetRequest.java | 64 ------- .../core/query/TermFacetRequestBuilder.java | 54 ------ .../data/elasticsearch/Article.java | 10 + .../data/elasticsearch/ArticleBuilder.java | 11 +- .../core/ElasticsearchTemplateFacetTests.java | 177 ++++++++++++++++-- 23 files changed, 594 insertions(+), 154 deletions(-) rename src/main/java/org/springframework/data/elasticsearch/core/{query => facet}/AbstractFacetRequest.java (91%) rename src/main/java/org/springframework/data/elasticsearch/core/{query => facet}/FacetRequest.java (83%) rename src/main/java/org/springframework/data/elasticsearch/core/{query => facet/request}/NativeFacetRequest.java (66%) create mode 100644 src/main/java/org/springframework/data/elasticsearch/core/facet/request/RangeFacetRequest.java create mode 100644 src/main/java/org/springframework/data/elasticsearch/core/facet/request/RangeFacetRequestBuilder.java create mode 100644 src/main/java/org/springframework/data/elasticsearch/core/facet/request/TermFacetOrder.java create mode 100644 src/main/java/org/springframework/data/elasticsearch/core/facet/request/TermFacetRequest.java create mode 100644 src/main/java/org/springframework/data/elasticsearch/core/facet/request/TermFacetRequestBuilder.java create mode 100644 src/main/java/org/springframework/data/elasticsearch/core/facet/result/Range.java create mode 100644 src/main/java/org/springframework/data/elasticsearch/core/facet/result/RangeResult.java rename src/main/java/org/springframework/data/elasticsearch/core/facet/{ => result}/Term.java (86%) rename src/main/java/org/springframework/data/elasticsearch/core/facet/{ => result}/TermResult.java (67%) delete mode 100644 src/main/java/org/springframework/data/elasticsearch/core/query/TermFacetRequest.java delete mode 100644 src/main/java/org/springframework/data/elasticsearch/core/query/TermFacetRequestBuilder.java diff --git a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java index b2f890187..39e11a106 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java @@ -47,6 +47,7 @@ import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter; import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter; import org.springframework.data.elasticsearch.core.facet.FacetMapper; +import org.springframework.data.elasticsearch.core.facet.FacetRequest; import org.springframework.data.elasticsearch.core.facet.FacetResult; import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity; import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext; diff --git a/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java b/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java index 362386352..ee43c9b99 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java @@ -17,7 +17,7 @@ package org.springframework.data.elasticsearch.core; import org.elasticsearch.common.xcontent.XContentBuilder; import org.springframework.data.elasticsearch.annotations.*; -import org.springframework.data.elasticsearch.core.query.FacetRequest; +import org.springframework.data.elasticsearch.core.facet.FacetRequest; import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/AbstractFacetRequest.java b/src/main/java/org/springframework/data/elasticsearch/core/facet/AbstractFacetRequest.java similarity index 91% rename from src/main/java/org/springframework/data/elasticsearch/core/query/AbstractFacetRequest.java rename to src/main/java/org/springframework/data/elasticsearch/core/facet/AbstractFacetRequest.java index d3ab6f0fd..5963b1164 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/AbstractFacetRequest.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/facet/AbstractFacetRequest.java @@ -1,4 +1,4 @@ -package org.springframework.data.elasticsearch.core.query; +package org.springframework.data.elasticsearch.core.facet; import org.springframework.util.Assert; diff --git a/src/main/java/org/springframework/data/elasticsearch/core/facet/FacetMapper.java b/src/main/java/org/springframework/data/elasticsearch/core/facet/FacetMapper.java index de44dab46..9dd5891fd 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/facet/FacetMapper.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/facet/FacetMapper.java @@ -1,7 +1,12 @@ package org.springframework.data.elasticsearch.core.facet; import org.elasticsearch.search.facet.Facet; +import org.elasticsearch.search.facet.range.RangeFacet; import org.elasticsearch.search.facet.terms.TermsFacet; +import org.springframework.data.elasticsearch.core.facet.result.Range; +import org.springframework.data.elasticsearch.core.facet.result.RangeResult; +import org.springframework.data.elasticsearch.core.facet.result.Term; +import org.springframework.data.elasticsearch.core.facet.result.TermResult; import java.util.ArrayList; import java.util.List; @@ -11,19 +16,32 @@ import java.util.List; */ public class FacetMapper { - public static FacetResult parse(Facet facet){ - if(facet instanceof TermsFacet){ + public static FacetResult parse(Facet facet) { + if (facet instanceof TermsFacet) { return parseTerm((TermsFacet) facet); } + + if (facet instanceof RangeFacet) { + return parseRange((RangeFacet) facet); + } + return null; } private static FacetResult parseTerm(TermsFacet facet) { - List terms = new ArrayList(); - for(TermsFacet.Entry entry:facet.getEntries()){ - terms.add(new Term(entry.getTerm().toString(),entry.getCount())); + List entries = new ArrayList(); + for (TermsFacet.Entry entry : facet.getEntries()) { + entries.add(new Term(entry.getTerm().toString(), entry.getCount())); } - return new TermResult(facet.getName(),terms); + return new TermResult(facet.getName(), entries); + } + + private static FacetResult parseRange(RangeFacet facet) { + List entries = new ArrayList(); + for (RangeFacet.Entry entry : facet.getEntries()) { + entries.add(new Range(entry.getFrom() == Double.NEGATIVE_INFINITY ? null : entry.getFrom(), entry.getTo() == Double.POSITIVE_INFINITY ? null : entry.getTo(), entry.getCount(), entry.getTotal())); + } + return new RangeResult(facet.getName(), entries); } } diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/FacetRequest.java b/src/main/java/org/springframework/data/elasticsearch/core/facet/FacetRequest.java similarity index 83% rename from src/main/java/org/springframework/data/elasticsearch/core/query/FacetRequest.java rename to src/main/java/org/springframework/data/elasticsearch/core/facet/FacetRequest.java index 45976a9ce..8adb0903c 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/FacetRequest.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/facet/FacetRequest.java @@ -1,4 +1,4 @@ -package org.springframework.data.elasticsearch.core.query; +package org.springframework.data.elasticsearch.core.facet; import org.elasticsearch.search.facet.FacetBuilder; diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/NativeFacetRequest.java b/src/main/java/org/springframework/data/elasticsearch/core/facet/request/NativeFacetRequest.java similarity index 66% rename from src/main/java/org/springframework/data/elasticsearch/core/query/NativeFacetRequest.java rename to src/main/java/org/springframework/data/elasticsearch/core/facet/request/NativeFacetRequest.java index fd8e9a017..5a86db19c 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/NativeFacetRequest.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/facet/request/NativeFacetRequest.java @@ -1,6 +1,11 @@ -package org.springframework.data.elasticsearch.core.query; +package org.springframework.data.elasticsearch.core.facet.request; import org.elasticsearch.search.facet.FacetBuilder; +import org.elasticsearch.search.facet.terms.TermsFacetBuilder; +import org.springframework.data.elasticsearch.core.facet.FacetRequest; + +import org.elasticsearch.search.facet.FacetBuilder; +import org.springframework.data.elasticsearch.core.facet.FacetRequest; /** * @author Artur Konczak diff --git a/src/main/java/org/springframework/data/elasticsearch/core/facet/request/RangeFacetRequest.java b/src/main/java/org/springframework/data/elasticsearch/core/facet/request/RangeFacetRequest.java new file mode 100644 index 000000000..a9cd448f0 --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/core/facet/request/RangeFacetRequest.java @@ -0,0 +1,72 @@ +package org.springframework.data.elasticsearch.core.facet.request; + +import org.apache.commons.lang.StringUtils; +import org.elasticsearch.search.facet.FacetBuilder; +import org.elasticsearch.search.facet.FacetBuilders; +import org.elasticsearch.search.facet.range.RangeFacetBuilder; +import org.springframework.data.elasticsearch.core.facet.AbstractFacetRequest; +import org.springframework.util.Assert; + +import java.util.ArrayList; +import java.util.List; + +/** + * Range facet for numeric fields + * + * @author Artur Konczak + */ +public class RangeFacetRequest extends AbstractFacetRequest { + + private String field; + private String keyField; + private String valueField; + + private List from = new ArrayList(); + private List to = new ArrayList(); + + public RangeFacetRequest(String name) { + super(name); + } + + public void setField(String field) { + this.field = field; + } + + public void setFields(String keyField, String valueField) { + this.keyField = keyField; + this.valueField = valueField; + } + + public void range(Double from, Double to) { + if (from == null) { + this.from.add(Double.NEGATIVE_INFINITY); + } else { + this.from.add(from); + } + + if (to == null) { + this.to.add(Double.POSITIVE_INFINITY); + } else { + this.to.add(to); + } + } + + @Override + public FacetBuilder getFacet() { + Assert.notNull(getName(), "Facet name can't be a null !!!"); + Assert.isTrue(StringUtils.isNotBlank(field) || StringUtils.isNotBlank(keyField) && StringUtils.isNotBlank(valueField), "Please select field or key field and value field !!!"); + + RangeFacetBuilder builder = FacetBuilders.rangeFacet(getName()); + if (StringUtils.isNotBlank(keyField)) { + builder.keyField(keyField).valueField(valueField); + } else { + builder.field(field); + } + Assert.notEmpty(from, "Please select at last one range"); + Assert.notEmpty(to, "Please select at last one range"); + for (int i = 0; i < from.size(); i++) { + builder.addRange(from.get(i), to.get(i)); + } + return builder; + } +} diff --git a/src/main/java/org/springframework/data/elasticsearch/core/facet/request/RangeFacetRequestBuilder.java b/src/main/java/org/springframework/data/elasticsearch/core/facet/request/RangeFacetRequestBuilder.java new file mode 100644 index 000000000..ed25ae899 --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/core/facet/request/RangeFacetRequestBuilder.java @@ -0,0 +1,52 @@ +package org.springframework.data.elasticsearch.core.facet.request; + +import org.springframework.data.elasticsearch.core.facet.FacetRequest; + +/** + * Basic range facet + * + * @author Artur Konczak + */ +public class RangeFacetRequestBuilder { + + RangeFacetRequest result; + + public RangeFacetRequestBuilder(String name) { + result = new RangeFacetRequest(name); + } + + public RangeFacetRequestBuilder field(String field) { + result.setField(field); + return this; + } + + public RangeFacetRequestBuilder fields(String keyField, String valueField) { + result.setFields(keyField, valueField); + return this; + } + + + public RangeFacetRequestBuilder range(double from, double to) { + result.range(from, to); + return this; + } + + public RangeFacetRequestBuilder from(double from) { + result.range(from, null); + return this; + } + + public RangeFacetRequestBuilder to(double to) { + result.range(null, to); + return this; + } + + public RangeFacetRequestBuilder applyQueryFilter() { + result.setApplyQueryFilter(true); + return this; + } + + public FacetRequest build() { + return result; + } +} diff --git a/src/main/java/org/springframework/data/elasticsearch/core/facet/request/TermFacetOrder.java b/src/main/java/org/springframework/data/elasticsearch/core/facet/request/TermFacetOrder.java new file mode 100644 index 000000000..ba27e51a8 --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/core/facet/request/TermFacetOrder.java @@ -0,0 +1,10 @@ +package org.springframework.data.elasticsearch.core.facet.request; + +/** + * + */ +public enum TermFacetOrder { + + ascTerm, descTerm, ascCount, descCount; + +} diff --git a/src/main/java/org/springframework/data/elasticsearch/core/facet/request/TermFacetRequest.java b/src/main/java/org/springframework/data/elasticsearch/core/facet/request/TermFacetRequest.java new file mode 100644 index 000000000..ee5f0fd07 --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/core/facet/request/TermFacetRequest.java @@ -0,0 +1,93 @@ +package org.springframework.data.elasticsearch.core.facet.request; + +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +import org.elasticsearch.search.facet.FacetBuilder; +import org.elasticsearch.search.facet.FacetBuilders; +import org.elasticsearch.search.facet.terms.TermsFacet; +import org.elasticsearch.search.facet.terms.TermsFacetBuilder; +import org.springframework.data.elasticsearch.core.facet.AbstractFacetRequest; +import org.springframework.util.Assert; + +/** + * Term facet + * + * @author Artur Konczak + */ +public class TermFacetRequest extends AbstractFacetRequest { + + private String[] fields; + private Object[] excludeTerms; + private int size = 10; + private TermFacetOrder order = TermFacetOrder.descCount; + private boolean allTerms = false; + private String regex = null; + private int regexFlag = 0; + + public TermFacetRequest(String name) { + super(name); + } + + public void setFields(String... fields) { + this.fields = fields; + } + + public void setSize(int size) { + Assert.isTrue(size >= 0, "Size should be bigger then zero !!!"); + this.size = size; + } + + public void setOrder(TermFacetOrder order) { + this.order = order; + } + + public void setExcludeTerms(Object... excludeTerms) { + this.excludeTerms = excludeTerms; + } + + public void setAllTerms(boolean allTerms) { + this.allTerms = allTerms; + } + + public void setRegex(String regex) { + this.regex = regex; + } + + public void setRegex(String regex, int regexFlag) { + this.regex = regex; + this.regexFlag = regexFlag; + } + + @Override + public FacetBuilder getFacet() { + Assert.notEmpty(fields, "Please select at last one field !!!"); + TermsFacetBuilder builder = FacetBuilders.termsFacet(getName()).fields(fields).size(size); + switch (order) { + + case descTerm: + builder.order(TermsFacet.ComparatorType.REVERSE_TERM); + break; + case ascTerm: + builder.order(TermsFacet.ComparatorType.TERM); + break; + case ascCount: + builder.order(TermsFacet.ComparatorType.REVERSE_COUNT); + break; + default: + builder.order(TermsFacet.ComparatorType.COUNT); + } + if (ArrayUtils.isNotEmpty(excludeTerms)) { + builder.exclude(excludeTerms); + } + + if (allTerms) { + builder.allTerms(allTerms); + } + + if (StringUtils.isNotBlank(regex)) { + builder.regex(regex, regexFlag); + } + + return builder; + } +} diff --git a/src/main/java/org/springframework/data/elasticsearch/core/facet/request/TermFacetRequestBuilder.java b/src/main/java/org/springframework/data/elasticsearch/core/facet/request/TermFacetRequestBuilder.java new file mode 100644 index 000000000..2ea124768 --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/core/facet/request/TermFacetRequestBuilder.java @@ -0,0 +1,76 @@ +package org.springframework.data.elasticsearch.core.facet.request; + +import org.springframework.data.elasticsearch.core.facet.FacetRequest; + +/** + * Basic term facet + * + * @author Artur Konczak + */ +public class TermFacetRequestBuilder { + + private TermFacetRequest result; + + public TermFacetRequestBuilder(String name) { + result = new TermFacetRequest(name); + } + + public TermFacetRequestBuilder fields(String... fields) { + result.setFields(fields); + return this; + } + + public TermFacetRequestBuilder size(int size) { + result.setSize(size); + return this; + } + + public TermFacetRequestBuilder excludeTerms(Object... terms) { + result.setExcludeTerms(terms); + return this; + } + + public TermFacetRequestBuilder allTerms() { + result.setAllTerms(true); + return this; + } + + public TermFacetRequestBuilder regex(String regex) { + result.setRegex(regex); + return this; + } + + public TermFacetRequestBuilder regex(String regex, int regexFlag) { + result.setRegex(regex, regexFlag); + return this; + } + + public TermFacetRequestBuilder ascTerm() { + result.setOrder(TermFacetOrder.ascTerm); + return this; + } + + public TermFacetRequestBuilder descTerm() { + result.setOrder(TermFacetOrder.descTerm); + return this; + } + + public TermFacetRequestBuilder ascCount() { + result.setOrder(TermFacetOrder.ascCount); + return this; + } + + public TermFacetRequestBuilder descCount() { + result.setOrder(TermFacetOrder.descCount); + return this; + } + + public TermFacetRequestBuilder applyQueryFilter() { + result.setApplyQueryFilter(true); + return this; + } + + public FacetRequest build() { + return result; + } +} diff --git a/src/main/java/org/springframework/data/elasticsearch/core/facet/result/Range.java b/src/main/java/org/springframework/data/elasticsearch/core/facet/result/Range.java new file mode 100644 index 000000000..2a0ac63f9 --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/core/facet/result/Range.java @@ -0,0 +1,46 @@ +package org.springframework.data.elasticsearch.core.facet.result; + +/** + * Single range + * + * @author Rizwan Idrees + * @author Mohsin Husen + * @author Artur Konczak + * @author Jonathan Yan + */ +public class Range { + + private Double from; + private Double to; + private long count; + private double total; + + + public Range(Double from, Double to, long count, double total) { + this.from = from; + this.to = to; + this.count = count; + this.total = total; + } + + public Double getFrom() { + return from; + } + + public Double getTo() { + return to; + } + + /** + * Return number of documents in range + * + * @return + */ + public long getCount() { + return count; + } + + public double getTotal() { + return total; + } +} diff --git a/src/main/java/org/springframework/data/elasticsearch/core/facet/result/RangeResult.java b/src/main/java/org/springframework/data/elasticsearch/core/facet/result/RangeResult.java new file mode 100644 index 000000000..0532e32a2 --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/core/facet/result/RangeResult.java @@ -0,0 +1,29 @@ +package org.springframework.data.elasticsearch.core.facet.result; + +import org.springframework.data.elasticsearch.core.facet.AbstactFacetResult; +import org.springframework.data.elasticsearch.core.facet.FacetType; + +import java.util.List; + +/** + * Basic term facet result + * + * @author Rizwan Idrees + * @author Mohsin Husen + * @author Artur Konczak + * @author Jonathan Yan + */ +public class RangeResult extends AbstactFacetResult { + + private List ranges; + + public RangeResult(String name, List ranges) { + super(name, FacetType.range); + this.ranges = ranges; + } + + public List getRanges() { + return ranges; + } + +} diff --git a/src/main/java/org/springframework/data/elasticsearch/core/facet/Term.java b/src/main/java/org/springframework/data/elasticsearch/core/facet/result/Term.java similarity index 86% rename from src/main/java/org/springframework/data/elasticsearch/core/facet/Term.java rename to src/main/java/org/springframework/data/elasticsearch/core/facet/result/Term.java index 964abfb87..349c1c9a8 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/facet/Term.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/facet/result/Term.java @@ -1,4 +1,4 @@ -package org.springframework.data.elasticsearch.core.facet; +package org.springframework.data.elasticsearch.core.facet.result; /** * Single term diff --git a/src/main/java/org/springframework/data/elasticsearch/core/facet/TermResult.java b/src/main/java/org/springframework/data/elasticsearch/core/facet/result/TermResult.java similarity index 67% rename from src/main/java/org/springframework/data/elasticsearch/core/facet/TermResult.java rename to src/main/java/org/springframework/data/elasticsearch/core/facet/result/TermResult.java index 8efe719a3..c4c5628cc 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/facet/TermResult.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/facet/result/TermResult.java @@ -1,6 +1,7 @@ -package org.springframework.data.elasticsearch.core.facet; +package org.springframework.data.elasticsearch.core.facet.result; -import org.springframework.util.Assert; +import org.springframework.data.elasticsearch.core.facet.AbstactFacetResult; +import org.springframework.data.elasticsearch.core.facet.FacetType; import java.util.List; diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQuery.java b/src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQuery.java index 2f179c4f2..46f80e264 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQuery.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQuery.java @@ -19,6 +19,7 @@ import org.elasticsearch.index.query.FilterBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.search.facet.FacetBuilder; import org.elasticsearch.search.sort.SortBuilder; +import org.springframework.data.elasticsearch.core.facet.FacetRequest; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQueryBuilder.java b/src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQueryBuilder.java index 6047023b4..6d8e354c4 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQueryBuilder.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQueryBuilder.java @@ -20,6 +20,7 @@ import org.elasticsearch.index.query.FilterBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.search.sort.SortBuilder; import org.springframework.data.domain.Pageable; +import org.springframework.data.elasticsearch.core.facet.FacetRequest; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/SearchQuery.java b/src/main/java/org/springframework/data/elasticsearch/core/query/SearchQuery.java index 5c8463faf..21a925073 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/SearchQuery.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/query/SearchQuery.java @@ -19,6 +19,7 @@ import org.elasticsearch.index.query.FilterBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.search.facet.FacetBuilder; import org.elasticsearch.search.sort.SortBuilder; +import org.springframework.data.elasticsearch.core.facet.FacetRequest; import java.util.List; diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/TermFacetRequest.java b/src/main/java/org/springframework/data/elasticsearch/core/query/TermFacetRequest.java deleted file mode 100644 index eb936a455..000000000 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/TermFacetRequest.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.springframework.data.elasticsearch.core.query; - -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang.ArrayUtils; -import org.elasticsearch.search.facet.FacetBuilder; -import org.elasticsearch.search.facet.FacetBuilders; -import org.elasticsearch.search.facet.terms.TermsFacet; -import org.elasticsearch.search.facet.terms.TermsFacetBuilder; -import org.springframework.util.Assert; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Basic term facet - * - * @author Artur Konczak - */ -public class TermFacetRequest extends AbstractFacetRequest { - - private String[] fields; - private int size = 10; - private TermsFacet.ComparatorType order; - - public TermFacetRequest(String name) { - super(name); - } - - public void setFields(String... fields) { - this.fields = fields; - } - - public void setSize(int size) { - Assert.isTrue(size <= 0, "Size should be bigger then zero !!!"); - this.size = size; - } - - public void ascTerm() { - order = TermsFacet.ComparatorType.TERM; - } - - public void descTerm() { - order = TermsFacet.ComparatorType.REVERSE_TERM; - } - - public void ascCount() { - order = TermsFacet.ComparatorType.REVERSE_COUNT; - } - - public void descCount() { - order = TermsFacet.ComparatorType.COUNT; - } - - @Override - public FacetBuilder getFacet() { - Assert.notEmpty(fields, "Please select at last one field !!!"); - TermsFacetBuilder builder = FacetBuilders.termsFacet(getName()).fields(fields).size(size); - if (order != null) { - builder.order(order); - } - return builder; - } -} diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/TermFacetRequestBuilder.java b/src/main/java/org/springframework/data/elasticsearch/core/query/TermFacetRequestBuilder.java deleted file mode 100644 index a4dcbe7df..000000000 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/TermFacetRequestBuilder.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.springframework.data.elasticsearch.core.query; - -/** - * Basic term facet - * - * @author Artur Konczak - */ -public class TermFacetRequestBuilder { - - private TermFacetRequest result; - - public TermFacetRequestBuilder(String name) { - result = new TermFacetRequest(name); - } - - public TermFacetRequestBuilder withFields(String... fields) { - result.setFields(fields); - return this; - } - - public TermFacetRequestBuilder withSize(int size) { - result.setSize(size); - return this; - } - - public TermFacetRequestBuilder ascTerm() { - result.ascTerm(); - return this; - } - - public TermFacetRequestBuilder descTerm() { - result.descTerm(); - return this; - } - - public TermFacetRequestBuilder ascCount() { - result.ascCount(); - return this; - } - - public TermFacetRequestBuilder descCount() { - result.descCount(); - return this; - } - - public TermFacetRequestBuilder applyQueryFilter() { - result.setApplyQueryFilter(true); - return this; - } - - public TermFacetRequest build() { - return result; - } -} diff --git a/src/test/java/org/springframework/data/elasticsearch/Article.java b/src/test/java/org/springframework/data/elasticsearch/Article.java index c0f217372..89b46ca2b 100644 --- a/src/test/java/org/springframework/data/elasticsearch/Article.java +++ b/src/test/java/org/springframework/data/elasticsearch/Article.java @@ -34,6 +34,8 @@ public class Article { @Field(type = Integer, store = true) private List publishedYears = new ArrayList(); + private int score; + public Article() { } @@ -73,4 +75,12 @@ public class Article { public void setPublishedYears(List publishedYears) { this.publishedYears = publishedYears; } + + public int getScore() { + return score; + } + + public void setScore(int score) { + this.score = score; + } } diff --git a/src/test/java/org/springframework/data/elasticsearch/ArticleBuilder.java b/src/test/java/org/springframework/data/elasticsearch/ArticleBuilder.java index 16a522a4a..bba9a5844 100644 --- a/src/test/java/org/springframework/data/elasticsearch/ArticleBuilder.java +++ b/src/test/java/org/springframework/data/elasticsearch/ArticleBuilder.java @@ -1,11 +1,7 @@ package org.springframework.data.elasticsearch; -import org.springframework.data.annotation.Id; -import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.core.query.IndexQuery; -import java.util.ArrayList; - /** * Simple type to test facets */ @@ -32,11 +28,16 @@ public class ArticleBuilder { return this; } + public ArticleBuilder score(int score) { + resutl.setScore(score); + return this; + } + public Article build() { return resutl; } - public IndexQuery buildIndex(){ + public IndexQuery buildIndex() { IndexQuery indexQuery = new IndexQuery(); indexQuery.setId(resutl.getId()); indexQuery.setObject(resutl); diff --git a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateFacetTests.java b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateFacetTests.java index 4b2e8a616..47c29c16d 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateFacetTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateFacetTests.java @@ -23,17 +23,22 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.elasticsearch.Article; import org.springframework.data.elasticsearch.ArticleBuilder; -import org.springframework.data.elasticsearch.core.facet.Term; -import org.springframework.data.elasticsearch.core.facet.TermResult; -import org.springframework.data.elasticsearch.core.query.*; +import org.springframework.data.elasticsearch.core.facet.request.NativeFacetRequest; +import org.springframework.data.elasticsearch.core.facet.request.RangeFacetRequestBuilder; +import org.springframework.data.elasticsearch.core.facet.request.TermFacetRequestBuilder; +import org.springframework.data.elasticsearch.core.facet.result.Range; +import org.springframework.data.elasticsearch.core.facet.result.RangeResult; +import org.springframework.data.elasticsearch.core.facet.result.Term; +import org.springframework.data.elasticsearch.core.facet.result.TermResult; +import org.springframework.data.elasticsearch.core.query.IndexQuery; +import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; +import org.springframework.data.elasticsearch.core.query.SearchQuery; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import static org.elasticsearch.index.query.FilterBuilders.termFilter; -import static org.elasticsearch.index.query.QueryBuilders.fieldQuery; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; +import static org.junit.Assert.assertThat; /** * @author Rizwan Idrees @@ -62,10 +67,10 @@ public class ElasticsearchTemplateFacetTests { elasticsearchTemplate.putMapping(Article.class); elasticsearchTemplate.refresh(Article.class, true); - IndexQuery article1 = new ArticleBuilder("1").title("article four").addAuthor(RIZWAN_IDREES).addAuthor(ARTUR_KONCZAK).addAuthor(MOHSIN_HUSEN).addAuthor(JONATHAN_YAN).buildIndex(); - IndexQuery article2 = new ArticleBuilder("2").title("article three").addAuthor(RIZWAN_IDREES).addAuthor(ARTUR_KONCZAK).addAuthor(MOHSIN_HUSEN).addPublishedYear(YEAR_2000).buildIndex(); - IndexQuery article3 = new ArticleBuilder("3").title("article two").addAuthor(RIZWAN_IDREES).addAuthor(ARTUR_KONCZAK).addPublishedYear(YEAR_2001).addPublishedYear(YEAR_2000).buildIndex(); - IndexQuery article4 = new ArticleBuilder("4").title("article one").addAuthor(RIZWAN_IDREES).addPublishedYear(YEAR_2002).addPublishedYear(YEAR_2001).addPublishedYear(YEAR_2000).buildIndex(); + IndexQuery article1 = new ArticleBuilder("1").title("article four").addAuthor(RIZWAN_IDREES).addAuthor(ARTUR_KONCZAK).addAuthor(MOHSIN_HUSEN).addAuthor(JONATHAN_YAN).score(10).buildIndex(); + IndexQuery article2 = new ArticleBuilder("2").title("article three").addAuthor(RIZWAN_IDREES).addAuthor(ARTUR_KONCZAK).addAuthor(MOHSIN_HUSEN).addPublishedYear(YEAR_2000).score(20).buildIndex(); + IndexQuery article3 = new ArticleBuilder("3").title("article two").addAuthor(RIZWAN_IDREES).addAuthor(ARTUR_KONCZAK).addPublishedYear(YEAR_2001).addPublishedYear(YEAR_2000).score(30).buildIndex(); + IndexQuery article4 = new ArticleBuilder("4").title("article one").addAuthor(RIZWAN_IDREES).addPublishedYear(YEAR_2002).addPublishedYear(YEAR_2001).addPublishedYear(YEAR_2000).score(40).buildIndex(); elasticsearchTemplate.index(article1); elasticsearchTemplate.index(article2); @@ -79,7 +84,7 @@ public class ElasticsearchTemplateFacetTests { // given String facetName = "fauthors"; - SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withFacet(new TermFacetRequestBuilder(facetName).withFields("authors.untouched").build()).build(); + SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withFacet(new TermFacetRequestBuilder(facetName).fields("authors.untouched").build()).build(); // when FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); // then @@ -110,7 +115,7 @@ public class ElasticsearchTemplateFacetTests { String facetName = "fauthors"; SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) .withFilter(FilterBuilders.notFilter(FilterBuilders.termFilter("title", "four"))) - .withFacet(new TermFacetRequestBuilder(facetName).applyQueryFilter().withFields("authors.untouched").build()).build(); + .withFacet(new TermFacetRequestBuilder(facetName).applyQueryFilter().fields("authors.untouched").build()).build(); // when FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); // then @@ -130,13 +135,35 @@ public class ElasticsearchTemplateFacetTests { assertThat(term.getCount(), is(1)); } + @Test + public void shouldExcludeTermsFromFacetedAuthorsForGivenQuery() { + // given + String facetName = "fauthors"; + SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) + .withFilter(FilterBuilders.notFilter(FilterBuilders.termFilter("title", "four"))) + .withFacet(new TermFacetRequestBuilder(facetName).applyQueryFilter().fields("authors.untouched").excludeTerms(RIZWAN_IDREES, ARTUR_KONCZAK).build()).build(); + // when + FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); + // then + assertThat(result.getNumberOfElements(), is(equalTo(3))); + + TermResult facet = (TermResult) result.getFacet(facetName); + + assertThat(facet.getTerms().size(), is(1)); + + Term term = facet.getTerms().get(0); + assertThat(term.getTerm(), is(MOHSIN_HUSEN)); + assertThat(term.getCount(), is(1)); + + } + @Test public void shouldReturnFacetedAuthorsForGivenQueryOrderedByTerm() { // given String facetName = "fauthors"; SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) - .withFacet(new TermFacetRequestBuilder(facetName).withFields("authors.untouched").ascTerm().build()).build(); + .withFacet(new TermFacetRequestBuilder(facetName).fields("authors.untouched").ascTerm().build()).build(); // when FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); // then @@ -167,7 +194,7 @@ public class ElasticsearchTemplateFacetTests { // given String facetName = "fauthors"; SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) - .withFacet(new TermFacetRequestBuilder(facetName).withFields("authors.untouched").ascCount().build()).build(); + .withFacet(new TermFacetRequestBuilder(facetName).fields("authors.untouched").ascCount().build()).build(); // when FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); // then @@ -197,7 +224,7 @@ public class ElasticsearchTemplateFacetTests { // given String facetName = "fyears"; SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) - .withFacet(new TermFacetRequestBuilder(facetName).withFields("publishedYears").descCount().build()).build(); + .withFacet(new TermFacetRequestBuilder(facetName).fields("publishedYears").descCount().build()).build(); // when FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); // then @@ -227,7 +254,7 @@ public class ElasticsearchTemplateFacetTests { // given String facetName = "fyears"; SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) - .withFacet(new TermFacetRequestBuilder(facetName).withFields("publishedYears", "authors.untouched").ascTerm().build()).build(); + .withFacet(new TermFacetRequestBuilder(facetName).fields("publishedYears", "authors.untouched").ascTerm().build()).build(); // when FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); // then @@ -274,8 +301,8 @@ public class ElasticsearchTemplateFacetTests { String numberFacetName = "fAuthors"; String stringFacetName = "fyears"; SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) - .withFacet(new TermFacetRequestBuilder(numberFacetName).withFields("publishedYears").ascTerm().build()) - .withFacet(new TermFacetRequestBuilder(stringFacetName).withFields("authors.untouched").ascTerm().build()) + .withFacet(new TermFacetRequestBuilder(numberFacetName).fields("publishedYears").ascTerm().build()) + .withFacet(new TermFacetRequestBuilder(stringFacetName).fields("authors.untouched").ascTerm().build()) .build(); // when FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); @@ -346,5 +373,119 @@ public class ElasticsearchTemplateFacetTests { assertThat(term.getCount(), is(1)); } + @Test + public void shouldFilterResultByRegexForGivenQuery() { + // given + String facetName = "regex_authors"; + SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) + .withFilter(FilterBuilders.notFilter(FilterBuilders.termFilter("title", "four"))) + .withFacet(new TermFacetRequestBuilder(facetName).applyQueryFilter().fields("authors.untouched").regex("Art.*").build()).build(); + // when + FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); + // then + assertThat(result.getNumberOfElements(), is(equalTo(3))); + + TermResult facet = (TermResult) result.getFacet(facetName); + + assertThat(facet.getTerms().size(), is(1)); + + Term term = facet.getTerms().get(0); + assertThat(term.getTerm(), is(ARTUR_KONCZAK)); + assertThat(term.getCount(), is(2)); + + } + + @Test + public void shouldReturnAllTermsForGivenQuery() { + // given + String facetName = "all_authors"; + SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) + .withFilter(FilterBuilders.notFilter(FilterBuilders.termFilter("title", "four"))) + .withFacet(new TermFacetRequestBuilder(facetName).applyQueryFilter().fields("authors.untouched").allTerms().build()).build(); + // when + FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); + // then + assertThat(result.getNumberOfElements(), is(equalTo(3))); + + TermResult facet = (TermResult) result.getFacet(facetName); + + assertThat(facet.getTerms().size(), is(4)); + } + + + @Test + public void shouldReturnRangeFacetForGivenQuery() { + // given + String facetName = "rangeYears"; + SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) + .withFacet( + new RangeFacetRequestBuilder(facetName).field("publishedYears") + .to(YEAR_2000).range(YEAR_2000, YEAR_2002).from(YEAR_2002).build() + ).build(); + // when + FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); + // then + assertThat(result.getNumberOfElements(), is(equalTo(4))); + + RangeResult facet = (RangeResult) result.getFacet(facetName); + assertThat(facet.getRanges().size(), is(equalTo(3))); + + + Range range = facet.getRanges().get(0); + assertThat(range.getFrom(), nullValue()); + assertThat(range.getTo(), is((double) YEAR_2000)); + assertThat(range.getCount(), is(0L)); + assertThat(range.getTotal(), is(0.0)); + + range = facet.getRanges().get(1); + assertThat(range.getFrom(), is((double) YEAR_2000)); + assertThat(range.getTo(), is((double) YEAR_2002)); + assertThat(range.getCount(), is(3L)); + assertThat(range.getTotal(), is(6000.0)); + + range = facet.getRanges().get(2); + assertThat(range.getFrom(), is((double) YEAR_2002)); + assertThat(range.getTo(), nullValue()); + assertThat(range.getCount(), is(1L)); + assertThat(range.getTotal(), is(2002.0)); + } + + @Test + public void shouldReturnKeyValueRangeFacetForGivenQuery() { + // given + String facetName = "rangeScoreOverYears"; + SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) + .withFacet( + new RangeFacetRequestBuilder(facetName).fields("publishedYears", "score") + .to(YEAR_2000).range(YEAR_2000, YEAR_2002).from(YEAR_2002).build() + ).build(); + // when + FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); + // then + assertThat(result.getNumberOfElements(), is(equalTo(4))); + + RangeResult facet = (RangeResult) result.getFacet(facetName); + assertThat(facet.getRanges().size(), is(equalTo(3))); + + + Range range = facet.getRanges().get(0); + assertThat(range.getFrom(), nullValue()); + assertThat(range.getTo(), is((double) YEAR_2000)); + assertThat(range.getCount(), is(0L)); + assertThat(range.getTotal(), is(0.0)); + + range = facet.getRanges().get(1); + assertThat(range.getFrom(), is((double) YEAR_2000)); + assertThat(range.getTo(), is((double) YEAR_2002)); + assertThat(range.getCount(), is(3L)); + assertThat(range.getTotal(), is(90.0)); + + range = facet.getRanges().get(2); + assertThat(range.getFrom(), is((double) YEAR_2002)); + assertThat(range.getTo(), nullValue()); + assertThat(range.getCount(), is(1L)); + assertThat(range.getTotal(), is(40.0)); + } + } From aa6a5e3753e8e58da2591ace2993617623bcfb6f Mon Sep 17 00:00:00 2001 From: Artur Konczak Date: Wed, 12 Jun 2013 18:58:05 +0100 Subject: [PATCH 2/2] DATAES-10 added 3 additional properties to Range Facet --- .../elasticsearch/core/facet/FacetMapper.java | 2 +- .../core/facet/result/Range.java | 21 +++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/springframework/data/elasticsearch/core/facet/FacetMapper.java b/src/main/java/org/springframework/data/elasticsearch/core/facet/FacetMapper.java index 9dd5891fd..a1cd30e89 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/facet/FacetMapper.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/facet/FacetMapper.java @@ -39,7 +39,7 @@ public class FacetMapper { private static FacetResult parseRange(RangeFacet facet) { List entries = new ArrayList(); for (RangeFacet.Entry entry : facet.getEntries()) { - entries.add(new Range(entry.getFrom() == Double.NEGATIVE_INFINITY ? null : entry.getFrom(), entry.getTo() == Double.POSITIVE_INFINITY ? null : entry.getTo(), entry.getCount(), entry.getTotal())); + entries.add(new Range(entry.getFrom() == Double.NEGATIVE_INFINITY ? null : entry.getFrom(), entry.getTo() == Double.POSITIVE_INFINITY ? null : entry.getTo(), entry.getCount(), entry.getTotal(), entry.getTotalCount(), entry.getMin(), entry.getMax())); } return new RangeResult(facet.getName(), entries); } diff --git a/src/main/java/org/springframework/data/elasticsearch/core/facet/result/Range.java b/src/main/java/org/springframework/data/elasticsearch/core/facet/result/Range.java index 2a0ac63f9..71a0ff7a5 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/facet/result/Range.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/facet/result/Range.java @@ -14,13 +14,18 @@ public class Range { private Double to; private long count; private double total; + private double totalCount; + private double min = Double.POSITIVE_INFINITY; + private double max = Double.NEGATIVE_INFINITY; - - public Range(Double from, Double to, long count, double total) { + public Range(Double from, Double to, long count, double total, double totalCount, double min, double max) { this.from = from; this.to = to; this.count = count; this.total = total; + this.totalCount = totalCount; + this.min = min; + this.max = max; } public Double getFrom() { @@ -43,4 +48,16 @@ public class Range { public double getTotal() { return total; } + + public double getTotalCount() { + return totalCount; + } + + public double getMin() { + return min; + } + + public double getMax() { + return max; + } }