Skip to content

Commit e684d4f

Browse files
committed
DATAES-26 - added support for histogram facets
1 parent 35cb4e6 commit e684d4f

File tree

9 files changed

+494
-0
lines changed

9 files changed

+494
-0
lines changed

src/main/java/org/springframework/data/elasticsearch/core/facet/FacetMapper.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ public static FacetResult parse(Facet facet) {
2929
return parseStatistical((StatisticalFacet) facet);
3030
}
3131

32+
if (facet instanceof HistogramFacet) {
33+
return parseHistogram((HistogramFacet) facet);
34+
}
35+
3236
return null;
3337
}
3438

@@ -52,4 +56,12 @@ private static FacetResult parseStatistical(StatisticalFacet facet) {
5256
return new StatisticalResult(facet.getName(), facet.getCount(), facet.getMax(), facet.getMin(), facet.getMean(), facet.getStdDeviation(), facet.getSumOfSquares(), facet.getTotal(), facet.getVariance());
5357
}
5458

59+
private static FacetResult parseHistogram(HistogramFacet facet) {
60+
List<IntervalUnit> entries = new ArrayList<IntervalUnit>();
61+
for (HistogramFacet.Entry entry : facet.getEntries()) {
62+
entries.add(new IntervalUnit(entry.getKey(), entry.getCount(), entry.getTotalCount(), entry.getTotal(), entry.getMean(), entry.getMin(), entry.getMax()));
63+
}
64+
return new HistogramResult(facet.getName(), entries);
65+
}
66+
5567
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package org.springframework.data.elasticsearch.core.facet.request;
2+
3+
import org.apache.commons.lang.StringUtils;
4+
import org.apache.commons.lang.math.NumberUtils;
5+
import org.elasticsearch.search.facet.FacetBuilder;
6+
import org.elasticsearch.search.facet.FacetBuilders;
7+
import org.elasticsearch.search.facet.histogram.HistogramFacetBuilder;
8+
import org.springframework.data.elasticsearch.core.facet.AbstractFacetRequest;
9+
import org.springframework.util.Assert;
10+
11+
import java.util.concurrent.TimeUnit;
12+
13+
/**
14+
* @author Artur Konczak
15+
*/
16+
public class HistogramFacetRequest extends AbstractFacetRequest {
17+
18+
private String field;
19+
private long interval;
20+
private TimeUnit timeUnit;
21+
22+
public HistogramFacetRequest(String name) {
23+
super(name);
24+
}
25+
26+
public void setField(String field) {
27+
this.field = field;
28+
}
29+
30+
public void setInterval(long interval) {
31+
this.interval = interval;
32+
}
33+
34+
public void setTimeUnit(TimeUnit timeUnit) {
35+
this.timeUnit = timeUnit;
36+
}
37+
38+
public FacetBuilder getFacet() {
39+
Assert.notNull(getName(), "Facet name can't be a null !!!");
40+
Assert.isTrue(StringUtils.isNotBlank(field), "Please select field on which to build the facet !!!");
41+
Assert.isTrue(interval > 0, "Please provide interval as positive value greater them zero !!!");
42+
43+
HistogramFacetBuilder builder = FacetBuilders.histogramFacet(getName());
44+
builder.field(field);
45+
46+
if (timeUnit != null) {
47+
builder.interval(interval, timeUnit);
48+
} else {
49+
builder.interval(interval);
50+
}
51+
52+
return builder;
53+
}
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package org.springframework.data.elasticsearch.core.facet.request;
2+
3+
import org.springframework.data.elasticsearch.core.facet.FacetRequest;
4+
5+
import java.util.concurrent.TimeUnit;
6+
7+
/**
8+
* @author Artur Konczak
9+
*/
10+
public class HistogramFacetRequestBuilder {
11+
12+
HistogramFacetRequest result;
13+
14+
public HistogramFacetRequestBuilder(String name) {
15+
result = new HistogramFacetRequest(name);
16+
}
17+
18+
public HistogramFacetRequestBuilder field(String field) {
19+
result.setField(field);
20+
return this;
21+
}
22+
23+
public HistogramFacetRequestBuilder interval(long interval) {
24+
result.setInterval(interval);
25+
return this;
26+
}
27+
28+
public HistogramFacetRequestBuilder timeUnit(TimeUnit timeUnit) {
29+
result.setTimeUnit(timeUnit);
30+
return this;
31+
}
32+
33+
public FacetRequest build() {
34+
return result;
35+
}
36+
37+
public HistogramFacetRequestBuilder applyQueryFilter() {
38+
result.setApplyQueryFilter(true);
39+
return this;
40+
}
41+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.springframework.data.elasticsearch.core.facet.result;
2+
3+
import org.springframework.data.elasticsearch.core.facet.AbstactFacetResult;
4+
import org.springframework.data.elasticsearch.core.facet.FacetType;
5+
6+
import java.util.List;
7+
8+
/**
9+
* @author Artur Konczak
10+
*/
11+
public class HistogramResult extends AbstactFacetResult {
12+
13+
private List<IntervalUnit> terms;
14+
15+
public HistogramResult(String name, List<IntervalUnit> terms) {
16+
super(name, FacetType.term);
17+
this.terms = terms;
18+
}
19+
20+
public List<IntervalUnit> getIntervalUnit() {
21+
return terms;
22+
}
23+
24+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package org.springframework.data.elasticsearch.core.facet.result;
2+
3+
import java.text.SimpleDateFormat;
4+
import java.util.Date;
5+
6+
/**
7+
* Single term
8+
*
9+
* @author Rizwan Idrees
10+
* @author Mohsin Husen
11+
* @author Artur Konczak
12+
* @author Jonathan Yan
13+
*/
14+
public class IntervalUnit {
15+
16+
private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm");
17+
18+
long key;
19+
long count;
20+
long totalCount;
21+
double total;
22+
double mean;
23+
double min;
24+
double max;
25+
26+
public IntervalUnit(long key, long count, long totalCount, double total, double mean, double min, double max) {
27+
this.key = key;
28+
this.count = count;
29+
this.totalCount = totalCount;
30+
this.total = total;
31+
this.mean = mean;
32+
this.min = min;
33+
this.max = max;
34+
}
35+
36+
public long getKey() {
37+
return key;
38+
}
39+
40+
public long getCount() {
41+
return count;
42+
}
43+
44+
public long getTotalCount() {
45+
return totalCount;
46+
}
47+
48+
public double getTotal() {
49+
return total;
50+
}
51+
52+
public double getMean() {
53+
return mean;
54+
}
55+
56+
public double getMin() {
57+
return min;
58+
}
59+
60+
public double getMax() {
61+
return max;
62+
}
63+
64+
@Override
65+
public String toString() {
66+
return "IntervalUnit{" +
67+
"key=" + format.format(new Date(key)) +
68+
", count=" + count +
69+
", totalCount=" + totalCount +
70+
", total=" + total +
71+
", mean=" + mean +
72+
", min=" + min +
73+
", max=" + max +
74+
'}';
75+
}
76+
}

src/test/java/org/springframework/data/elasticsearch/core/facet/ElasticsearchTemplateFacetTests.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,4 +500,32 @@ public void shouldReturnStatisticalFacetForGivenQuery() {
500500
assertThat(facet.getMax(), is(equalTo(2002.0)));
501501
assertThat(facet.getMin(), is(equalTo(2000.0)));
502502
}
503+
504+
@Test
505+
public void shouldReturnHistogramFacetForGivenQuery() {
506+
// given
507+
String facetName = "numberPublicationPerYear";
508+
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
509+
.withFacet(new HistogramFacetRequestBuilder(facetName).field(PUBLISHED_YEARS).interval(1).build()
510+
).build();
511+
// when
512+
FacetedPage<ArticleEntity> result = elasticsearchTemplate.queryForPage(searchQuery, ArticleEntity.class);
513+
// then
514+
assertThat(result.getNumberOfElements(), is(equalTo(4)));
515+
516+
HistogramResult facet = (HistogramResult) result.getFacet(facetName);
517+
assertThat(facet.getIntervalUnit().size(), is(equalTo(3)));
518+
519+
IntervalUnit unit = facet.getIntervalUnit().get(0);
520+
assertThat(unit.getKey(), is(Long.valueOf(YEAR_2000)));
521+
assertThat(unit.getCount(), is(3L));
522+
523+
unit = facet.getIntervalUnit().get(1);
524+
assertThat(unit.getKey(), is(Long.valueOf(YEAR_2001)));
525+
assertThat(unit.getCount(), is(2L));
526+
527+
unit = facet.getIntervalUnit().get(2);
528+
assertThat(unit.getKey(), is(Long.valueOf(YEAR_2002)));
529+
assertThat(unit.getCount(), is(1L));
530+
}
503531
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*
2+
* Copyright 2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.elasticsearch.core.facet;
17+
18+
import org.junit.Before;
19+
import org.junit.Test;
20+
import org.junit.runner.RunWith;
21+
import org.springframework.beans.factory.annotation.Autowired;
22+
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
23+
import org.springframework.data.elasticsearch.core.FacetedPage;
24+
import org.springframework.data.elasticsearch.core.facet.request.HistogramFacetRequestBuilder;
25+
import org.springframework.data.elasticsearch.core.facet.result.*;
26+
import org.springframework.data.elasticsearch.core.query.IndexQuery;
27+
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
28+
import org.springframework.data.elasticsearch.core.query.SearchQuery;
29+
import org.springframework.test.context.ContextConfiguration;
30+
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
31+
32+
import java.text.ParseException;
33+
import java.text.SimpleDateFormat;
34+
import java.util.concurrent.TimeUnit;
35+
36+
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
37+
import static org.hamcrest.Matchers.*;
38+
import static org.junit.Assert.assertThat;
39+
40+
/**
41+
* @author Rizwan Idrees
42+
* @author Mohsin Husen
43+
* @author Jonathan Yan
44+
* @author Artur Konczak
45+
*/
46+
@RunWith(SpringJUnit4ClassRunner.class)
47+
@ContextConfiguration("classpath:elasticsearch-template-test.xml")
48+
public class ElasticsearchTemplateHistogramFacetTests {
49+
50+
public static final long SEQUECE_CODE_INSERT = 1;
51+
public static final long SEQUECE_CODE_UPDATE = 2;
52+
public static final long SEQUECE_CODE_DELETE = 3;
53+
public static final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm");
54+
public static final String DATE_18 = "2013-10-18 18:01";
55+
public static final String DATE_17 = "2013-10-18 17:01";
56+
public static final String DATE_16 = "2013-10-18 16:01";
57+
58+
59+
@Autowired
60+
private ElasticsearchTemplate elasticsearchTemplate;
61+
62+
@Before
63+
public void before() throws ParseException {
64+
elasticsearchTemplate.deleteIndex(LogEntity.class);
65+
elasticsearchTemplate.createIndex(LogEntity.class);
66+
elasticsearchTemplate.putMapping(LogEntity.class);
67+
elasticsearchTemplate.refresh(LogEntity.class, true);
68+
69+
IndexQuery entry1 = new LogEntityBuilder("1").action("update").date(dateFormatter.parse(DATE_18)).code(SEQUECE_CODE_UPDATE).buildIndex();
70+
IndexQuery entry2 = new LogEntityBuilder("2").action("insert").date(dateFormatter.parse(DATE_17)).code(SEQUECE_CODE_INSERT).buildIndex();
71+
IndexQuery entry3 = new LogEntityBuilder("3").action("update").date(dateFormatter.parse(DATE_17)).code(SEQUECE_CODE_UPDATE).buildIndex();
72+
IndexQuery entry4 = new LogEntityBuilder("4").action("delete").date(dateFormatter.parse(DATE_16)).code(SEQUECE_CODE_DELETE).buildIndex();
73+
74+
75+
elasticsearchTemplate.index(entry1);
76+
elasticsearchTemplate.index(entry2);
77+
elasticsearchTemplate.index(entry3);
78+
elasticsearchTemplate.index(entry4);
79+
80+
elasticsearchTemplate.refresh(LogEntity.class, true);
81+
}
82+
83+
84+
@Test
85+
public void shouldReturnSimpleHistogramFacetForGivenQuery() {
86+
// given
87+
String facetName = "sequenceCodeFacet";
88+
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
89+
.withFacet(new HistogramFacetRequestBuilder(facetName).field("sequenceCode").interval(1).build()
90+
).build();
91+
// when
92+
FacetedPage<LogEntity> result = elasticsearchTemplate.queryForPage(searchQuery, LogEntity.class);
93+
// then
94+
assertThat(result.getNumberOfElements(), is(equalTo(4)));
95+
96+
HistogramResult facet = (HistogramResult) result.getFacet(facetName);
97+
assertThat(facet.getIntervalUnit().size(), is(equalTo(3)));
98+
99+
IntervalUnit unit = facet.getIntervalUnit().get(0);
100+
assertThat(unit.getKey(), is(SEQUECE_CODE_INSERT));
101+
assertThat(unit.getCount(), is(1L));
102+
103+
unit = facet.getIntervalUnit().get(1);
104+
assertThat(unit.getKey(), is(SEQUECE_CODE_UPDATE));
105+
assertThat(unit.getCount(), is(2L));
106+
107+
unit = facet.getIntervalUnit().get(2);
108+
assertThat(unit.getKey(), is(SEQUECE_CODE_DELETE));
109+
assertThat(unit.getCount(), is(1L));
110+
}
111+
112+
@Test
113+
public void shouldReturnDateHistogramFacetForGivenQuery() throws ParseException {
114+
// given
115+
String facetName = "sequenceCodeFacet";
116+
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
117+
.withFacet(new HistogramFacetRequestBuilder(facetName).field("date").interval(1).timeUnit(TimeUnit.HOURS).build()
118+
).build();
119+
// when
120+
FacetedPage<LogEntity> result = elasticsearchTemplate.queryForPage(searchQuery, LogEntity.class);
121+
// then
122+
assertThat(result.getNumberOfElements(), is(equalTo(4)));
123+
124+
HistogramResult facet = (HistogramResult) result.getFacet(facetName);
125+
assertThat(facet.getIntervalUnit().size(), is(equalTo(3)));
126+
127+
IntervalUnit unit = facet.getIntervalUnit().get(0);
128+
assertThat(unit.getKey(),is(dateFormatter.parse("2013-10-18 16:00").getTime()));
129+
assertThat(unit.getCount(), is(1L));
130+
131+
unit = facet.getIntervalUnit().get(1);
132+
assertThat(unit.getKey(),is(dateFormatter.parse("2013-10-18 17:00").getTime()));
133+
assertThat(unit.getCount(), is(2L));
134+
135+
unit = facet.getIntervalUnit().get(2);
136+
assertThat(unit.getKey(),is(dateFormatter.parse("2013-10-18 18:00").getTime()));
137+
assertThat(unit.getCount(), is(1L));
138+
}
139+
}

0 commit comments

Comments
 (0)