added annotation for multfield

This commit is contained in:
Artur Konczak 2013-05-21 08:23:25 +01:00
parent b2572d4950
commit 8862e01c74
12 changed files with 168 additions and 100 deletions

View File

@ -28,9 +28,9 @@ import java.lang.annotation.*;
@Documented @Documented
public @interface Field { public @interface Field {
String type() default ""; FieldType type() default FieldType.Auto;
String index() default ""; FieldIndex index() default FieldIndex.analyzed;
boolean store() default false; boolean store() default false;
@ -38,8 +38,4 @@ public @interface Field {
String indexAnalyzer() default ""; String indexAnalyzer() default "";
boolean facetable() default false;
boolean sortable() default false;
} }

View File

@ -0,0 +1,7 @@
package org.springframework.data.elasticsearch.annotations;
/**
*/
public enum FieldIndex {
not_analyzed, analyzed
}

View File

@ -0,0 +1,7 @@
package org.springframework.data.elasticsearch.annotations;
/**
*/
public enum FieldType {
String, Integer, Long, Date, Object, Auto
}

View File

@ -0,0 +1,19 @@
package org.springframework.data.elasticsearch.annotations;
import java.lang.annotation.*;
/**
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
* @author Jonathan Yan
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface MultiField {
public Field mainField();
public NestedField[] otherFields() default {};
}

View File

@ -0,0 +1,26 @@
package org.springframework.data.elasticsearch.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface NestedField {
String dotSuffix();
FieldType type();
FieldIndex index() default FieldIndex.analyzed;
boolean store() default false;
String searchAnalyzer() default "";
String indexAnalyzer() default "";
}

View File

@ -16,7 +16,7 @@
package org.springframework.data.elasticsearch.core; package org.springframework.data.elasticsearch.core;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.*;
import org.springframework.data.elasticsearch.core.query.FacetRequest; import org.springframework.data.elasticsearch.core.query.FacetRequest;
import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.ClassTypeInformation;
@ -71,15 +71,14 @@ class MappingBuilder {
if (isEntity(field)) { if (isEntity(field)) {
mapEntity(xContentBuilder, field.getType(), false, EMPTY, field.getName()); mapEntity(xContentBuilder, field.getType(), false, EMPTY, field.getName());
} }
Field fieldAnnotation = field.getAnnotation(Field.class); Field singleField = field.getAnnotation(Field.class);
if (isRootObject && fieldAnnotation != null && isIdField(field, idFieldName)) { MultiField multiField = field.getAnnotation(MultiField.class);
if (isRootObject && singleField != null && isIdField(field, idFieldName)) {
applyDefaultIdFieldMapping(xContentBuilder, field); applyDefaultIdFieldMapping(xContentBuilder, field);
} else if (fieldAnnotation != null) { } else if (multiField != null) {
if ((fieldAnnotation.sortable() || fieldAnnotation.facetable()) && TYPE_VALUE_STRING.equals(fieldAnnotation.type())) { addMultiFieldMapping(xContentBuilder, field, multiField);
addMultiFieldMapping(xContentBuilder, field, fieldAnnotation); } else if (singleField != null) {
} else { addSingleFieldMapping(xContentBuilder, field, singleField);
addSimpleFieldMapping(xContentBuilder, field, fieldAnnotation);
}
} }
} }
@ -105,15 +104,15 @@ class MappingBuilder {
* @param fieldAnnotation * @param fieldAnnotation
* @throws IOException * @throws IOException
*/ */
private static void addSimpleFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field, private static void addSingleFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field,
Field fieldAnnotation) throws IOException { Field fieldAnnotation) throws IOException {
xContentBuilder.startObject(field.getName()); xContentBuilder.startObject(field.getName());
xContentBuilder.field(FIELD_STORE, fieldAnnotation.store()); xContentBuilder.field(FIELD_STORE, fieldAnnotation.store());
if (isNotBlank(fieldAnnotation.type())) { if (FieldType.Auto != fieldAnnotation.type()) {
xContentBuilder.field(FIELD_TYPE, fieldAnnotation.type()); xContentBuilder.field(FIELD_TYPE, fieldAnnotation.type().name().toLowerCase());
} }
if (isNotBlank(fieldAnnotation.index())) { if (FieldIndex.not_analyzed == fieldAnnotation.index()) {
xContentBuilder.field(FIELD_INDEX, fieldAnnotation.index()); xContentBuilder.field(FIELD_INDEX, fieldAnnotation.index().name().toLowerCase());
} }
if (isNotBlank(fieldAnnotation.searchAnalyzer())) { if (isNotBlank(fieldAnnotation.searchAnalyzer())) {
xContentBuilder.field(FIELD_SEARCH_ANALYZER, fieldAnnotation.searchAnalyzer()); xContentBuilder.field(FIELD_SEARCH_ANALYZER, fieldAnnotation.searchAnalyzer());
@ -124,6 +123,33 @@ class MappingBuilder {
xContentBuilder.endObject(); xContentBuilder.endObject();
} }
/**
* Apply mapping for a single nested @Field annotation
*
* @param builder
* @param field
* @param annotation
* @throws IOException
*/
private static void addNestedFieldMapping(XContentBuilder builder, java.lang.reflect.Field field,
NestedField annotation) throws IOException {
builder.startObject(field.getName() + "." + annotation.dotSuffix());
builder.field(FIELD_STORE, annotation.store());
if (FieldType.Auto != annotation.type()) {
builder.field(FIELD_TYPE, annotation.type().name().toLowerCase());
}
if (FieldIndex.not_analyzed == annotation.index()) {
builder.field(FIELD_INDEX, annotation.index().name().toLowerCase());
}
if (isNotBlank(annotation.searchAnalyzer())) {
builder.field(FIELD_SEARCH_ANALYZER, annotation.searchAnalyzer());
}
if (isNotBlank(annotation.indexAnalyzer())) {
builder.field(FIELD_INDEX_ANALYZER, annotation.indexAnalyzer());
}
builder.endObject();
}
/** /**
* Multi field mappings for string type fields, support for sorts and facets * Multi field mappings for string type fields, support for sorts and facets
* *
@ -133,19 +159,14 @@ class MappingBuilder {
* @throws IOException * @throws IOException
*/ */
private static void addMultiFieldMapping(XContentBuilder builder, java.lang.reflect.Field field, private static void addMultiFieldMapping(XContentBuilder builder, java.lang.reflect.Field field,
Field annotation) throws IOException { MultiField annotation) throws IOException {
builder.startObject(field.getName()); builder.startObject(field.getName());
builder.field(FIELD_TYPE, "multi_field"); builder.field(FIELD_TYPE, "multi_field");
builder.startObject("fields"); builder.startObject("fields");
//add standard field //add standard field
addSimpleFieldMapping(builder, field, annotation); addSingleFieldMapping(builder, field, annotation.mainField());
//facet field - untouched, not analise, stored for (NestedField nestedField : annotation.otherFields()) {
if (annotation.facetable()) { addNestedFieldMapping(builder, field, nestedField);
addFacetMapping(builder, field, annotation);
}
//sort field - lowercase, not analise, stored
if (annotation.sortable()) {
addSortMapping(builder, field, annotation);
} }
builder.endObject(); builder.endObject();
builder.endObject(); builder.endObject();

View File

@ -2,6 +2,6 @@ package org.springframework.data.elasticsearch.core.facet;
public enum FacetType { public enum FacetType {
term, renage, histogram term, range, histogram
} }

View File

@ -19,8 +19,7 @@ import java.util.List;
*/ */
public class TermFacetRequest extends AbstractFacetRequest { public class TermFacetRequest extends AbstractFacetRequest {
private String[] stringFields; private String[] fields;
private String[] numberFields;
private int size = 10; private int size = 10;
private TermsFacet.ComparatorType order; private TermsFacet.ComparatorType order;
@ -28,12 +27,8 @@ public class TermFacetRequest extends AbstractFacetRequest {
super(name); super(name);
} }
public void setStringFields(String... fields) { public void setFields(String... fields) {
this.stringFields = fields; this.fields = fields;
}
public void setNumberFields(String... fields) {
this.numberFields = fields;
} }
public void setSize(int size) { public void setSize(int size) {
@ -57,24 +52,10 @@ public class TermFacetRequest extends AbstractFacetRequest {
order = TermsFacet.ComparatorType.COUNT; order = TermsFacet.ComparatorType.COUNT;
} }
private List<String> convertStringFieldsToFullNames() {
List<String> result = new ArrayList<String>();
if (ArrayUtils.isNotEmpty(stringFields)) {
for (String stringField : stringFields) {
result.add(stringField + "." + FIELD_UNTOUCHED);
}
}
if (ArrayUtils.isNotEmpty(numberFields)) {
Collections.addAll(result, numberFields);
}
return result;
}
@Override @Override
public FacetBuilder getFacet() { public FacetBuilder getFacet() {
List<String> fields = convertStringFieldsToFullNames();
Assert.notEmpty(fields, "Please select at last one field !!!"); Assert.notEmpty(fields, "Please select at last one field !!!");
TermsFacetBuilder builder = FacetBuilders.termsFacet(getName()).fields(fields.toArray(new String[fields.size()])).size(size); TermsFacetBuilder builder = FacetBuilders.termsFacet(getName()).fields(fields).size(size);
if (order != null) { if (order != null) {
builder.order(order); builder.order(order);
} }

View File

@ -13,13 +13,8 @@ public class TermFacetRequestBuilder {
result = new TermFacetRequest(name); result = new TermFacetRequest(name);
} }
public TermFacetRequestBuilder withStringFields(String... fields) { public TermFacetRequestBuilder withFields(String... fields) {
result.setStringFields(fields); result.setFields(fields);
return this;
}
public TermFacetRequestBuilder withNumberFields(String... fields) {
result.setNumberFields(fields);
return this; return this;
} }

View File

@ -1,12 +1,16 @@
package org.springframework.data.elasticsearch; package org.springframework.data.elasticsearch;
import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.annotations.*;
import org.springframework.data.elasticsearch.annotations.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static org.springframework.data.elasticsearch.annotations.FieldIndex.analyzed;
import static org.springframework.data.elasticsearch.annotations.FieldIndex.not_analyzed;
import static org.springframework.data.elasticsearch.annotations.FieldType.Integer;
import static org.springframework.data.elasticsearch.annotations.FieldType.String;
/** /**
* Simple type to test facets * Simple type to test facets
*/ */
@ -18,10 +22,16 @@ public class Article {
private String title; private String title;
@Field(type = "string", facetable = true) @MultiField(
mainField = @Field(type = String, index = analyzed),
otherFields = {
@NestedField(dotSuffix = "untouched", type = String, store = true, index = not_analyzed),
@NestedField(dotSuffix = "sort", type = String, store = true, indexAnalyzer = "keyword")
}
)
private List<String> authors = new ArrayList<String>(); private List<String> authors = new ArrayList<String>();
@Field(type = "integer", facetable = true) @Field(type = Integer, store = true)
private List<Integer> publishedYears = new ArrayList<Integer>(); private List<Integer> publishedYears = new ArrayList<Integer>();
public Article() { public Article() {

View File

@ -18,6 +18,11 @@ package org.springframework.data.elasticsearch;
import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldIndex;
import org.springframework.data.elasticsearch.annotations.FieldType;
import static org.springframework.data.elasticsearch.annotations.FieldIndex.*;
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
/** /**
* @author Rizwan Idrees * @author Rizwan Idrees
@ -26,40 +31,41 @@ import org.springframework.data.elasticsearch.annotations.Field;
@Document(indexName = "test-mapping", type = "mapping", indexStoreType = "memory", shards = 1, replicas = 0, refreshInterval = "-1") @Document(indexName = "test-mapping", type = "mapping", indexStoreType = "memory", shards = 1, replicas = 0, refreshInterval = "-1")
public class SampleMappingEntity { public class SampleMappingEntity {
@Id @Id
private String id; private String id;
@Field(type = "string", index = "not_analyzed", store = true, searchAnalyzer = "standard", indexAnalyzer = "standard")
private String message;
private NestedEntity nested; @Field(type = String, index = not_analyzed, store = true, searchAnalyzer = "standard", indexAnalyzer = "standard")
private String message;
public String getId() { private NestedEntity nested;
return id;
}
public void setId(String id) { public String getId() {
this.id = id; return id;
} }
public String getMessage() { public void setId(String id) {
return message; this.id = id;
} }
public void setMessage(String message) { public String getMessage() {
this.message = message; return message;
} }
static class NestedEntity { public void setMessage(String message) {
@Field(type = "string") this.message = message;
private String someField; }
public String getSomeField() { static class NestedEntity {
return someField; @Field(type = String)
} private String someField;
public void setSomeField(String someField) { public String getSomeField() {
this.someField = someField; return someField;
} }
}
public void setSomeField(String someField) {
this.someField = someField;
}
}
} }

View File

@ -79,7 +79,7 @@ public class ElasticsearchTemplateFacetTests {
// given // given
String facetName = "fauthors"; String facetName = "fauthors";
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withFacet(new TermFacetRequestBuilder(facetName).withStringFields("authors").build()).build(); SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withFacet(new TermFacetRequestBuilder(facetName).withFields("authors.untouched").build()).build();
// when // when
FacetedPage<Article> result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); FacetedPage<Article> result = elasticsearchTemplate.queryForPage(searchQuery, Article.class);
// then // then
@ -109,8 +109,8 @@ public class ElasticsearchTemplateFacetTests {
// given // given
String facetName = "fauthors"; String facetName = "fauthors";
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
.withFilter(FilterBuilders.notFilter(FilterBuilders.termFilter("title","four"))) .withFilter(FilterBuilders.notFilter(FilterBuilders.termFilter("title", "four")))
.withFacet(new TermFacetRequestBuilder(facetName).applyQueryFilter().withStringFields("authors").build()).build(); .withFacet(new TermFacetRequestBuilder(facetName).applyQueryFilter().withFields("authors.untouched").build()).build();
// when // when
FacetedPage<Article> result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); FacetedPage<Article> result = elasticsearchTemplate.queryForPage(searchQuery, Article.class);
// then // then
@ -136,7 +136,7 @@ public class ElasticsearchTemplateFacetTests {
// given // given
String facetName = "fauthors"; String facetName = "fauthors";
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
.withFacet(new TermFacetRequestBuilder(facetName).withStringFields("authors").ascTerm().build()).build(); .withFacet(new TermFacetRequestBuilder(facetName).withFields("authors.untouched").ascTerm().build()).build();
// when // when
FacetedPage<Article> result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); FacetedPage<Article> result = elasticsearchTemplate.queryForPage(searchQuery, Article.class);
// then // then
@ -167,7 +167,7 @@ public class ElasticsearchTemplateFacetTests {
// given // given
String facetName = "fauthors"; String facetName = "fauthors";
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
.withFacet(new TermFacetRequestBuilder(facetName).withStringFields("authors").ascCount().build()).build(); .withFacet(new TermFacetRequestBuilder(facetName).withFields("authors.untouched").ascCount().build()).build();
// when // when
FacetedPage<Article> result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); FacetedPage<Article> result = elasticsearchTemplate.queryForPage(searchQuery, Article.class);
// then // then
@ -197,7 +197,7 @@ public class ElasticsearchTemplateFacetTests {
// given // given
String facetName = "fyears"; String facetName = "fyears";
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
.withFacet(new TermFacetRequestBuilder(facetName).withNumberFields("publishedYears").descCount().build()).build(); .withFacet(new TermFacetRequestBuilder(facetName).withFields("publishedYears").descCount().build()).build();
// when // when
FacetedPage<Article> result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); FacetedPage<Article> result = elasticsearchTemplate.queryForPage(searchQuery, Article.class);
// then // then
@ -227,7 +227,7 @@ public class ElasticsearchTemplateFacetTests {
// given // given
String facetName = "fyears"; String facetName = "fyears";
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
.withFacet(new TermFacetRequestBuilder(facetName).withNumberFields("publishedYears").withStringFields("authors").ascTerm().build()).build(); .withFacet(new TermFacetRequestBuilder(facetName).withFields("publishedYears", "authors.untouched").ascTerm().build()).build();
// when // when
FacetedPage<Article> result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); FacetedPage<Article> result = elasticsearchTemplate.queryForPage(searchQuery, Article.class);
// then // then
@ -274,8 +274,8 @@ public class ElasticsearchTemplateFacetTests {
String numberFacetName = "fAuthors"; String numberFacetName = "fAuthors";
String stringFacetName = "fyears"; String stringFacetName = "fyears";
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
.withFacet(new TermFacetRequestBuilder(numberFacetName).withNumberFields("publishedYears").ascTerm().build()) .withFacet(new TermFacetRequestBuilder(numberFacetName).withFields("publishedYears").ascTerm().build())
.withFacet(new TermFacetRequestBuilder(stringFacetName).withStringFields("authors").ascTerm().build()) .withFacet(new TermFacetRequestBuilder(stringFacetName).withFields("authors.untouched").ascTerm().build())
.build(); .build();
// when // when
FacetedPage<Article> result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); FacetedPage<Article> result = elasticsearchTemplate.queryForPage(searchQuery, Article.class);