diff --git a/pom.xml b/pom.xml
index 06c7d4508..b96e01410 100644
--- a/pom.xml
+++ b/pom.xml
@@ -54,6 +54,27 @@
${springdata.commons}
+
+ com.querydsl.contrib
+ querydsl-elasticsearch
+ ${querydsl}
+ true
+
+
+
+ com.querydsl
+ querydsl-apt
+ ${querydsl}
+ provided
+
+
+
+ javax.annotation
+ jsr250-api
+ 1.0
+ true
+
+
commons-lang
@@ -139,6 +160,30 @@
+
+ com.mysema.maven
+ apt-maven-plugin
+ ${apt}
+
+
+ com.querydsl
+ querydsl-apt
+ ${querydsl}
+
+
+
+
+ generate-test-sources
+
+ test-process
+
+
+ target/generated-test-sources
+ org.springframework.data.elasticsearch.repository.support.ElasticsearchAnnotationProcessor
+
+
+
+
org.apache.maven.plugins
maven-assembly-plugin
diff --git a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchOperations.java b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchOperations.java
index 53ac150d2..6f950ef44 100755
--- a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchOperations.java
+++ b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchOperations.java
@@ -16,6 +16,7 @@
package org.springframework.data.elasticsearch.core;
import org.elasticsearch.action.update.UpdateResponse;
+import org.elasticsearch.client.Client;
import org.springframework.data.domain.Page;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
@@ -41,6 +42,16 @@ public interface ElasticsearchOperations {
*/
ElasticsearchConverter getElasticsearchConverter();
+ /**
+ * @return Client in use
+ */
+ Client getClient();
+
+ /**
+ * @return ResultsMapper in use
+ */
+ ResultsMapper getResultsMapper();
+
/**
* Create an index for a class
*
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 c431c20dd..396de8b92 100755
--- a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java
+++ b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java
@@ -52,6 +52,7 @@
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
+import org.elasticsearch.action.suggest.SuggestRequest;
import org.elasticsearch.action.suggest.SuggestRequestBuilder;
import org.elasticsearch.action.suggest.SuggestResponse;
import org.elasticsearch.action.update.UpdateRequestBuilder;
@@ -64,6 +65,7 @@
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.common.collect.Maps;
import org.elasticsearch.common.unit.TimeValue;
+import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.query.FilterBuilder;
import org.elasticsearch.index.query.QueryBuilder;
@@ -232,6 +234,16 @@ public ElasticsearchConverter getElasticsearchConverter() {
return elasticsearchConverter;
}
+ @Override
+ public Client getClient() {
+ return client;
+ }
+
+ @Override
+ public ResultsMapper getResultsMapper() {
+ return resultsMapper;
+ }
+
@Override
public T queryForObject(GetQuery query, Class clazz) {
return queryForObject(query, clazz, resultsMapper);
@@ -1151,10 +1163,6 @@ private static String[] toArray(List values) {
return values.toArray(valuesAsArray);
}
- protected ResultsMapper getResultsMapper() {
- return resultsMapper;
- }
-
private boolean isDocument(Class clazz) {
return clazz.isAnnotationPresent(Document.class);
}
diff --git a/src/main/java/org/springframework/data/elasticsearch/repository/support/ElasticsearchAnnotationProcessor.java b/src/main/java/org/springframework/data/elasticsearch/repository/support/ElasticsearchAnnotationProcessor.java
new file mode 100644
index 000000000..755570ba9
--- /dev/null
+++ b/src/main/java/org/springframework/data/elasticsearch/repository/support/ElasticsearchAnnotationProcessor.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed 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.springframework.data.elasticsearch.repository.support;
+
+import java.util.Collections;
+
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.annotation.processing.SupportedSourceVersion;
+import javax.lang.model.SourceVersion;
+import javax.tools.Diagnostic;
+
+import com.querydsl.core.annotations.QueryEmbeddable;
+import com.querydsl.core.annotations.QueryEmbedded;
+import com.querydsl.core.annotations.QueryEntities;
+import com.querydsl.core.annotations.QuerySupertype;
+import com.querydsl.core.annotations.QueryTransient;
+import com.querydsl.apt.AbstractQuerydslProcessor;
+import com.querydsl.apt.Configuration;
+import com.querydsl.apt.DefaultConfiguration;
+
+import org.springframework.data.elasticsearch.annotations.Document;
+
+/**
+ * Annotation processor to create Querydsl query types for QueryDsl annotated classes.
+ *
+ * @author Kevin Leturc
+ */
+@SupportedAnnotationTypes({ "com.querydsl.core.annotations.*", "org.springframework.data.elasticsearch.annotations.*" })
+@SupportedSourceVersion(SourceVersion.RELEASE_6)
+public class ElasticsearchAnnotationProcessor extends AbstractQuerydslProcessor {
+
+ /*
+ * (non-Javadoc)
+ * @see com.querydsl.core.apt.AbstractQuerydslProcessor#createConfiguration(javax.annotation.processing.RoundEnvironment)
+ */
+ @Override
+ protected Configuration createConfiguration(RoundEnvironment roundEnv) {
+
+ processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Running " + getClass().getSimpleName());
+
+ DefaultConfiguration configuration = new DefaultConfiguration(roundEnv, processingEnv.getOptions(),
+ Collections. emptySet(), QueryEntities.class, Document.class, QuerySupertype.class,
+ QueryEmbeddable.class, QueryEmbedded.class, QueryTransient.class);
+ configuration.setUnknownAsEmbedded(true);
+
+ return configuration;
+ }
+
+}
diff --git a/src/main/java/org/springframework/data/elasticsearch/repository/support/ElasticsearchRepositoryFactory.java b/src/main/java/org/springframework/data/elasticsearch/repository/support/ElasticsearchRepositoryFactory.java
index 9e1263022..545384897 100644
--- a/src/main/java/org/springframework/data/elasticsearch/repository/support/ElasticsearchRepositoryFactory.java
+++ b/src/main/java/org/springframework/data/elasticsearch/repository/support/ElasticsearchRepositoryFactory.java
@@ -41,6 +41,7 @@
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Ryan Henszey
+ * @author Kevin Leturc
*/
public class ElasticsearchRepositoryFactory extends RepositoryFactorySupport {
@@ -68,9 +69,8 @@ protected Object getTargetRepository(RepositoryInformation metadata) {
@Override
protected Class> getRepositoryBaseClass(RepositoryMetadata metadata) {
if (isQueryDslRepository(metadata.getRepositoryInterface())) {
- throw new IllegalArgumentException("QueryDsl Support has not been implemented yet.");
- }
- if (Integer.class.isAssignableFrom(metadata.getIdType())
+ return QueryDslElasticsearchRepository.class;
+ } else if (Integer.class.isAssignableFrom(metadata.getIdType())
|| Long.class.isAssignableFrom(metadata.getIdType())
|| Double.class.isAssignableFrom(metadata.getIdType())) {
return NumberKeyedRepository.class;
diff --git a/src/main/java/org/springframework/data/elasticsearch/repository/support/QueryDslElasticsearchRepository.java b/src/main/java/org/springframework/data/elasticsearch/repository/support/QueryDslElasticsearchRepository.java
new file mode 100644
index 000000000..c6b62ff4f
--- /dev/null
+++ b/src/main/java/org/springframework/data/elasticsearch/repository/support/QueryDslElasticsearchRepository.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed 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.springframework.data.elasticsearch.repository.support;
+
+import java.io.Serializable;
+
+import com.querydsl.elasticsearch.ElasticsearchQuery;
+import com.querydsl.core.types.EntityPath;
+import com.querydsl.core.types.Expression;
+import com.querydsl.core.types.OrderSpecifier;
+import com.querydsl.core.types.Predicate;
+import com.querydsl.core.types.dsl.PathBuilder;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
+import org.springframework.data.querydsl.EntityPathResolver;
+import org.springframework.data.querydsl.QueryDslPredicateExecutor;
+import org.springframework.data.querydsl.SimpleEntityPathResolver;
+import org.springframework.util.Assert;
+
+/**
+ * Special QueryDsl based repository implementation that allows execution {@link Predicate}s in various forms.
+ *
+ * @author Kevin Leturc
+ */
+public class QueryDslElasticsearchRepository extends AbstractElasticsearchRepository
+ implements QueryDslPredicateExecutor {
+
+ private final PathBuilder builder;
+
+ /**
+ * Creates a new {@link QueryDslElasticsearchRepository} for the given {@link ElasticsearchEntityInformation} and {@link ElasticsearchOperations}. Uses
+ * the {@link org.springframework.data.querydsl.SimpleEntityPathResolver} to create an {@link com.querydsl.core.types.EntityPath} for the given domain class.
+ *
+ * @param entityInformation The elasticsearch entity information.
+ * @param elasticsearchOperations The elasticsearch operations.
+ */
+ public QueryDslElasticsearchRepository(ElasticsearchEntityInformation entityInformation, ElasticsearchOperations elasticsearchOperations) {
+ this(entityInformation, elasticsearchOperations, SimpleEntityPathResolver.INSTANCE);
+ }
+
+ /**
+ * Creates a new {@link QueryDslElasticsearchRepository} for the given {@link ElasticsearchEntityInformation}, {@link ElasticsearchOperations}
+ * and {@link org.springframework.data.querydsl.EntityPathResolver}.
+ *
+ * @param entityInformation The elasticsearch entity information.
+ * @param elasticsearchOperations The elasticsearch operations.
+ * @param resolver The query dsl path resolver.
+ */
+ public QueryDslElasticsearchRepository(ElasticsearchEntityInformation entityInformation, ElasticsearchOperations elasticsearchOperations,
+ EntityPathResolver resolver) {
+
+ super(entityInformation, elasticsearchOperations);
+ Assert.notNull(resolver);
+ EntityPath path = resolver.createPath(entityInformation.getJavaType());
+ this.builder = new PathBuilder(path.getType(), path.getMetadata());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findOne(com.querydsl.core.types.Predicate)
+ */
+ @Override
+ public T findOne(Predicate predicate) {
+ return createQueryFor(predicate).fetchFirst();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.querydsl.core.types.Predicate)
+ */
+ @Override
+ public Iterable findAll(Predicate predicate) {
+ return createQueryFor(predicate).fetch();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.querydsl.core.types.Predicate, org.springframework.data.domain.Sort)
+ */
+ @Override
+ public Iterable findAll(Predicate predicate, Sort sort) {
+ return applySorting(createQueryFor(predicate), sort).fetch();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.querydsl.core.types.Predicate, com.querydsl.core.types.OrderSpecifier>[])
+ */
+ @Override
+ public Iterable findAll(Predicate predicate, OrderSpecifier>... orders) {
+ return createQueryFor(predicate).orderBy(orders).fetch();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.querydsl.core.types.OrderSpecifier>[])
+ */
+ @Override
+ public Iterable findAll(OrderSpecifier>... orders) {
+ return createQuery().orderBy(orders).fetch();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.querydsl.core.types.Predicate, org.springframework.data.domain.Pageable)
+ */
+ @Override
+ public Page findAll(Predicate predicate, Pageable pageable) {
+ ElasticsearchQuery countQuery = createQueryFor(predicate);
+ ElasticsearchQuery query = createQueryFor(predicate);
+
+ return new PageImpl(applyPagination(query, pageable).fetch(), pageable, countQuery.fetchCount());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#count(com.querydsl.core.types.Predicate)
+ */
+ @Override
+ public long count(Predicate predicate) {
+ return createQueryFor(predicate).fetchCount();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#exists(com.querydsl.core.types.Predicate)
+ */
+ @Override
+ public boolean exists(Predicate predicate) {
+ return createQueryFor(predicate).fetchCount() > 0;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#stringIdRepresentation(ID)
+ */
+ @Override
+ protected String stringIdRepresentation(ID id) {
+ // Handle String and Number ids
+ return String.valueOf(id);
+ }
+
+ /**
+ * Creates an {@link ElasticsearchQuery} for the given {@link Predicate}.
+ *
+ * @param predicate The predicate.
+ * @return The querydsl query.
+ */
+ private ElasticsearchQuery createQueryFor(Predicate predicate) {
+ return createQuery().where(predicate);
+ }
+
+ /**
+ * Creates an {@link ElasticsearchQuery}.
+ *ate.
+ * @return The querydsl query.
+ */
+ private ElasticsearchQuery createQuery() {
+ return new SpringDataElasticsearchQuery(elasticsearchOperations, entityInformation);
+ }
+
+ /**
+ * Applies the given {@link Pageable} to the given {@link ElasticsearchQuery}.
+ *
+ * @param query The query to apply pagination.
+ * @param pageable The pageable to apply.
+ * @return The querydsl query.
+ */
+ private ElasticsearchQuery applyPagination(ElasticsearchQuery query, Pageable pageable) {
+
+ if (pageable == null) {
+ return query;
+ }
+
+ query = query.offset(pageable.getOffset()).limit(pageable.getPageSize());
+ return applySorting(query, pageable.getSort());
+ }
+
+ /**
+ * Applies the given {@link Sort} to the given {@link ElasticsearchQuery}.
+ *
+ * @param query The query to apply sort.
+ * @param sort The sort to appy.
+ * @return The querydsl query.
+ */
+ private ElasticsearchQuery applySorting(ElasticsearchQuery query, Sort sort) {
+
+ if (sort == null) {
+ return query;
+ }
+
+ for (Sort.Order order : sort) {
+ query.orderBy(toOrder(order));
+ }
+
+ return query;
+ }
+
+ /**
+ * Transforms a plain {@link Sort.Order} into a QueryDsl specific {@link OrderSpecifier}.
+ *
+ * @param order The order to convert.
+ * @return The querydsl order.
+ */
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ private OrderSpecifier> toOrder(Sort.Order order) {
+
+ Expression