1
0
mirror of synced 2026-09-11 02:19:47 +00:00

Compare commits

...

72 Commits

Author SHA1 Message Date
Mark Paluch e7e0983ea5 DATAES-486 - Release version 3.1.1 (Lovelace SR1). 2018-10-15 10:42:05 +02:00
Mark Paluch a299260a03 DATAES-486 - Prepare 3.1.1 (Lovelace SR1). 2018-10-15 10:40:57 +02:00
Mark Paluch 627d96cbfc DATAES-486 - Updated changelog. 2018-10-15 10:40:56 +02:00
Mark Paluch efa3b4d17e DATAES-480 - After release cleanups. 2018-09-21 07:46:18 -04:00
Mark Paluch f5d44ad755 DATAES-480 - Prepare next development iteration. 2018-09-21 07:46:17 -04:00
Mark Paluch 5a50114b73 DATAES-480 - Release version 3.1 GA (Lovelace). 2018-09-21 07:08:39 -04:00
Mark Paluch 45a9384d4f DATAES-480 - Prepare 3.1 GA (Lovelace). 2018-09-21 07:07:51 -04:00
Mark Paluch 789acdc3dc DATAES-480 - Updated changelog. 2018-09-21 07:07:49 -04:00
Mark Paluch 946a60d24d DATAES-473 - Updated changelog. 2018-09-10 14:15:51 +02:00
Mark Paluch 838776dd63 DATAES-474 - Updated changelog. 2018-09-10 10:20:58 +02:00
jnizet b8324f9205 DATAES-479 - Allow specifying a HighlightBuilder when creating a query.
Original pull request: #217.
2018-08-24 15:59:12 +02:00
Oliver Gierke 94d18e8111 DATAES-472 - After release cleanups. 2018-08-20 10:56:53 +02:00
Oliver Gierke a1aa755253 DATAES-472 - Prepare next development iteration. 2018-08-20 10:56:52 +02:00
Oliver Gierke dfd9d962d3 DATAES-472 - Release version 3.1 RC2 (Lovelace). 2018-08-20 10:40:11 +02:00
Oliver Gierke e1d3f6f160 DATAES-472 - Prepare 3.1 RC2 (Lovelace). 2018-08-20 10:39:43 +02:00
Oliver Gierke 7bb108c998 DATAES-472 - Updated changelog. 2018-08-20 10:39:41 +02:00
Mark Paluch d1b71f2e73 DATAES-463 - Updated changelog. 2018-07-27 11:45:25 +02:00
Mark Paluch 88fe60c7d4 DATAES-465 - Updated changelog. 2018-07-26 16:23:57 +02:00
Mark Paluch 9a0a931ce2 DATAES-452 - After release cleanups. 2018-07-26 12:32:29 +02:00
Mark Paluch 02f2171cb6 DATAES-452 - Prepare next development iteration. 2018-07-26 12:32:25 +02:00
Mark Paluch 5d4cabae10 DATAES-452 - Release version 3.1 RC1 (Lovelace). 2018-07-26 12:06:35 +02:00
Mark Paluch 2632b69d12 DATAES-452 - Prepare 3.1 RC1 (Lovelace). 2018-07-26 12:04:30 +02:00
Mark Paluch d30328f2e1 DATAES-452 - Updated changelog. 2018-07-26 12:04:28 +02:00
Oliver Gierke 469455383f DATAES-470 - Fixed parsing of cluster nodes in TransportClientFactoryBean.
Extracted ClusterNodes value object to capture the parsing logic and actually properly test it. Added unit tests to verify the proper rejection and the two cases outlined in the ticket.

Related tickets: DATAES-283.
2018-07-12 21:48:31 +02:00
Oliver Gierke cdbc832068 DATAES-471 - Adapt object property access behavior after changes in Spring Data Commons.
Changes made for Spring Data Commons result in final fields being rejected for manipulation unless there's a wither method available on the object at hand. Unfortunately adapting Spring Data Elasticsearch to support that doesn't work easily as it requires breaking changes to ElasticsearchTemplate as most of the methods assume parameters being handed to be mutable, see the implementation of SimpleElasticsearchRepository.save(…) for instance.

We now mitigate the problem, by enforcing the BeanWrapperPropertyAccessor being used and treating all properties as mutable.

Related tickets: DATACMNS-1322.
2018-07-12 15:27:36 +02:00
xhaggi 143a359eca DATAES-317 - Introduce query logging in ElasticsearchTemplate.
Original pull request: #180.
2018-06-28 11:21:43 +02:00
xhaggi e58c43f391 DATAES-469 - Remove superfluous dependency elasticsearch. 2018-06-28 11:16:33 +02:00
petar.tahchiev d66b2a6f63 DATAES-283 - Get rid of Commons Lang dependency.
Replace all StringUtils and ArrayUtils usages with Springframework's StringUtils and ObjectUtils. Left the commons-lang as test-scope dependency as I believe it brings some values in the tests.

Original pull request: #211.
2018-06-20 20:10:21 +02:00
xhaggi ba3eba5734 DATAES-467 - Fix sorting by _score if Spring Data sort is used.
Original pull request: #209.
2018-06-18 11:18:31 +02:00
Mark Paluch 4a26af38c3 DATAES-448 - Updated changelog. 2018-06-13 21:39:54 +02:00
Oliver Gierke 5ddb46c435 DATAES-462 - Polishing.
SimpleElasticsearchPersistentProperty now already checks for the correct type of score properties. Added unit tests for that. Also added unit tests for SimpleElasticsearchPersistentEntity rejecting more than one score property being present.

Additional non-null assertions on components that are required so that we can remove superfluous null checks.

A bit o formatting, Javadoc, missing @since tags and license headers.

Original pull request: #207.
2018-06-13 19:00:36 +02:00
xhaggi d996406113 DATAES-462 - Add support for mapping max score and document scores.
Original pull request: #207.
2018-06-13 18:58:13 +02:00
xhaggi 112600261d DATAES-462 - Add query builder option to track scores.
Original pull request: #207.
2018-06-13 18:57:54 +02:00
Oliver Gierke 62a03a8fb7 DATAES-464 - DefaultEntityWriter now considers read-only and transient properties.
We now register a custom Jackson module that filters read-only and transient properties for serialization.
2018-06-13 18:21:37 +02:00
Mark Paluch ac62aaf856 DATAES-447 - Updated changelog. 2018-06-13 15:01:59 +02:00
Christoph Strobl 28629a6ede DATAES-440 - After release cleanups. 2018-05-17 10:09:36 +02:00
Christoph Strobl 12cc5ced16 DATAES-440 - Prepare next development iteration. 2018-05-17 10:09:34 +02:00
Christoph Strobl a807a3801c DATAES-440 - Release version 3.1 M3 (Lovelace). 2018-05-17 09:51:42 +02:00
Christoph Strobl ac2e5be02b DATAES-440 - Prepare 3.1 M3 (Lovelace). 2018-05-17 09:50:48 +02:00
Christoph Strobl b5b8066e6f DATAES-440 - Updated changelog. 2018-05-17 09:50:46 +02:00
Ted Liang e8e407f93b DATAES-312 - Fix NullHandling.NULLS_LAST in query.sort
Original pull request: #163
2018-05-17 09:31:59 +02:00
Nordine Bittich 1b0006f9f7 DATAES-420 - Analyzer of main field ignored when using @MultiField annotation 2018-05-15 17:16:29 +02:00
Oliver Gierke 36c52a5308 DATAES-451 - Adapt to SpEL extension API changes in Spring Data Commons.
Related tickets: DATACMNS-1260.
2018-05-14 17:23:29 +02:00
Mark Paluch 324ab13ca1 DATAES-437 - Updated changelog. 2018-05-08 15:27:21 +02:00
Mark Paluch c87499b189 DATAES-436 - Updated changelog. 2018-05-08 12:22:52 +02:00
xhaggi 64f259cb8e DATAES-427 - update version overview in README 2018-05-08 11:37:00 +02:00
xhaggi 8c4a669ce5 DATAES-285 - fix broken tests after 4970783ca4 2018-05-08 10:47:22 +02:00
xhaggi ace5a21b05 DATAES-285 - Polishing
* drop superfluous class FieldIndex
* change FieldType text and keyword starting with capital letters

Original commit: 089d7746be
2018-05-07 13:19:37 +02:00
xhaggi 9c235cd53c DATAES-412 - only the last highlight field is added to SearchRequest 2018-05-07 11:39:58 +02:00
Chris White ef1dca31e4 DATAES-198 - Fixed @Version annotation on fields. 2018-04-23 17:33:54 +02:00
Alen Turkovic de1afe8bb0 DATAES-422 - Add support for IndicesOptions in search queries.
See also: DATAES-191..
2018-04-20 14:32:01 +02:00
Oliver Gierke 92c3bbebed DATAES-363 - Polishing.
Original pull request: #183.
2018-04-18 12:36:27 +02:00
Michael Wirth 512e9eacad DATAES-363 - Fixed CrudRepository.existsById(…) implementation.
Properly use Optional.isPresent() over a null check.

Original pull request: #183.
2018-04-18 12:36:10 +02:00
Mark Paluch b8400d8d4d DATAES-427 - After release cleanups. 2018-04-13 15:08:34 +02:00
Mark Paluch c53ecdc41c DATAES-427 - Prepare next development iteration. 2018-04-13 15:08:32 +02:00
Mark Paluch c2aca20505 DATAES-427 - Release version 3.1 M2 (Lovelace). 2018-04-13 14:30:58 +02:00
Mark Paluch e703fbc8bf DATAES-427 - Prepare 3.1 M2 (Lovelace). 2018-04-13 14:30:01 +02:00
Mark Paluch 1b44f051c7 DATAES-427 - Updated changelog. 2018-04-13 14:29:59 +02:00
xhaggi b949daadea DATAES-438 - get rid of deprecation warning because of invalid mapping for @Id 2018-04-13 11:55:17 +01:00
Oliver Gierke 8c40bac9ab DATAES-439 - Adapt to API changes in Spring Data Commons.
Related tickets: DATACMNS-1275.
2018-04-09 14:40:47 +02:00
Mark Paluch 9fba9ea076 DATAES-430 - Updated changelog. 2018-04-04 17:12:55 +02:00
Mark Paluch b5d5fdc32e DATAES-423 - Updated changelog. 2018-04-04 15:16:22 +02:00
Oliver Gierke 6db756a233 DATAES-434 - Removed explicit declaration of Jackson library versions. 2018-03-27 19:38:13 +02:00
Artur Konczak e50e5f9146 DATAES-402 - fixed broken tests 2018-03-15 13:43:06 +00:00
Remco Zigterman 4856974c1a DATAES-402 - fixing paging information 2018-03-15 09:57:19 +00:00
Artur Konczak 51f9485700 DATAES-421 updated to latest version of ES 6.2.2 2018-03-15 09:14:43 +00:00
rivergod e7b93bee90 DATAES-421 - Update ES to 6.1.0 2018-03-15 09:14:43 +00:00
Mark Paluch 9bc9c47f42 DATAES-432 - Export composable repositories via CDI.
We now export composable repositories through our CDI extension. Repositories can now be customized either by a single custom implementation (as it was before) and by providing fragment interfaces along their fragment implementation.

This change aligns CDI support with the existing RepositoryFactory support we provide within a Spring application context.
2018-03-12 17:00:16 +01:00
Mark Paluch 4313d13ddd DATAES-429 - Updated changelog. 2018-02-28 11:17:39 +01:00
Mark Paluch 2075775478 DATAES-425 - Updated changelog. 2018-02-19 20:30:08 +01:00
Christoph Strobl e4009df6b9 DATAES-401 - After release cleanups. 2018-02-06 10:11:17 +01:00
Christoph Strobl 053e1726f5 DATAES-401 - Prepare next development iteration. 2018-02-06 10:11:15 +01:00
73 changed files with 2045 additions and 655 deletions
+7 -11
View File
@@ -52,17 +52,13 @@ the appropriate dependency version.
</repository>
```
| spring data elasticsearch | elasticsearch |
|:-----------------------------------:|:-------------:|
| 3.0.0.RC2 | 5.5.0 |
| 3.0.0.M4 | 5.4.0 |
| 2.0.4.RELEASE | 2.4.0 |
| 2.0.0.RELEASE | 2.2.0 |
| 1.4.0.M1 | 1.7.3 |
| 1.3.0.RELEASE | 1.5.2 |
| 1.2.0.RELEASE | 1.4.4 |
| 1.1.0.RELEASE | 1.3.2 |
| 1.0.0.RELEASE | 1.1.1 |
| spring data elasticsearch | elasticsearch |
|:-------------------------:|:-------------:|
| 3.1.x | 6.2.2 |
| 3.0.x | 5.5.0 |
| 2.1.x | 2.4.0 |
| 2.0.x | 2.2.0 |
| 1.3.x | 1.5.2 |
### ElasticsearchRepository
+8 -15
View File
@@ -4,12 +4,12 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>3.1.0.M1</version>
<version>3.1.1.RELEASE</version>
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>2.1.0.M1</version>
<version>2.1.1.RELEASE</version>
</parent>
<name>Spring Data Elasticsearch</name>
@@ -19,9 +19,9 @@
<properties>
<commonscollections>3.2.1</commonscollections>
<commonslang>2.6</commonslang>
<elasticsearch>5.5.0</elasticsearch>
<log4j>2.8.2</log4j>
<springdata.commons>2.1.0.M1</springdata.commons>
<elasticsearch>6.2.2</elasticsearch>
<log4j>2.9.1</log4j>
<springdata.commons>2.1.1.RELEASE</springdata.commons>
<java-module-name>spring.data.elasticsearch</java-module-name>
</properties>
@@ -56,6 +56,7 @@
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>${commonslang}</version>
<scope>test</scope>
</dependency>
<!-- JODA Time -->
@@ -66,12 +67,6 @@
</dependency>
<!-- Elasticsearch -->
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>${elasticsearch}</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
@@ -102,12 +97,10 @@
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson}</version>
</dependency>
<!-- CDI -->
@@ -223,8 +216,8 @@
<repositories>
<repository>
<id>spring-libs-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
<id>spring-libs-release</id>
<url>https://repo.spring.io/libs-release</url>
</repository>
</repositories>
@@ -21,5 +21,17 @@ package org.springframework.data.elasticsearch.annotations;
* @author Artur Konczak
*/
public enum FieldType {
text, Integer, Long, Date, Float, Double, Boolean, Object, Auto, Nested, Ip, Attachment, keyword
Text,
Integer,
Long,
Date,
Float,
Double,
Boolean,
Object,
Auto,
Nested,
Ip,
Attachment,
Keyword
}
@@ -33,11 +33,15 @@ public @interface InnerField {
boolean index() default true;
DateFormat format() default DateFormat.none;
String pattern() default "";
boolean store() default false;
boolean fielddata() default false;
String searchAnalyzer() default "";
String indexAnalyzer() default "";
String analyzer() default "";
}
@@ -0,0 +1,23 @@
package org.springframework.data.elasticsearch.annotations;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.data.annotation.ReadOnlyProperty;
/**
* Specifies that this field is used for storing the document score.
*
* @author Sascha Woo
* @since 3.1
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
@Inherited
@ReadOnlyProperty
public @interface Score {}
@@ -0,0 +1,102 @@
/*
* Copyright 2018 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.client;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import org.elasticsearch.common.transport.TransportAddress;
import org.springframework.data.util.Streamable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Value object to represent a list of cluster nodes.
*
* @author Oliver Gierke
* @since 3.1
*/
class ClusterNodes implements Streamable<TransportAddress> {
public static ClusterNodes DEFAULT = ClusterNodes.of("127.0.0.1:9300");
private static final String COLON = ":";
private static final String COMMA = ",";
private final List<TransportAddress> clusterNodes;
/**
* Creates a new {@link ClusterNodes} by parsing the given source.
*
* @param source must not be {@literal null} or empty.
*/
private ClusterNodes(String source) {
Assert.hasText(source, "Cluster nodes source must not be null or empty!");
String[] nodes = StringUtils.delimitedListToStringArray(source, COMMA);
this.clusterNodes = Arrays.stream(nodes).map(node -> {
String[] segments = StringUtils.delimitedListToStringArray(node, COLON);
Assert.isTrue(segments.length == 2,
() -> String.format("Invalid cluster node %s in %s! Must be in the format host:port!", node, source));
String host = segments[0].trim();
String port = segments[1].trim();
Assert.hasText(host, () -> String.format("No host name given cluster node %s!", node));
Assert.hasText(port, () -> String.format("No port given in cluster node %s!", node));
return new TransportAddress(toInetAddress(host), Integer.valueOf(port));
}).collect(Collectors.toList());
}
/**
* Creates a new {@link ClusterNodes} by parsing the given source. The expected format is a comma separated list of
* host-port-combinations separated by a colon: {@code host:port,host:port,…}.
*
* @param source must not be {@literal null} or empty.
* @return
*/
public static ClusterNodes of(String source) {
return new ClusterNodes(source);
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
@Override
public Iterator<TransportAddress> iterator() {
return clusterNodes.iterator();
}
private static InetAddress toInetAddress(String host) {
try {
return InetAddress.getByName(host);
} catch (UnknownHostException o_O) {
throw new IllegalArgumentException(o_O);
}
}
}
@@ -18,7 +18,6 @@ package org.springframework.data.elasticsearch.client;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.settings.Settings;
@@ -31,6 +30,8 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.StringUtils;
import static java.util.Arrays.*;
/**
@@ -38,6 +39,7 @@ import static java.util.Arrays.*;
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Ilkang Na
*/
public class NodeClientFactoryBean implements FactoryBean<Client>, InitializingBean, DisposableBean {
@@ -85,21 +87,19 @@ public class NodeClientFactoryBean implements FactoryBean<Client>, InitializingB
nodeClient = (NodeClient) new TestNode(
Settings.builder().put(loadConfig())
.put("transport.type", "netty4")
.put("transport.type", "local")
.put("http.type", "netty4")
.put("path.home", this.pathHome)
.put("path.data", this.pathData)
.put("cluster.name", this.clusterName)
.put("node.max_local_storage_nodes", 100)
.put("script.inline", "true")
.build(), asList(Netty4Plugin.class)).start().client();
}
private Settings loadConfig() throws IOException {
if (StringUtils.isNotBlank(pathConfiguration)) {
if (!StringUtils.isEmpty(pathConfiguration)) {
InputStream stream = getClass().getClassLoader().getResourceAsStream(pathConfiguration);
if (stream != null) {
return Settings.builder().loadFromStream(pathConfiguration, getClass().getClassLoader().getResourceAsStream(pathConfiguration)).build();
return Settings.builder().loadFromStream(pathConfiguration, getClass().getClassLoader().getResourceAsStream(pathConfiguration), false).build();
}
logger.error(String.format("Unable to read node configuration from file [%s]", pathConfiguration));
}
@@ -1,156 +1,152 @@
/*
* Copyright 2013 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.client;
import static org.apache.commons.lang.StringUtils.*;
import java.net.InetAddress;
import java.util.Properties;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
/*
* Copyright 2013 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.client;
import java.util.Properties;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* TransportClientFactoryBean
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Jakub Vavrik
* @author Piotr Betkier
*/
public class TransportClientFactoryBean implements FactoryBean<TransportClient>, InitializingBean, DisposableBean {
private static final Logger logger = LoggerFactory.getLogger(TransportClientFactoryBean.class);
private String clusterNodes = "127.0.0.1:9300";
private String clusterName = "elasticsearch";
private Boolean clientTransportSniff = true;
private Boolean clientIgnoreClusterName = Boolean.FALSE;
private String clientPingTimeout = "5s";
private String clientNodesSamplerInterval = "5s";
private TransportClient client;
private Properties properties;
static final String COLON = ":";
static final String COMMA = ",";
@Override
public void destroy() throws Exception {
try {
logger.info("Closing elasticSearch client");
if (client != null) {
client.close();
}
} catch (final Exception e) {
logger.error("Error closing ElasticSearch client: ", e);
}
}
@Override
public TransportClient getObject() throws Exception {
return client;
}
@Override
public Class<TransportClient> getObjectType() {
return TransportClient.class;
}
@Override
public boolean isSingleton() {
return false;
}
@Override
public void afterPropertiesSet() throws Exception {
buildClient();
}
protected void buildClient() throws Exception {
client = new PreBuiltTransportClient(settings());
Assert.hasText(clusterNodes, "[Assertion failed] clusterNodes settings missing.");
for (String clusterNode : split(clusterNodes, COMMA)) {
String hostName = substringBeforeLast(clusterNode, COLON);
String port = substringAfterLast(clusterNode, COLON);
Assert.hasText(hostName, "[Assertion failed] missing host name in 'clusterNodes'");
Assert.hasText(port, "[Assertion failed] missing port in 'clusterNodes'");
logger.info("adding transport node : " + clusterNode);
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(hostName), Integer.valueOf(port)));
}
client.connectedNodes();
}
private Settings settings() {
if (properties != null) {
return Settings.builder().put(properties).build();
}
return Settings.builder()
.put("cluster.name", clusterName)
.put("client.transport.sniff", clientTransportSniff)
.put("client.transport.ignore_cluster_name", clientIgnoreClusterName)
.put("client.transport.ping_timeout", clientPingTimeout)
.put("client.transport.nodes_sampler_interval", clientNodesSamplerInterval)
.build();
}
public void setClusterNodes(String clusterNodes) {
this.clusterNodes = clusterNodes;
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public void setClientTransportSniff(Boolean clientTransportSniff) {
this.clientTransportSniff = clientTransportSniff;
}
public String getClientNodesSamplerInterval() {
return clientNodesSamplerInterval;
}
public void setClientNodesSamplerInterval(String clientNodesSamplerInterval) {
this.clientNodesSamplerInterval = clientNodesSamplerInterval;
}
public String getClientPingTimeout() {
return clientPingTimeout;
}
public void setClientPingTimeout(String clientPingTimeout) {
this.clientPingTimeout = clientPingTimeout;
}
public Boolean getClientIgnoreClusterName() {
return clientIgnoreClusterName;
}
public void setClientIgnoreClusterName(Boolean clientIgnoreClusterName) {
this.clientIgnoreClusterName = clientIgnoreClusterName;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
/**
* TransportClientFactoryBean
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Jakub Vavrik
* @author Piotr Betkier
* @author Ilkang Na
* @author Oliver Gierke
*/
public class TransportClientFactoryBean implements FactoryBean<TransportClient>, InitializingBean, DisposableBean {
private static final Logger logger = LoggerFactory.getLogger(TransportClientFactoryBean.class);
private ClusterNodes clusterNodes = ClusterNodes.of("127.0.0.1:9300");
private String clusterName = "elasticsearch";
private Boolean clientTransportSniff = true;
private Boolean clientIgnoreClusterName = Boolean.FALSE;
private String clientPingTimeout = "5s";
private String clientNodesSamplerInterval = "5s";
private TransportClient client;
private Properties properties;
@Override
public void destroy() throws Exception {
try {
logger.info("Closing elasticSearch client");
if (client != null) {
client.close();
}
} catch (final Exception e) {
logger.error("Error closing ElasticSearch client: ", e);
}
}
@Override
public TransportClient getObject() throws Exception {
return client;
}
@Override
public Class<TransportClient> getObjectType() {
return TransportClient.class;
}
@Override
public boolean isSingleton() {
return false;
}
@Override
public void afterPropertiesSet() throws Exception {
buildClient();
}
protected void buildClient() throws Exception {
client = new PreBuiltTransportClient(settings());
clusterNodes.stream() //
.peek(it -> logger.info("Adding transport node : " + it.toString())) //
.forEach(client::addTransportAddress);
client.connectedNodes();
}
private Settings settings() {
if (properties != null) {
Settings.Builder builder = Settings.builder();
properties.forEach((key, value) -> {
builder.put(key.toString(), value.toString());
});
return builder.build();
}
return Settings.builder()
.put("cluster.name", clusterName)
.put("client.transport.sniff", clientTransportSniff)
.put("client.transport.ignore_cluster_name", clientIgnoreClusterName)
.put("client.transport.ping_timeout", clientPingTimeout)
.put("client.transport.nodes_sampler_interval", clientNodesSamplerInterval)
.build();
}
public void setClusterNodes(String clusterNodes) {
this.clusterNodes = ClusterNodes.of(clusterNodes);
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public void setClientTransportSniff(Boolean clientTransportSniff) {
this.clientTransportSniff = clientTransportSniff;
}
public String getClientNodesSamplerInterval() {
return clientNodesSamplerInterval;
}
public void setClientNodesSamplerInterval(String clientNodesSamplerInterval) {
this.clientNodesSamplerInterval = clientNodesSamplerInterval;
}
public String getClientPingTimeout() {
return clientPingTimeout;
}
public void setClientPingTimeout(String clientPingTimeout) {
this.clientPingTimeout = clientPingTimeout;
}
public Boolean getClientIgnoreClusterName() {
return clientIgnoreClusterName;
}
public void setClientIgnoreClusterName(Boolean clientIgnoreClusterName) {
this.clientIgnoreClusterName = clientIgnoreClusterName;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
@@ -15,11 +15,11 @@
*/
package org.springframework.data.elasticsearch.core;
import static org.apache.commons.lang.StringUtils.*;
import java.io.IOException;
import org.springframework.data.elasticsearch.ElasticsearchException;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* @author Artur Konczak
@@ -29,11 +29,14 @@ public abstract class AbstractResultMapper implements ResultsMapper {
private EntityMapper entityMapper;
public AbstractResultMapper(EntityMapper entityMapper) {
Assert.notNull(entityMapper, "EntityMapper must not be null!");
this.entityMapper = entityMapper;
}
public <T> T mapEntity(String source, Class<T> clazz) {
if (isBlank(source)) {
if (StringUtils.isEmpty(source)) {
return null;
}
try {
@@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2014-2018 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.
@@ -16,52 +16,142 @@
package org.springframework.data.elasticsearch.core;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.annotation.ReadOnlyProperty;
import org.springframework.data.elasticsearch.core.geo.CustomGeoModule;
import org.springframework.data.geo.*;
import java.util.List;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.util.Assert;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
/**
* DocumentMapper using jackson
* EntityMapper based on a Jackson {@link ObjectMapper}.
*
* @author Artur Konczak
* @author Petar Tahchiev
* @author Oliver Gierke
*/
public class DefaultEntityMapper implements EntityMapper {
private ObjectMapper objectMapper;
public DefaultEntityMapper() {
/**
* Creates a new {@link DefaultEntityMapper} using the given {@link MappingContext}.
*
* @param context must not be {@literal null}.
*/
public DefaultEntityMapper(
MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> context) {
Assert.notNull(context, "MappingContext must not be null!");
objectMapper = new ObjectMapper();
objectMapper.registerModule(new SpringDataElasticsearchModule(context));
objectMapper.registerModule(new CustomGeoModule());
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
objectMapper.registerModule(new CustomGeoModule());
}
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.core.EntityMapper#mapToString(java.lang.Object)
*/
@Override
public String mapToString(Object object) throws IOException {
return objectMapper.writeValueAsString(object);
}
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.core.EntityMapper#mapToObject(java.lang.String, java.lang.Class)
*/
@Override
public <T> T mapToObject(String source, Class<T> clazz) throws IOException {
return objectMapper.readValue(source, clazz);
}
/**
* A simple Jackson module to register the {@link SpringDataSerializerModifier}.
*
* @author Oliver Gierke
* @since 3.1
*/
private static class SpringDataElasticsearchModule extends SimpleModule {
private static final long serialVersionUID = -9168968092458058966L;
/**
* Creates a new {@link SpringDataElasticsearchModule} using the given {@link MappingContext}.
*
* @param context must not be {@literal null}.
*/
public SpringDataElasticsearchModule(
MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> context) {
Assert.notNull(context, "MappingContext must not be null!");
setSerializerModifier(new SpringDataSerializerModifier(context));
}
/**
* A {@link BeanSerializerModifier} that will drop properties annotated with {@link ReadOnlyProperty} for
* serialization.
*
* @author Oliver Gierke
* @since 3.1
*/
private static class SpringDataSerializerModifier extends BeanSerializerModifier {
private final MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> context;
public SpringDataSerializerModifier(
MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> context) {
Assert.notNull(context, "MappingContext must not be null!");
this.context = context;
}
/*
* (non-Javadoc)
* @see com.fasterxml.jackson.databind.ser.BeanSerializerModifier#changeProperties(com.fasterxml.jackson.databind.SerializationConfig, com.fasterxml.jackson.databind.BeanDescription, java.util.List)
*/
@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription description,
List<BeanPropertyWriter> properties) {
Class<?> type = description.getBeanClass();
ElasticsearchPersistentEntity<?> entity = context.getPersistentEntity(type);
if (entity == null) {
return super.changeProperties(config, description, properties);
}
List<BeanPropertyWriter> result = new ArrayList<>(properties.size());
for (BeanPropertyWriter beanPropertyWriter : properties) {
ElasticsearchPersistentProperty property = entity.getPersistentProperty(beanPropertyWriter.getName());
if (property != null && property.isWritable()) {
result.add(beanPropertyWriter);
}
}
return result;
}
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 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.
@@ -23,13 +23,12 @@ import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.get.MultiGetItemResponse;
import org.elasticsearch.action.get.MultiGetResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.document.DocumentField;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHitField;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.ElasticsearchException;
import org.springframework.data.elasticsearch.annotations.Document;
@@ -38,62 +37,84 @@ import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.util.Assert;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import org.springframework.util.StringUtils;
/**
* @author Artur Konczak
* @author Petar Tahchiev
* @author Young Gu
* @author Oliver Gierke
* @author Chris White
* @author Mark Paluch
* @author Ilkang Na
* @author Sascha Woo
*/
public class DefaultResultMapper extends AbstractResultMapper {
private MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext;
private final MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext;
public DefaultResultMapper() {
super(new DefaultEntityMapper());
this(new SimpleElasticsearchMappingContext());
}
public DefaultResultMapper(MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext) {
super(new DefaultEntityMapper());
super(new DefaultEntityMapper(mappingContext));
Assert.notNull(mappingContext, "MappingContext must not be null!");
this.mappingContext = mappingContext;
}
public DefaultResultMapper(EntityMapper entityMapper) {
super(entityMapper);
this(new SimpleElasticsearchMappingContext(), entityMapper);
}
public DefaultResultMapper(
MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext,
EntityMapper entityMapper) {
super(entityMapper);
Assert.notNull(mappingContext, "MappingContext must not be null!");
this.mappingContext = mappingContext;
}
@Override
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
long totalHits = response.getHits().totalHits();
long totalHits = response.getHits().getTotalHits();
float maxScore = response.getHits().getMaxScore();
List<T> results = new ArrayList<>();
for (SearchHit hit : response.getHits()) {
if (hit != null) {
T result = null;
if (StringUtils.isNotBlank(hit.sourceAsString())) {
result = mapEntity(hit.sourceAsString(), clazz);
if (!StringUtils.isEmpty(hit.getSourceAsString())) {
result = mapEntity(hit.getSourceAsString(), clazz);
} else {
result = mapEntity(hit.getFields().values(), clazz);
}
setPersistentEntityId(result, hit.getId(), clazz);
setPersistentEntityVersion(result, hit.getVersion(), clazz);
setPersistentEntityScore(result, hit.getScore(), clazz);
populateScriptFields(result, hit);
results.add(result);
}
}
return new AggregatedPageImpl<T>(results, pageable, totalHits, response.getAggregations(), response.getScrollId());
return new AggregatedPageImpl<T>(results, pageable, totalHits, response.getAggregations(), response.getScrollId(),
maxScore);
}
private <T> void populateScriptFields(T result, SearchHit hit) {
@@ -102,14 +123,14 @@ public class DefaultResultMapper extends AbstractResultMapper {
ScriptedField scriptedField = field.getAnnotation(ScriptedField.class);
if (scriptedField != null) {
String name = scriptedField.name().isEmpty() ? field.getName() : scriptedField.name();
SearchHitField searchHitField = hit.getFields().get(name);
DocumentField searchHitField = hit.getFields().get(name);
if (searchHitField != null) {
field.setAccessible(true);
try {
field.set(result, searchHitField.getValue());
} catch (IllegalArgumentException e) {
throw new ElasticsearchException("failed to set scripted field: " + name + " with value: "
+ searchHitField.getValue(), e);
throw new ElasticsearchException(
"failed to set scripted field: " + name + " with value: " + searchHitField.getValue(), e);
} catch (IllegalAccessException e) {
throw new ElasticsearchException("failed to access scripted field: " + name, e);
}
@@ -119,17 +140,17 @@ public class DefaultResultMapper extends AbstractResultMapper {
}
}
private <T> T mapEntity(Collection<SearchHitField> values, Class<T> clazz) {
private <T> T mapEntity(Collection<DocumentField> values, Class<T> clazz) {
return mapEntity(buildJSONFromFields(values), clazz);
}
private String buildJSONFromFields(Collection<SearchHitField> values) {
private String buildJSONFromFields(Collection<DocumentField> values) {
JsonFactory nodeFactory = new JsonFactory();
try {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
JsonGenerator generator = nodeFactory.createGenerator(stream, JsonEncoding.UTF8);
generator.writeStartObject();
for (SearchHitField value : values) {
for (DocumentField value : values) {
if (value.getValues().size() > 1) {
generator.writeArrayFieldStart(value.getName());
for (Object val : value.getValues()) {
@@ -153,6 +174,7 @@ public class DefaultResultMapper extends AbstractResultMapper {
T result = mapEntity(response.getSourceAsString(), clazz);
if (result != null) {
setPersistentEntityId(result, response.getId(), clazz);
setPersistentEntityVersion(result, response.getVersion(), clazz);
}
return result;
}
@@ -164,6 +186,7 @@ public class DefaultResultMapper extends AbstractResultMapper {
if (!response.isFailed() && response.getResponse().isExists()) {
T result = mapEntity(response.getResponse().getSourceAsString(), clazz);
setPersistentEntityId(result, response.getResponse().getId(), clazz);
setPersistentEntityVersion(result, response.getResponse().getVersion(), clazz);
list.add(result);
}
}
@@ -171,9 +194,9 @@ public class DefaultResultMapper extends AbstractResultMapper {
}
private <T> void setPersistentEntityId(T result, String id, Class<T> clazz) {
if (mappingContext != null && clazz.isAnnotationPresent(Document.class)) {
if (clazz.isAnnotationPresent(Document.class)) {
ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getRequiredPersistentEntity(clazz);
ElasticsearchPersistentProperty idProperty = persistentEntity.getIdProperty();
@@ -181,7 +204,38 @@ public class DefaultResultMapper extends AbstractResultMapper {
if (idProperty != null && idProperty.getType().isAssignableFrom(String.class)) {
persistentEntity.getPropertyAccessor(result).setProperty(idProperty, id);
}
}
}
private <T> void setPersistentEntityVersion(T result, long version, Class<T> clazz) {
if (clazz.isAnnotationPresent(Document.class)) {
ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(clazz);
ElasticsearchPersistentProperty versionProperty = persistentEntity.getVersionProperty();
// Only deal with Long because ES versions are longs !
if (versionProperty != null && versionProperty.getType().isAssignableFrom(Long.class)) {
// check that a version was actually returned in the response, -1 would indicate that
// a search didn't request the version ids in the response, which would be an issue
Assert.isTrue(version != -1, "Version in response is -1");
persistentEntity.getPropertyAccessor(result).setProperty(versionProperty, version);
}
}
}
private <T> void setPersistentEntityScore(T result, float score, Class<T> clazz) {
if (clazz.isAnnotationPresent(Document.class)) {
ElasticsearchPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(clazz);
if (!entity.hasScoreProperty()) {
return;
}
entity.getPropertyAccessor(result) //
.setProperty(entity.getScoreProperty(), score);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2018 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.
@@ -15,6 +15,12 @@
*/
package org.springframework.data.elasticsearch.core;
import static org.elasticsearch.client.Requests.*;
import static org.elasticsearch.index.VersionType.*;
import static org.elasticsearch.index.query.QueryBuilders.*;
import static org.springframework.data.elasticsearch.core.MappingBuilder.*;
import static org.springframework.util.CollectionUtils.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -26,7 +32,9 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import org.elasticsearch.action.ListenableActionFuture;
import java.util.stream.Collectors;
import org.elasticsearch.action.ActionFuture;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
@@ -44,6 +52,7 @@ import org.elasticsearch.action.get.MultiGetResponse;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequestBuilder;
import org.elasticsearch.action.update.UpdateRequestBuilder;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.Client;
@@ -51,15 +60,20 @@ import org.elasticsearch.client.Requests;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.MoreLikeThisQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.ScoreSortBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.slf4j.Logger;
@@ -84,15 +98,24 @@ import org.springframework.data.elasticsearch.core.facet.FacetRequest;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.data.elasticsearch.core.query.AliasQuery;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.DeleteQuery;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.GetQuery;
import org.springframework.data.elasticsearch.core.query.IndexBoost;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.MoreLikeThisQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.ScriptField;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.data.elasticsearch.core.query.SourceFilter;
import org.springframework.data.elasticsearch.core.query.StringQuery;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.data.util.CloseableIterator;
import org.springframework.util.Assert;
import static org.apache.commons.lang.StringUtils.*;
import static org.elasticsearch.client.Requests.*;
import static org.elasticsearch.index.VersionType.*;
import static org.elasticsearch.index.query.QueryBuilders.*;
import static org.springframework.data.elasticsearch.core.MappingBuilder.*;
import static org.springframework.util.CollectionUtils.isEmpty;
import org.springframework.util.StringUtils;
/**
* ElasticsearchTemplate
@@ -105,11 +128,20 @@ import static org.springframework.util.CollectionUtils.isEmpty;
* @author Young Gu
* @author Oliver Gierke
* @author Mark Janssen
* @author Chris White
* @author Mark Paluch
* @author Ilkang Na
* @author Alen Turkovic
* @author Sascha Woo
* @author Ted Liang
* @author Jean-Baptiste Nizet
*/
public class ElasticsearchTemplate implements ElasticsearchOperations, ApplicationContextAware {
private static final Logger logger = LoggerFactory.getLogger(ElasticsearchTemplate.class);
private static final Logger QUERY_LOGGER = LoggerFactory.getLogger("org.springframework.data.elasticsearch.core.QUERY");
private static final Logger LOGGER = LoggerFactory.getLogger(ElasticsearchTemplate.class);
private static final String FIELD_SCORE = "_score";
private Client client;
private ElasticsearchConverter elasticsearchConverter;
private ResultsMapper resultsMapper;
@@ -173,13 +205,13 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
public <T> boolean putMapping(Class<T> clazz) {
if (clazz.isAnnotationPresent(Mapping.class)) {
String mappingPath = clazz.getAnnotation(Mapping.class).mappingPath();
if (isNotBlank(mappingPath)) {
if (!StringUtils.isEmpty(mappingPath)) {
String mappings = readFileFromClasspath(mappingPath);
if (isNotBlank(mappings)) {
if (!StringUtils.isEmpty(mappings)) {
return putMapping(clazz, mappings);
}
} else {
logger.info("mappingPath in @Mapping has to be defined. Building mappings using @Field");
LOGGER.info("mappingPath in @Mapping has to be defined. Building mappings using @Field");
}
}
ElasticsearchPersistentEntity<T> persistentEntity = getPersistentEntityFor(clazz);
@@ -208,7 +240,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
Assert.notNull(type, "No type defined for putMapping()");
PutMappingRequestBuilder requestBuilder = client.admin().indices().preparePutMapping(indexName).setType(type);
if (mapping instanceof String) {
requestBuilder.setSource(String.valueOf(mapping));
requestBuilder.setSource(String.valueOf(mapping), XContentType.JSON);
} else if (mapping instanceof Map) {
requestBuilder.setSource((Map) mapping);
} else if (mapping instanceof XContentBuilder) {
@@ -310,7 +342,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
if (query.getFilter() != null) {
request.setPostFilter(query.getFilter());
}
SearchResponse response = getSearchResponse(request.execute());
SearchResponse response = getSearchResponse(request);
return extractIds(response);
}
@@ -333,11 +365,8 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
if (elasticsearchFilter != null)
searchRequestBuilder.setPostFilter(elasticsearchFilter);
if (logger.isDebugEnabled()) {
logger.debug("doSearch query:\n" + searchRequestBuilder.toString());
}
SearchResponse response = getSearchResponse(searchRequestBuilder.execute());
SearchResponse response = getSearchResponse(searchRequestBuilder);
return resultsMapper.mapResults(response, clazz, criteriaQuery.getPageable());
}
@@ -348,7 +377,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
@Override
public <T> Page<T> queryForPage(StringQuery query, Class<T> clazz, SearchResultMapper mapper) {
SearchResponse response = getSearchResponse(prepareSearch(query, clazz).setQuery(wrapperQuery(query.getSource())).execute());
SearchResponse response = getSearchResponse(prepareSearch(query, clazz).setQuery(wrapperQuery(query.getSource())));
return mapper.mapResults(response, clazz, query.getPageable());
}
@@ -559,9 +588,9 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
}
private UpdateRequestBuilder prepareUpdate(UpdateQuery query) {
String indexName = isNotBlank(query.getIndexName()) ? query.getIndexName()
String indexName = !StringUtils.isEmpty(query.getIndexName()) ? query.getIndexName()
: getPersistentEntityFor(query.getClazz()).getIndexName();
String type = isNotBlank(query.getType()) ? query.getType()
String type = !StringUtils.isEmpty(query.getType()) ? query.getType()
: getPersistentEntityFor(query.getClazz()).getIndexType();
Assert.notNull(indexName, "No index defined for Query");
Assert.notNull(type, "No type define for Query");
@@ -661,9 +690,9 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
@Override
public <T> void delete(DeleteQuery deleteQuery, Class<T> clazz) {
String indexName = isNotBlank(deleteQuery.getIndex()) ? deleteQuery.getIndex()
String indexName = !StringUtils.isEmpty(deleteQuery.getIndex()) ? deleteQuery.getIndex()
: getPersistentEntityFor(clazz).getIndexName();
String typeName = isNotBlank(deleteQuery.getType()) ? deleteQuery.getType()
String typeName = !StringUtils.isEmpty(deleteQuery.getType()) ? deleteQuery.getType()
: getPersistentEntityFor(clazz).getIndexType();
Integer pageSize = deleteQuery.getPageSize() != null ? deleteQuery.getPageSize() : 1000;
Long scrollTimeInMillis = deleteQuery.getScrollTimeInMillis() != null ? deleteQuery.getScrollTimeInMillis()
@@ -730,7 +759,10 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
private SearchRequestBuilder prepareScroll(Query query, long scrollTimeInMillis) {
SearchRequestBuilder requestBuilder = client.prepareSearch(toArray(query.getIndices()))
.setTypes(toArray(query.getTypes())).setScroll(TimeValue.timeValueMillis(scrollTimeInMillis)).setFrom(0);
.setTypes(toArray(query.getTypes()))
.setScroll(TimeValue.timeValueMillis(scrollTimeInMillis))
.setFrom(0)
.setVersion(true);
if(query.getPageable().isPaged()){
requestBuilder.setSize(query.getPageable().getPageSize());
@@ -761,7 +793,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
requestBuilder.setPostFilter(elasticsearchFilter);
}
return getSearchResponse(requestBuilder.execute());
return getSearchResponse(requestBuilder);
}
private SearchResponse doScroll(SearchRequestBuilder requestBuilder, SearchQuery searchQuery) {
@@ -773,7 +805,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
requestBuilder.setPostFilter(searchQuery.getFilter());
}
return getSearchResponse(requestBuilder.setQuery(searchQuery.getQuery()).execute());
return getSearchResponse(requestBuilder.setQuery(searchQuery.getQuery()));
}
public <T> Page<T> startScroll(long scrollTimeInMillis, SearchQuery searchQuery, Class<T> clazz) {
@@ -817,8 +849,8 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
public <T> Page<T> moreLikeThis(MoreLikeThisQuery query, Class<T> clazz) {
ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(clazz);
String indexName = isNotBlank(query.getIndexName()) ? query.getIndexName() : persistentEntity.getIndexName();
String type = isNotBlank(query.getType()) ? query.getType() : persistentEntity.getIndexType();
String indexName = !StringUtils.isEmpty(query.getIndexName()) ? query.getIndexName() : persistentEntity.getIndexName();
String type = !StringUtils.isEmpty(query.getType()) ? query.getType() : persistentEntity.getIndexType();
Assert.notNull(indexName, "No 'indexName' defined for MoreLikeThisQuery");
Assert.notNull(type, "No 'type' defined for MoreLikeThisQuery");
@@ -867,17 +899,22 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
}
if (!searchQuery.getScriptFields().isEmpty()) {
//_source should be return all the time
//searchRequest.addStoredField("_source");
// _source should be return all the time
// searchRequest.addStoredField("_source");
for (ScriptField scriptedField : searchQuery.getScriptFields()) {
searchRequest.addScriptField(scriptedField.fieldName(), scriptedField.script());
}
}
if (searchQuery.getHighlightFields() != null) {
for (HighlightBuilder.Field highlightField : searchQuery.getHighlightFields()) {
searchRequest.highlighter(new HighlightBuilder().field(highlightField));
if (searchQuery.getHighlightFields() != null || searchQuery.getHighlightBuilder() != null) {
HighlightBuilder highlightBuilder = searchQuery.getHighlightBuilder();
if (highlightBuilder == null) {
highlightBuilder = new HighlightBuilder();
}
for (HighlightBuilder.Field highlightField : searchQuery.getHighlightFields()) {
highlightBuilder.field(highlightField);
}
searchRequest.highlighter(highlightBuilder);
}
if (!isEmpty(searchQuery.getIndicesBoost())) {
@@ -897,10 +934,19 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
searchRequest.addAggregation(aggregatedFacet.getFacet());
}
}
return getSearchResponse(searchRequest.setQuery(searchQuery.getQuery()).execute());
return getSearchResponse(searchRequest.setQuery(searchQuery.getQuery()));
}
private SearchResponse getSearchResponse(ListenableActionFuture<SearchResponse> response) {
private SearchResponse getSearchResponse(SearchRequestBuilder requestBuilder) {
if (QUERY_LOGGER.isDebugEnabled()) {
QUERY_LOGGER.debug(requestBuilder.toString());
}
return getSearchResponse(requestBuilder.execute());
}
private SearchResponse getSearchResponse(ActionFuture<SearchResponse> response) {
return searchTimeout == null ? response.actionGet() : response.actionGet(searchTimeout);
}
@@ -911,13 +957,13 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
private <T> boolean createIndexWithSettings(Class<T> clazz) {
if (clazz.isAnnotationPresent(Setting.class)) {
String settingPath = clazz.getAnnotation(Setting.class).settingPath();
if (isNotBlank(settingPath)) {
if (!StringUtils.isEmpty(settingPath)) {
String settings = readFileFromClasspath(settingPath);
if (isNotBlank(settings)) {
if (!StringUtils.isEmpty(settings)) {
return createIndex(getPersistentEntityFor(clazz).getIndexName(), settings);
}
} else {
logger.info("settingPath in @Setting has to be defined. Using default instead.");
LOGGER.info("settingPath in @Setting has to be defined. Using default instead.");
}
}
return createIndex(getPersistentEntityFor(clazz).getIndexName(), getDefaultSettings(getPersistentEntityFor(clazz)));
@@ -927,7 +973,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
public boolean createIndex(String indexName, Object settings) {
CreateIndexRequestBuilder createIndexRequestBuilder = client.admin().indices().prepareCreate(indexName);
if (settings instanceof String) {
createIndexRequestBuilder.setSettings(String.valueOf(settings));
createIndexRequestBuilder.setSettings(String.valueOf(settings), Requests.INDEX_CONTENT_TYPE);
} else if (settings instanceof Map) {
createIndexRequestBuilder.setSettings((Map) settings);
} else if (settings instanceof XContentBuilder) {
@@ -960,8 +1006,9 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
@Override
public Map getSetting(String indexName) {
Assert.notNull(indexName, "No index defined for getSettings");
return client.admin().indices().getSettings(new GetSettingsRequest()).actionGet().getIndexToSettings()
.get(indexName).getAsMap();
Settings settings = client.admin().indices().getSettings(new GetSettingsRequest()).actionGet()
.getIndexToSettings().get(indexName);
return settings.keySet().stream().collect(Collectors.toMap((key)->key, (key)->settings.get(key)));
}
private <T> SearchRequestBuilder prepareSearch(Query query, Class<T> clazz) {
@@ -975,7 +1022,10 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
int startRecord = 0;
SearchRequestBuilder searchRequestBuilder = client.prepareSearch(toArray(query.getIndices()))
.setSearchType(query.getSearchType()).setTypes(toArray(query.getTypes()));
.setSearchType(query.getSearchType())
.setTypes(toArray(query.getTypes()))
.setVersion(true)
.setTrackScores(query.getTrackScores());
if (query.getSourceFilter() != null) {
SourceFilter sourceFilter = query.getSourceFilter();
@@ -989,13 +1039,36 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
searchRequestBuilder.setFrom(startRecord);
if (!query.getFields().isEmpty()) {
searchRequestBuilder.setFetchSource(toArray(query.getFields()),null);
searchRequestBuilder.setFetchSource(toArray(query.getFields()), null);
}
if (query.getIndicesOptions() != null) {
searchRequestBuilder.setIndicesOptions(query.getIndicesOptions());
}
if (query.getSort() != null) {
for (Sort.Order order : query.getSort()) {
searchRequestBuilder.addSort(order.getProperty(),
order.getDirection() == Sort.Direction.DESC ? SortOrder.DESC : SortOrder.ASC);
SortOrder sortOrder = order.getDirection().isDescending() ? SortOrder.DESC : SortOrder.ASC;
if (FIELD_SCORE.equals(order.getProperty())) {
ScoreSortBuilder sort = SortBuilders //
.scoreSort() //
.order(sortOrder);
searchRequestBuilder.addSort(sort);
} else {
FieldSortBuilder sort = SortBuilders //
.fieldSort(order.getProperty()) //
.order(sortOrder);
if (order.getNullHandling() == Sort.NullHandling.NULLS_FIRST) {
sort.missing("_first");
} else if (order.getNullHandling() == Sort.NullHandling.NULLS_LAST) {
sort.missing("_last");
}
searchRequestBuilder.addSort(sort);
}
}
}
@@ -1007,24 +1080,27 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
private IndexRequestBuilder prepareIndex(IndexQuery query) {
try {
String indexName = isBlank(query.getIndexName())
? retrieveIndexNameFromPersistentEntity(query.getObject().getClass())[0] : query.getIndexName();
String type = isBlank(query.getType()) ? retrieveTypeFromPersistentEntity(query.getObject().getClass())[0]
String indexName = StringUtils.isEmpty(query.getIndexName())
? retrieveIndexNameFromPersistentEntity(query.getObject().getClass())[0]
: query.getIndexName();
String type = StringUtils.isEmpty(query.getType()) ? retrieveTypeFromPersistentEntity(query.getObject().getClass())[0]
: query.getType();
IndexRequestBuilder indexRequestBuilder = null;
if (query.getObject() != null) {
String id = isBlank(query.getId()) ? getPersistentEntityId(query.getObject()) : query.getId();
String id = StringUtils.isEmpty(query.getId()) ? getPersistentEntityId(query.getObject()) : query.getId();
// If we have a query id and a document id, do not ask ES to generate one.
if (id != null) {
indexRequestBuilder = client.prepareIndex(indexName, type, id);
} else {
indexRequestBuilder = client.prepareIndex(indexName, type);
}
indexRequestBuilder.setSource(resultsMapper.getEntityMapper().mapToString(query.getObject()));
indexRequestBuilder.setSource(resultsMapper.getEntityMapper().mapToString(query.getObject()),
Requests.INDEX_CONTENT_TYPE);
} else if (query.getSource() != null) {
indexRequestBuilder = client.prepareIndex(indexName, type, query.getId()).setSource(query.getSource());
indexRequestBuilder = client.prepareIndex(indexName, type, query.getId()).setSource(query.getSource(),
Requests.INDEX_CONTENT_TYPE);
} else {
throw new ElasticsearchException(
"object or source is null, failed to index the document [id: " + query.getId() + "]");
@@ -1059,17 +1135,18 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
public Boolean addAlias(AliasQuery query) {
Assert.notNull(query.getIndexName(), "No index defined for Alias");
Assert.notNull(query.getAliasName(), "No alias defined");
final IndicesAliasesRequest.AliasActions aliasAction = IndicesAliasesRequest.AliasActions.add().alias(query.getAliasName()).index(query.getIndexName());
final IndicesAliasesRequest.AliasActions aliasAction = IndicesAliasesRequest.AliasActions.add()
.alias(query.getAliasName()).index(query.getIndexName());
if (query.getFilterBuilder() != null) {
aliasAction.filter(query.getFilterBuilder());
} else if (query.getFilter() != null) {
aliasAction.filter(query.getFilter());
} else if (isNotBlank(query.getRouting())) {
} else if (!StringUtils.isEmpty(query.getRouting())) {
aliasAction.routing(query.getRouting());
} else if (isNotBlank(query.getSearchRouting())) {
} else if (!StringUtils.isEmpty(query.getSearchRouting())) {
aliasAction.searchRouting(query.getSearchRouting());
} else if (isNotBlank(query.getIndexRouting())) {
} else if (!StringUtils.isEmpty(query.getIndexRouting())) {
aliasAction.indexRouting(query.getIndexRouting());
}
return client.admin().indices().prepareAliases().addAliasAction(aliasAction).execute().actionGet().isAcknowledged();
@@ -1101,7 +1178,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
ElasticsearchPersistentEntity<?> persistentEntity = getPersistentEntityFor(entity.getClass());
Object identifier = persistentEntity.getIdentifierAccessor(entity).getIdentifier();
if (identifier != null){
if (identifier != null) {
return identifier.toString();
}
@@ -1189,14 +1266,14 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
stringBuilder.append(line).append(lineSeparator);
}
} catch (Exception e) {
logger.debug(String.format("Failed to load file from url: %s: %s", url, e.getMessage()));
LOGGER.debug(String.format("Failed to load file from url: %s: %s", url, e.getMessage()));
return null;
} finally {
if (bufferedReader != null)
try {
bufferedReader.close();
} catch (IOException e) {
logger.debug(String.format("Unable to close buffered reader.. %s", e.getMessage()));
LOGGER.debug(String.format("Unable to close buffered reader.. %s", e.getMessage()));
}
}
@@ -40,6 +40,8 @@ import org.springframework.data.elasticsearch.core.facet.result.StatisticalResul
import org.springframework.data.elasticsearch.core.facet.result.Term;
import org.springframework.data.elasticsearch.core.facet.result.TermResult;
import static java.util.Optional.ofNullable;
/**
* Container for query result and facet results
*
@@ -48,6 +50,7 @@ import org.springframework.data.elasticsearch.core.facet.result.TermResult;
* @author Artur Konczak
* @author Jonathan Yan
* @author Philipp Kräutli
* @author Remco Zigterman
*/
@Deprecated
public abstract class FacetedPageImpl<T> extends PageImpl<T> implements FacetedPage<T>, AggregatedPage<T> {
@@ -60,7 +63,7 @@ public abstract class FacetedPageImpl<T> extends PageImpl<T> implements FacetedP
}
public FacetedPageImpl(List<T> content, Pageable pageable, long total) {
super(content, Pageable.unpaged(), total);
super(content, ofNullable(pageable).orElse(Pageable.unpaged()), total);
}
@Override
@@ -37,7 +37,8 @@ import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import static org.apache.commons.lang.StringUtils.*;
import org.springframework.util.StringUtils;
import static org.elasticsearch.common.xcontent.XContentFactory.*;
import static org.springframework.util.StringUtils.*;
@@ -50,6 +51,8 @@ import static org.springframework.util.StringUtils.*;
* @author Dennis Maaß
* @author Pavel Luhin
* @author Mark Paluch
* @author Sascha Woo
* @author Nordine Bittich
*/
class MappingBuilder {
@@ -67,8 +70,7 @@ class MappingBuilder {
public static final String COMPLETION_PRESERVE_POSITION_INCREMENTS = "preserve_position_increments";
public static final String COMPLETION_MAX_INPUT_LENGTH = "max_input_length";
public static final String INDEX_VALUE_NOT_ANALYZED = "not_analyzed";
public static final String TYPE_VALUE_STRING = "text";
public static final String TYPE_VALUE_KEYWORD = "keyword";
public static final String TYPE_VALUE_GEO_POINT = "geo_point";
public static final String TYPE_VALUE_COMPLETION = "completion";
public static final String TYPE_VALUE_GEO_HASH_PREFIX = "geohash_prefix";
@@ -87,7 +89,7 @@ class MappingBuilder {
// Properties
XContentBuilder xContentBuilder = mapping.startObject(FIELD_PROPERTIES);
mapEntity(xContentBuilder, clazz, true, idFieldName, EMPTY, false, FieldType.Auto, null);
mapEntity(xContentBuilder, clazz, true, idFieldName, "", false, FieldType.Auto, null);
return xContentBuilder.endObject().endObject().endObject();
}
@@ -118,7 +120,7 @@ class MappingBuilder {
if (field.isAnnotationPresent(Mapping.class)) {
String mappingPath = field.getAnnotation(Mapping.class).mappingPath();
if (isNotBlank(mappingPath)) {
if (!StringUtils.isEmpty(mappingPath)) {
ClassPathResource mappings = new ClassPathResource(mappingPath);
if (mappings.exists()) {
xContentBuilder.rawField(field.getName(), mappings.getInputStream());
@@ -136,7 +138,7 @@ class MappingBuilder {
continue;
}
boolean nestedOrObject = isNestedOrObjectField(field);
mapEntity(xContentBuilder, getFieldType(field), false, EMPTY, field.getName(), nestedOrObject, singleField.type(), field.getAnnotation(Field.class));
mapEntity(xContentBuilder, getFieldType(field), false, "", field.getName(), nestedOrObject, singleField.type(), field.getAnnotation(Field.class));
if (nestedOrObject) {
continue;
}
@@ -202,10 +204,10 @@ class MappingBuilder {
xContentBuilder.field(COMPLETION_MAX_INPUT_LENGTH, annotation.maxInputLength());
xContentBuilder.field(COMPLETION_PRESERVE_POSITION_INCREMENTS, annotation.preservePositionIncrements());
xContentBuilder.field(COMPLETION_PRESERVE_SEPARATORS, annotation.preserveSeparators());
if (isNotBlank(annotation.searchAnalyzer())) {
if (!StringUtils.isEmpty(annotation.searchAnalyzer())) {
xContentBuilder.field(FIELD_SEARCH_ANALYZER, annotation.searchAnalyzer());
}
if (isNotBlank(annotation.analyzer())) {
if (!StringUtils.isEmpty(annotation.analyzer())) {
xContentBuilder.field(FIELD_INDEX_ANALYZER, annotation.analyzer());
}
}
@@ -215,89 +217,107 @@ class MappingBuilder {
private static void applyDefaultIdFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field)
throws IOException {
xContentBuilder.startObject(field.getName())
.field(FIELD_TYPE, TYPE_VALUE_STRING)
.field(FIELD_INDEX, INDEX_VALUE_NOT_ANALYZED);
.field(FIELD_TYPE, TYPE_VALUE_KEYWORD)
.field(FIELD_INDEX, true);
xContentBuilder.endObject();
}
/**
* Apply mapping for a single @Field annotation
* Add mapping for @Field annotation
*
* @throws IOException
*/
private static void addSingleFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field,
Field fieldAnnotation, boolean nestedOrObjectField) throws IOException {
xContentBuilder.startObject(field.getName());
if(!nestedOrObjectField) {
xContentBuilder.field(FIELD_STORE, fieldAnnotation.store());
private static void addSingleFieldMapping(XContentBuilder builder, java.lang.reflect.Field field, Field annotation, boolean nestedOrObjectField) throws IOException {
builder.startObject(field.getName());
addFieldMappingParameters(builder, annotation, nestedOrObjectField);
builder.endObject();
}
/**
* Add mapping for @MultiField annotation
*
* @throws IOException
*/
private static void addMultiFieldMapping(
XContentBuilder builder,
java.lang.reflect.Field field,
MultiField annotation,
boolean nestedOrObjectField) throws IOException {
// main field
builder.startObject(field.getName());
addFieldMappingParameters(builder, annotation.mainField(), nestedOrObjectField);
// inner fields
builder.startObject("fields");
for (InnerField innerField : annotation.otherFields()) {
builder.startObject(innerField.suffix());
addFieldMappingParameters(builder, innerField, false);
builder.endObject();
}
if(fieldAnnotation.fielddata()) {
xContentBuilder.field(FIELD_DATA, fieldAnnotation.fielddata());
builder.endObject();
builder.endObject();
}
private static void addFieldMappingParameters(XContentBuilder builder, Object annotation, boolean nestedOrObjectField) throws IOException {
boolean index = true;
boolean store = false;
boolean fielddata = false;
FieldType type = null;
DateFormat dateFormat = null;
String datePattern = null;
String analyzer = null;
String searchAnalyzer = null;
if (annotation instanceof Field) {
// @Field
Field fieldAnnotation = (Field) annotation;
index = fieldAnnotation.index();
store = fieldAnnotation.store();
fielddata = fieldAnnotation.fielddata();
type = fieldAnnotation.type();
dateFormat = fieldAnnotation.format();
datePattern = fieldAnnotation.pattern();
analyzer = fieldAnnotation.analyzer();
searchAnalyzer = fieldAnnotation.searchAnalyzer();
} else if (annotation instanceof InnerField) {
// @InnerField
InnerField fieldAnnotation = (InnerField) annotation;
index = fieldAnnotation.index();
store = fieldAnnotation.store();
fielddata = fieldAnnotation.fielddata();
type = fieldAnnotation.type();
dateFormat = fieldAnnotation.format();
datePattern = fieldAnnotation.pattern();
analyzer = fieldAnnotation.analyzer();
searchAnalyzer = fieldAnnotation.searchAnalyzer();
} else {
throw new IllegalArgumentException("annotation must be an instance of @Field or @InnerField");
}
if (FieldType.Auto != fieldAnnotation.type()) {
xContentBuilder.field(FIELD_TYPE, fieldAnnotation.type().name().toLowerCase());
if (FieldType.Date == fieldAnnotation.type() && DateFormat.none != fieldAnnotation.format()) {
xContentBuilder.field(FIELD_FORMAT, DateFormat.custom == fieldAnnotation.format()
? fieldAnnotation.pattern() : fieldAnnotation.format());
if (!nestedOrObjectField) {
builder.field(FIELD_STORE, store);
}
if (fielddata) {
builder.field(FIELD_DATA, fielddata);
}
if (type != FieldType.Auto) {
builder.field(FIELD_TYPE, type.name().toLowerCase());
if (type == FieldType.Date && dateFormat != DateFormat.none) {
builder.field(FIELD_FORMAT, dateFormat == DateFormat.custom ? datePattern : dateFormat.toString());
}
}
if(!fieldAnnotation.index()) {
xContentBuilder.field(FIELD_INDEX, fieldAnnotation.index());
if (!index) {
builder.field(FIELD_INDEX, index);
}
if (isNotBlank(fieldAnnotation.searchAnalyzer())) {
xContentBuilder.field(FIELD_SEARCH_ANALYZER, fieldAnnotation.searchAnalyzer());
if (!StringUtils.isEmpty(analyzer)) {
builder.field(FIELD_INDEX_ANALYZER, analyzer);
}
if (isNotBlank(fieldAnnotation.analyzer())) {
xContentBuilder.field(FIELD_INDEX_ANALYZER, fieldAnnotation.analyzer());
if (!StringUtils.isEmpty(searchAnalyzer)) {
builder.field(FIELD_SEARCH_ANALYZER, searchAnalyzer);
}
xContentBuilder.endObject();
}
/**
* Apply mapping for a single nested @Field annotation
*
* @throws IOException
*/
private static void addNestedFieldMapping(XContentBuilder builder, java.lang.reflect.Field field,
InnerField annotation) throws IOException {
builder.startObject(annotation.suffix());
//builder.field(FIELD_STORE, annotation.store());
if (FieldType.Auto != annotation.type()) {
builder.field(FIELD_TYPE, annotation.type().name().toLowerCase());
}
if(!annotation.index()) {
builder.field(FIELD_INDEX, annotation.index());
}
if (isNotBlank(annotation.searchAnalyzer())) {
builder.field(FIELD_SEARCH_ANALYZER, annotation.searchAnalyzer());
}
if (isNotBlank(annotation.indexAnalyzer())) {
builder.field(FIELD_INDEX_ANALYZER, annotation.indexAnalyzer());
}
if (annotation.fielddata()) {
builder.field(FIELD_DATA, annotation.fielddata());
}
builder.endObject();
}
/**
* Multi field mappings for string type fields, support for sorts and facets
*
* @throws IOException
*/
private static void addMultiFieldMapping(XContentBuilder builder, java.lang.reflect.Field field,
MultiField annotation, boolean nestedOrObjectField) throws IOException {
builder.startObject(field.getName());
builder.field(FIELD_TYPE, annotation.mainField().type());
builder.startObject("fields");
//add standard field
//addSingleFieldMapping(builder, field, annotation.mainField(), nestedOrObjectField);
for (InnerField innerField : annotation.otherFields()) {
addNestedFieldMapping(builder, field, innerField);
}
builder.endObject();
builder.endObject();
}
protected static boolean isEntity(java.lang.reflect.Field field) {
@@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2018 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.
@@ -13,14 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.annotations;
package org.springframework.data.elasticsearch.core;
import org.springframework.data.domain.Page;
/**
* @author Artur Konczak
* @author Mohsin Husen
* @author Alexander Volz
* @author Dennis Maaß
* A score-aware page gaining information about max score.
*
* @param <T>
* @author Sascha Woo
*/
public enum FieldIndex {
not_analyzed, analyzed, no
public interface ScoredPage<T> extends Page<T> {
float getMaxScore();
}
@@ -3,12 +3,14 @@ package org.springframework.data.elasticsearch.core.aggregation;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.Aggregations;
import org.springframework.data.elasticsearch.core.FacetedPage;
import org.springframework.data.elasticsearch.core.ScoredPage;
import org.springframework.data.elasticsearch.core.ScrolledPage;
/**
* @author Petar Tahchiev
* @author Sascha Woo
*/
public interface AggregatedPage<T> extends FacetedPage<T>, ScrolledPage<T> {
public interface AggregatedPage<T> extends FacetedPage<T>, ScrolledPage<T>, ScoredPage<T> {
boolean hasAggregations();
@@ -15,9 +15,7 @@
*/
package org.springframework.data.elasticsearch.core.aggregation.impl;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.Aggregations;
@@ -29,55 +27,77 @@ import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
* @author Petar Tahchiev
* @author Artur Konczak
* @author Mohsin Husen
* @author Sascha Woo
*/
public class AggregatedPageImpl<T> extends FacetedPageImpl<T> implements AggregatedPage<T> {
private Aggregations aggregations;
private Map<String, Aggregation> mapOfAggregations = new HashMap<>();
private String scrollId;
private String scrollId;
private float maxScore;
public AggregatedPageImpl(List<T> content) {
super(content);
}
public AggregatedPageImpl(List<T> content, float maxScore) {
super(content);
this.maxScore = maxScore;
}
public AggregatedPageImpl(List<T> content, String scrollId) {
super(content);
this.scrollId = scrollId;
}
public AggregatedPageImpl(List<T> content, String scrollId, float maxScore) {
this(content, scrollId);
this.maxScore = maxScore;
}
public AggregatedPageImpl(List<T> content, Pageable pageable, long total) {
super(content, pageable, total);
}
public AggregatedPageImpl(List<T> content, Pageable pageable, long total, float maxScore) {
super(content, pageable, total);
this.maxScore = maxScore;
}
public AggregatedPageImpl(List<T> content, Pageable pageable, long total, String scrollId) {
super(content, pageable, total);
this.scrollId = scrollId;
}
public AggregatedPageImpl(List<T> content, Pageable pageable, long total, String scrollId, float maxScore) {
this(content, pageable, total, scrollId);
this.maxScore = maxScore;
}
public AggregatedPageImpl(List<T> content, Pageable pageable, long total, Aggregations aggregations) {
super(content, pageable, total);
this.aggregations = aggregations;
if (aggregations != null) {
for (Aggregation aggregation : aggregations) {
mapOfAggregations.put(aggregation.getName(), aggregation);
}
}
}
public AggregatedPageImpl(List<T> content, Pageable pageable, long total, Aggregations aggregations, String scrollId) {
super(content, pageable, total);
this.aggregations = aggregations;
public AggregatedPageImpl(List<T> content, Pageable pageable, long total, Aggregations aggregations, float maxScore) {
this(content, pageable, total, aggregations);
this.maxScore = maxScore;
}
public AggregatedPageImpl(List<T> content, Pageable pageable, long total, Aggregations aggregations,
String scrollId) {
this(content, pageable, total, aggregations);
this.scrollId = scrollId;
if (aggregations != null) {
for (Aggregation aggregation : aggregations) {
mapOfAggregations.put(aggregation.getName(), aggregation);
}
}
}
public AggregatedPageImpl(List<T> content, Pageable pageable, long total, Aggregations aggregations, String scrollId,
float maxScore) {
this(content, pageable, total, aggregations, scrollId);
this.maxScore = maxScore;
}
@Override
public boolean hasAggregations() {
return aggregations != null && mapOfAggregations.size() > 0;
return aggregations != null;
}
@Override
@@ -94,4 +114,9 @@ public class AggregatedPageImpl<T> extends FacetedPageImpl<T> implements Aggrega
public String getScrollId() {
return scrollId;
}
@Override
public float getMaxScore() {
return maxScore;
}
}
@@ -16,14 +16,13 @@
package org.springframework.data.elasticsearch.core.facet.request;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.springframework.data.elasticsearch.core.facet.AbstractFacetRequest;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* @author Artur Konczak
@@ -54,7 +53,7 @@ public class HistogramFacetRequest extends AbstractFacetRequest {
public AbstractAggregationBuilder getFacet() {
Assert.notNull(getName(), "Facet name can't be a null !!!");
Assert.isTrue(StringUtils.isNotBlank(field), "Please select field on which to build the facet !!!");
Assert.isTrue(!StringUtils.isEmpty(field), "Please select field on which to build the facet !!!");
Assert.isTrue(interval > 0, "Please provide interval as positive value greater them zero !!!");
DateHistogramAggregationBuilder dateHistogramBuilder = AggregationBuilders.dateHistogram(getName());
@@ -19,13 +19,12 @@ package org.springframework.data.elasticsearch.core.facet.request;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.range.RangeAggregationBuilder;
import org.springframework.data.elasticsearch.core.facet.AbstractFacetRequest;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Range facet for numeric fields
@@ -77,7 +76,7 @@ public class RangeFacetRequest extends AbstractFacetRequest {
Assert.notNull(getName(), "Facet name can't be a null !!!");
RangeAggregationBuilder rangeBuilder = AggregationBuilders.range(getName());
final String field = StringUtils.isNotBlank(keyField) ? keyField : this.field;
final String field = !StringUtils.isEmpty(keyField) ? keyField : this.field;
rangeBuilder.field(field);
for (Entry entry : entries) {
@@ -86,7 +85,7 @@ public class RangeFacetRequest extends AbstractFacetRequest {
}
rangeBuilder.subAggregation(AggregationBuilders.extendedStats(INTERNAL_STATS).field(field));
if(StringUtils.isNotBlank(valueField)){
if(!StringUtils.isEmpty(valueField)){
rangeBuilder.subAggregation(AggregationBuilders.sum(RANGE_INTERNAL_SUM).field(valueField));
}
@@ -16,12 +16,11 @@
package org.springframework.data.elasticsearch.core.facet.request;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.springframework.data.elasticsearch.core.facet.AbstractFacetRequest;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* @author Petar Tahchiev
@@ -47,7 +46,7 @@ public class StatisticalFacetRequest extends AbstractFacetRequest {
public AbstractAggregationBuilder getFacet() {
Assert.notNull(getName(), "Facet name can't be a null !!!");
Assert.isTrue(StringUtils.isNotBlank(field) && fields == null, "Please select field or fields on which to build the facets !!!");
Assert.isTrue(!StringUtils.isEmpty(field) && fields == null, "Please select field or fields on which to build the facets !!!");
return AggregationBuilders.extendedStats(getName()).field(field);
}
}
@@ -16,22 +16,22 @@
package org.springframework.data.elasticsearch.core.facet.request;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.util.automaton.RegExp;
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.IncludeExclude;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.terms.support.IncludeExclude;
import org.springframework.data.elasticsearch.core.facet.AbstractFacetRequest;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Term facet
*
* @author Artur Konczak
* @author Ilkang Na
*/
@Deprecated
public class TermFacetRequest extends AbstractFacetRequest {
@@ -48,8 +48,8 @@ public class TermFacetRequest extends AbstractFacetRequest {
}
public void setFields(String... fields) {
Assert.isTrue(ArrayUtils.isNotEmpty(fields), "Term agg need one field only");
Assert.isTrue(ArrayUtils.getLength(fields) == 1, "Term agg need one field only");
Assert.isTrue(!ObjectUtils.isEmpty(fields), "Term agg need one field only");
Assert.isTrue(fields.length == 1, "Term agg need one field only");
this.fields = fields;
}
@@ -81,29 +81,29 @@ public class TermFacetRequest extends AbstractFacetRequest {
switch (order) {
case descTerm:
termsBuilder.order(Terms.Order.term(false));
termsBuilder.order(BucketOrder.key(false));
break;
case ascTerm:
termsBuilder.order(Terms.Order.term(true));
termsBuilder.order(BucketOrder.key(true));
break;
case descCount:
termsBuilder.order(Terms.Order.count(false));
termsBuilder.order(BucketOrder.count(false));
break;
default:
termsBuilder.order(Terms.Order.count(true));
termsBuilder.order(BucketOrder.count(true));
}
if (ArrayUtils.isNotEmpty(excludeTerms)) {
termsBuilder.includeExclude(new IncludeExclude(null,excludeTerms));
if (!ObjectUtils.isEmpty(excludeTerms)) {
termsBuilder.includeExclude(new IncludeExclude(null, excludeTerms));
}
if (allTerms) {
termsBuilder.size(Integer.MAX_VALUE);
}
if (StringUtils.isNotBlank(regex)) {
termsBuilder.includeExclude(new IncludeExclude(new RegExp(regex),null));
if (!StringUtils.isEmpty(regex)) {
termsBuilder.includeExclude(new IncludeExclude(new RegExp(regex), null));
}
return termsBuilder;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2018 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.
@@ -16,6 +16,7 @@
package org.springframework.data.elasticsearch.core.mapping;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.lang.Nullable;
/**
* ElasticsearchPersistentEntity
@@ -23,6 +24,8 @@ import org.springframework.data.mapping.PersistentEntity;
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Mark Paluch
* @author Sascha Woo
* @author Oliver Gierke
*/
public interface ElasticsearchPersistentEntity<T> extends PersistentEntity<T, ElasticsearchPersistentProperty> {
@@ -49,4 +52,24 @@ public interface ElasticsearchPersistentEntity<T> extends PersistentEntity<T, El
String settingPath();
boolean isCreateIndexAndMapping();
/**
* Returns whether the {@link ElasticsearchPersistentEntity} has an score property. If this call returns
* {@literal true}, {@link #getScoreProperty()} will return a non-{@literal null} value.
*
* @return false when {@link ElasticsearchPersistentEntity} does not define a score property.
* @since 3.1
*/
boolean hasScoreProperty();
/**
* Returns the score property of the {@link ElasticsearchPersistentEntity}. Can be {@literal null} in case no score
* property is available on the entity.
*
* @return the score {@link ElasticsearchPersistentProperty} of the {@link PersistentEntity} or {@literal null} if not
* defined.
* @since 3.1
*/
@Nullable
ElasticsearchPersistentProperty getScoreProperty();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2018 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.
@@ -23,12 +23,25 @@ import org.springframework.data.mapping.PersistentProperty;
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Sascha Woo
* @author Oliver Gierke
*/
public interface ElasticsearchPersistentProperty extends PersistentProperty<ElasticsearchPersistentProperty> {
String getFieldName();
/**
* Returns whether the current property is a <em>potential</em> score property of the owning
* {@link ElasticsearchPersistentEntity}. This method is mainly used by {@link ElasticsearchPersistentEntity}
* implementation to discover score property candidates on {@link ElasticsearchPersistentEntity} creation you should
* rather call {@link ElasticsearchPersistentEntity#isScoreProperty(PersistentProperty)} to determine whether the
* current property is the version property of that {@link ElasticsearchPersistentEntity} under consideration.
*
* @return
* @since 3.1
*/
boolean isScoreProperty();
public enum PropertyToFieldNameConverter implements Converter<ElasticsearchPersistentProperty, String> {
INSTANCE;
@@ -18,7 +18,6 @@ package org.springframework.data.elasticsearch.core.mapping;
import static org.springframework.util.StringUtils.*;
import java.util.Locale;
import java.util.Optional;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
@@ -28,12 +27,16 @@ import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Parent;
import org.springframework.data.elasticsearch.annotations.Setting;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.model.BasicPersistentEntity;
import org.springframework.data.mapping.model.PersistentPropertyAccessorFactory;
import org.springframework.data.util.TypeInformation;
import org.springframework.expression.Expression;
import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -43,6 +46,7 @@ import org.springframework.util.Assert;
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Mark Paluch
* @author Sascha Woo
*/
public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntity<T, ElasticsearchPersistentProperty>
implements ElasticsearchPersistentEntity<T>, ApplicationContextAware {
@@ -59,6 +63,7 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
private String indexStoreType;
private String parentType;
private ElasticsearchPersistentProperty parentIdProperty;
private ElasticsearchPersistentProperty scoreProperty;
private String settingPath;
private boolean createIndexAndMapping;
@@ -150,6 +155,17 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
return createIndexAndMapping;
}
@Override
public boolean hasScoreProperty() {
return scoreProperty != null;
}
@Nullable
@Override
public ElasticsearchPersistentProperty getScoreProperty() {
return scoreProperty;
}
@Override
public void addPersistentProperty(ElasticsearchPersistentProperty property) {
super.addPersistentProperty(property);
@@ -165,7 +181,32 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
}
if (property.isVersionProperty()) {
Assert.isTrue(property.getType() == Long.class, "Version property should be Long");
Assert.isTrue(property.getType() == Long.class, "Version property must be of type Long!");
}
if (property.isScoreProperty()) {
ElasticsearchPersistentProperty scoreProperty = this.scoreProperty;
if (scoreProperty != null) {
throw new MappingException(
String.format("Attempt to add score property %s but already have property %s registered "
+ "as version. Check your mapping configuration!", property.getField(), scoreProperty.getField()));
}
this.scoreProperty = property;
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.BasicPersistentEntity#setPersistentPropertyAccessorFactory(org.springframework.data.mapping.model.PersistentPropertyAccessorFactory)
*/
@Override
public void setPersistentPropertyAccessorFactory(PersistentPropertyAccessorFactory factory) {
// Do nothing to avoid the usage of ClassGeneratingPropertyAccessorFactory for now
// DATACMNS-1322 switches to proper immutability behavior which Spring Data Elasticsearch
// cannot yet implement
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2018 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.
@@ -15,10 +15,13 @@
*/
package org.springframework.data.elasticsearch.core.mapping;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.springframework.data.elasticsearch.annotations.Score;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
import org.springframework.data.mapping.model.Property;
@@ -30,12 +33,16 @@ import org.springframework.data.mapping.model.SimpleTypeHolder;
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Mark Paluch
* @author Sascha Woo
* @author Oliver Gierke
*/
public class SimpleElasticsearchPersistentProperty extends
AnnotationBasedPersistentProperty<ElasticsearchPersistentProperty> implements ElasticsearchPersistentProperty {
private static final Set<Class<?>> SUPPORTED_ID_TYPES = new HashSet<>();
private static final Set<String> SUPPORTED_ID_PROPERTY_NAMES = new HashSet<>();
private final boolean isScore;
static {
SUPPORTED_ID_TYPES.add(String.class);
@@ -45,7 +52,14 @@ public class SimpleElasticsearchPersistentProperty extends
public SimpleElasticsearchPersistentProperty(Property property,
PersistentEntity<?, ElasticsearchPersistentProperty> owner, SimpleTypeHolder simpleTypeHolder) {
super(property, owner, simpleTypeHolder);
this.isScore = isAnnotationPresent(Score.class);
if (isScore && !Arrays.asList(Float.TYPE, Float.class).contains(getType())) {
throw new MappingException(String.format("Score property %s must be either of type float or Float!", property.getName()));
}
}
@Override
@@ -62,4 +76,22 @@ public class SimpleElasticsearchPersistentProperty extends
protected Association<ElasticsearchPersistentProperty> createAssociation() {
return null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty#isScoreProperty()
*/
@Override
public boolean isScoreProperty() {
return isScore;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.AbstractPersistentProperty#isImmutable()
*/
@Override
public boolean isImmutable() {
return false;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2018 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.
@@ -15,11 +15,14 @@
*/
package org.springframework.data.elasticsearch.core.query;
import static java.util.Collections.addAll;
import static java.util.Collections.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.support.IndicesOptions;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.util.Assert;
@@ -30,6 +33,8 @@ import org.springframework.util.Assert;
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Mark Paluch
* @author Alen Turkovic
* @author Sascha Woo
*/
abstract class AbstractQuery implements Query {
@@ -43,6 +48,8 @@ abstract class AbstractQuery implements Query {
protected Collection<String> ids;
protected String route;
protected SearchType searchType = SearchType.DFS_QUERY_THEN_FETCH;
protected IndicesOptions indicesOptions;
protected boolean trackScores;
@Override
public Sort getSort() {
@@ -149,4 +156,31 @@ abstract class AbstractQuery implements Query {
public SearchType getSearchType() {
return searchType;
}
public IndicesOptions getIndicesOptions() {
return indicesOptions;
}
public void setIndicesOptions(IndicesOptions indicesOptions) {
this.indicesOptions = indicesOptions;
}
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.core.query.Query#getTrackScores()
*/
@Override
public boolean getTrackScores() {
return trackScores;
}
/**
* Configures whether to track scores.
*
* @param trackScores
* @since 3.1
*/
public void setTrackScores(boolean trackScores) {
this.trackScores = trackScores;
}
}
@@ -17,7 +17,8 @@ package org.springframework.data.elasticsearch.core.query;
import java.util.*;
import org.apache.commons.lang.StringUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.elasticsearch.core.geo.GeoBox;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
@@ -42,8 +43,8 @@ public class Criteria {
"field=" + field.getName() +
", boost=" + boost +
", negating=" + negating +
", queryCriteria=" + StringUtils.join(queryCriteria, '|') +
", filterCriteria=" + StringUtils.join(filterCriteria, '|') +
", queryCriteria=" + ObjectUtils.nullSafeToString(queryCriteria) +
", filterCriteria=" + ObjectUtils.nullSafeToString(filterCriteria) +
'}';
}
@@ -437,7 +438,7 @@ public class Criteria {
* @return
*/
public Criteria within(String geoLocation, String distance) {
Assert.isTrue(StringUtils.isNotBlank(geoLocation), "geoLocation value must not be null");
Assert.isTrue(!StringUtils.isEmpty(geoLocation), "geoLocation value must not be null");
filterCriteria.add(new CriteriaEntry(OperationKey.WITHIN, new Object[]{geoLocation, distance}));
return this;
}
@@ -474,8 +475,8 @@ public class Criteria {
* @return Criteria the chaind criteria with the new 'boundedBy' criteria included.
*/
public Criteria boundedBy(String topLeftGeohash, String bottomRightGeohash) {
Assert.isTrue(StringUtils.isNotBlank(topLeftGeohash), "topLeftGeohash must not be empty");
Assert.isTrue(StringUtils.isNotBlank(bottomRightGeohash), "bottomRightGeohash must not be empty");
Assert.isTrue(!StringUtils.isEmpty(topLeftGeohash), "topLeftGeohash must not be empty");
Assert.isTrue(!StringUtils.isEmpty(bottomRightGeohash), "bottomRightGeohash must not be empty");
filterCriteria.add(new CriteriaEntry(OperationKey.BBOX, new Object[]{topLeftGeohash, bottomRightGeohash}));
return this;
}
@@ -502,7 +503,7 @@ public class Criteria {
}
private void assertNoBlankInWildcardedQuery(String searchString, boolean leadingWildcard, boolean trailingWildcard) {
if (StringUtils.contains(searchString, CRITERIA_VALUE_SEPERATOR)) {
if (searchString != null && searchString.contains(CRITERIA_VALUE_SEPERATOR)) {
throw new InvalidDataAccessApiUsageException("Cannot constructQuery '" + (leadingWildcard ? "*" : "") + "\""
+ searchString + "\"" + (trailingWildcard ? "*" : "") + "'. Use expression or multiple clauses instead.");
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2018 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.
@@ -32,6 +32,7 @@ import java.util.Arrays;
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
* @author Jean-Baptiste Nizet
*/
public class NativeSearchQuery extends AbstractQuery implements SearchQuery {
@@ -41,6 +42,7 @@ public class NativeSearchQuery extends AbstractQuery implements SearchQuery {
private final List<ScriptField> scriptFields = new ArrayList<>();
private List<FacetRequest> facets;
private List<AbstractAggregationBuilder> aggregations;
private HighlightBuilder highlightBuilder;
private HighlightBuilder.Field[] highlightFields;
private List<IndexBoost> indicesBoost;
@@ -67,6 +69,15 @@ public class NativeSearchQuery extends AbstractQuery implements SearchQuery {
this.highlightFields = highlightFields;
}
public NativeSearchQuery(QueryBuilder query, QueryBuilder filter, List<SortBuilder> sorts,
HighlightBuilder highlighBuilder, HighlightBuilder.Field[] highlightFields) {
this.query = query;
this.filter = filter;
this.sorts = sorts;
this.highlightBuilder = highlighBuilder;
this.highlightFields = highlightFields;
}
public QueryBuilder getQuery() {
return query;
}
@@ -79,6 +90,11 @@ public class NativeSearchQuery extends AbstractQuery implements SearchQuery {
return sorts;
}
@Override
public HighlightBuilder getHighlightBuilder() {
return highlightBuilder;
}
@Override
public HighlightBuilder.Field[] getHighlightFields() {
return highlightFields;
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2018 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.
@@ -20,6 +20,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
@@ -34,15 +35,19 @@ import org.springframework.data.elasticsearch.core.facet.FacetRequest;
* @author Mohsin Husen
* @author Artur Konczak
* @author Mark Paluch
* @author Alen Turkovic
* @author Sascha Woo
* @author Jean-Baptiste Nizet
*/
public class NativeSearchQueryBuilder {
private QueryBuilder queryBuilder;
private QueryBuilder filterBuilder;
private List<ScriptField> scriptFields = new ArrayList<>();
private List<ScriptField> scriptFields = new ArrayList<>();
private List<SortBuilder> sortBuilders = new ArrayList<>();
private List<FacetRequest> facetRequests = new ArrayList<>();
private List<AbstractAggregationBuilder> aggregationBuilders = new ArrayList<>();
private HighlightBuilder highlightBuilder;
private HighlightBuilder.Field[] highlightFields;
private Pageable pageable = Pageable.unpaged();
private String[] indices;
@@ -51,9 +56,11 @@ public class NativeSearchQueryBuilder {
private SourceFilter sourceFilter;
private List<IndexBoost> indicesBoost;
private float minScore;
private boolean trackScores;
private Collection<String> ids;
private String route;
private SearchType searchType;
private IndicesOptions indicesOptions;
public NativeSearchQueryBuilder withQuery(QueryBuilder queryBuilder) {
this.queryBuilder = queryBuilder;
@@ -70,10 +77,10 @@ public class NativeSearchQueryBuilder {
return this;
}
public NativeSearchQueryBuilder withScriptField(ScriptField scriptField) {
this.scriptFields.add(scriptField);
return this;
}
public NativeSearchQueryBuilder withScriptField(ScriptField scriptField) {
this.scriptFields.add(scriptField);
return this;
}
public NativeSearchQueryBuilder addAggregation(AbstractAggregationBuilder aggregationBuilder) {
this.aggregationBuilders.add(aggregationBuilder);
@@ -85,6 +92,11 @@ public class NativeSearchQueryBuilder {
return this;
}
public NativeSearchQueryBuilder withHighlightBuilder(HighlightBuilder highlightBuilder) {
this.highlightBuilder = highlightBuilder;
return this;
}
public NativeSearchQueryBuilder withHighlightFields(HighlightBuilder.Field... highlightFields) {
this.highlightFields = highlightFields;
return this;
@@ -116,8 +128,8 @@ public class NativeSearchQueryBuilder {
}
public NativeSearchQueryBuilder withSourceFilter(SourceFilter sourceFilter) {
this.sourceFilter = sourceFilter;
return this;
this.sourceFilter = sourceFilter;
return this;
}
public NativeSearchQueryBuilder withMinScore(float minScore) {
@@ -125,6 +137,16 @@ public class NativeSearchQueryBuilder {
return this;
}
/**
* @param trackScores whether to track scores.
* @return
* @since 3.1
*/
public NativeSearchQueryBuilder withTrackScores(boolean trackScores) {
this.trackScores = trackScores;
return this;
}
public NativeSearchQueryBuilder withIds(Collection<String> ids) {
this.ids = ids;
return this;
@@ -140,9 +162,17 @@ public class NativeSearchQueryBuilder {
return this;
}
public NativeSearchQueryBuilder withIndicesOptions(IndicesOptions indicesOptions) {
this.indicesOptions = indicesOptions;
return this;
}
public NativeSearchQuery build() {
NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(queryBuilder, filterBuilder, sortBuilders, highlightFields);
NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(queryBuilder, filterBuilder, sortBuilders,
highlightBuilder, highlightFields);
nativeSearchQuery.setPageable(pageable);
nativeSearchQuery.setTrackScores(trackScores);
if (indices != null) {
nativeSearchQuery.addIndices(indices);
@@ -160,13 +190,13 @@ public class NativeSearchQueryBuilder {
nativeSearchQuery.addSourceFilter(sourceFilter);
}
if(indicesBoost != null) {
nativeSearchQuery.setIndicesBoost(indicesBoost);
if (indicesBoost != null) {
nativeSearchQuery.setIndicesBoost(indicesBoost);
}
if (!isEmpty(scriptFields)) {
nativeSearchQuery.setScriptFields(scriptFields);
}
if (!isEmpty(scriptFields)) {
nativeSearchQuery.setScriptFields(scriptFields);
}
if (!isEmpty(facetRequests)) {
nativeSearchQuery.setFacets(facetRequests);
@@ -192,6 +222,10 @@ public class NativeSearchQueryBuilder {
nativeSearchQuery.setSearchType(searchType);
}
if (indicesOptions != null) {
nativeSearchQuery.setIndicesOptions(indicesOptions);
}
return nativeSearchQuery;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2018 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.
@@ -18,6 +18,7 @@ package org.springframework.data.elasticsearch.core.query;
import java.util.Collection;
import java.util.List;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.support.IndicesOptions;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
@@ -28,6 +29,8 @@ import org.springframework.data.domain.Sort;
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Mark Paluch
* @author Alen Turkovic
* @author Sascha Woo
*/
public interface Query {
@@ -112,8 +115,7 @@ public interface Query {
void addSourceFilter(SourceFilter sourceFilter);
/**
* Get SourceFilter to be returned to get include and exclude source
* fields as part of search request.
* Get SourceFilter to be returned to get include and exclude source fields as part of search request.
*
* @return SourceFilter
*/
@@ -126,6 +128,14 @@ public interface Query {
*/
float getMinScore();
/**
* Get if scores will be computed and tracked, regardless of whether sorting on a field. Defaults to <tt>false</tt>.
*
* @return
* @since 3.1
*/
boolean getTrackScores();
/**
* Get Ids
*
@@ -140,11 +150,17 @@ public interface Query {
*/
String getRoute();
/**
* Type of search
*
* @return
*/
SearchType getSearchType();
/**
* Get indices options
*
* @return null if not set
*/
IndicesOptions getIndicesOptions();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2016 the original author or authors.
* Copyright 2013-2018 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.
@@ -29,6 +29,7 @@ import org.springframework.data.elasticsearch.core.facet.FacetRequest;
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
* @author Jean-Baptiste Nizet
*/
public interface SearchQuery extends Query {
@@ -43,6 +44,8 @@ public interface SearchQuery extends Query {
List<AbstractAggregationBuilder> getAggregations();
HighlightBuilder getHighlightBuilder();
HighlightBuilder.Field[] getHighlightFields();
List<IndexBoost> getIndicesBoost();
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2018 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.
@@ -60,24 +60,21 @@ public class ElasticsearchRepositoryBean<T> extends CdiRepositoryBean<T> {
this.elasticsearchOperationsBean = operations;
}
/* (non-Javadoc)
* @see org.springframework.data.repository.cdi.CdiRepositoryBean#create(javax.enterprise.context.spi.CreationalContext, java.lang.Class, java.util.Optional)
/*
* (non-Javadoc)
* @see org.springframework.data.repository.cdi.CdiRepositoryBean#create(javax.enterprise.context.spi.CreationalContext, java.lang.Class)
*/
@Override
protected T create(CreationalContext<T> creationalContext, Class<T> repositoryType,
Optional<Object> customImplementation) {
protected T create(CreationalContext<T> creationalContext, Class<T> repositoryType) {
ElasticsearchOperations elasticsearchOperations = getDependencyInstance(elasticsearchOperationsBean,
ElasticsearchOperations operations = getDependencyInstance(elasticsearchOperationsBean,
ElasticsearchOperations.class);
ElasticsearchRepositoryFactory factory = new ElasticsearchRepositoryFactory(elasticsearchOperations);
return customImplementation //
.map(o -> factory.getRepository(repositoryType, o)) //
.orElseGet(() -> factory.getRepository(repositoryType));
return create(() -> new ElasticsearchRepositoryFactory(operations), repositoryType);
}
/* (non-Javadoc)
/*
* (non-Javadoc)
* @see org.springframework.data.repository.cdi.CdiRepositoryBean#getScope()
*/
@Override
@@ -28,8 +28,8 @@ import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.geo.Box;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.context.PersistentPropertyPath;
import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
import org.springframework.data.repository.query.parser.Part;
@@ -55,6 +55,7 @@ import org.springframework.util.Assert;
* @author Kevin Leturc
* @author Mark Paluch
* @author Christoph Strobl
* @author Michael Wirth
*/
public abstract class AbstractElasticsearchRepository<T, ID extends Serializable>
implements ElasticsearchRepository<T, ID> {
@@ -188,7 +189,7 @@ public abstract class AbstractElasticsearchRepository<T, ID extends Serializable
@Override
public boolean existsById(ID id) {
return findById(id) != null;
return findById(id).isPresent();
}
@Override
@@ -32,10 +32,11 @@ import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.data.repository.query.EvaluationContextProvider;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.spel.EvaluationContextProvider;
import org.springframework.util.Assert;
/**
@@ -97,7 +98,7 @@ public class ElasticsearchRepositoryFactory extends RepositoryFactorySupport {
@Override
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(Key key,
EvaluationContextProvider evaluationContextProvider) {
QueryMethodEvaluationContextProvider evaluationContextProvider) {
return Optional.of(new ElasticsearchQueryLookupStrategy());
}
+125
View File
@@ -1,6 +1,131 @@
Spring Data Elasticsearch Changelog
===================================
Changes in version 3.1.1.RELEASE (2018-10-15)
---------------------------------------------
* DATAES-486 - Release 3.1.1 (Lovelace SR1).
Changes in version 3.1.0.RELEASE (2018-09-21)
---------------------------------------------
* DATAES-480 - Release 3.1 GA (Lovelace).
* DATAES-479 - NativeSearchQueryBuilder should have a withHighlighter() method.
Changes in version 3.0.10.RELEASE (2018-09-10)
----------------------------------------------
* DATAES-473 - Release 3.0.10 (Kay SR10).
Changes in version 2.1.15.RELEASE (2018-09-10)
----------------------------------------------
* DATAES-474 - Release 2.1.15 (Ingalls SR15).
Changes in version 3.1.0.RC2 (2018-08-20)
-----------------------------------------
* DATAES-472 - Release 3.1 RC2 (Lovelace).
Changes in version 2.1.14.RELEASE (2018-07-27)
----------------------------------------------
* DATAES-463 - Release 2.1.14 (Ingalls SR14).
* DATAES-283 - Remove dependency to Commons Lang.
Changes in version 3.0.9.RELEASE (2018-07-26)
---------------------------------------------
* DATAES-470 - TransportClientFactoryBean does not parse clusterNodes correctly.
* DATAES-469 - Is org.elasticsearch:elasticsearch a necessary dependency?.
* DATAES-467 - No mapping found for [_score] in order to sort on.
* DATAES-465 - Release 3.0.9 (Kay SR9).
* DATAES-317 - Add query logging to ElasticsearchTemplate.
* DATAES-283 - Remove dependency to Commons Lang.
Changes in version 3.1.0.RC1 (2018-07-26)
-----------------------------------------
* DATAES-471 - Adapt to changes in immutable entities support.
* DATAES-470 - TransportClientFactoryBean does not parse clusterNodes correctly.
* DATAES-469 - Is org.elasticsearch:elasticsearch a necessary dependency?.
* DATAES-467 - No mapping found for [_score] in order to sort on.
* DATAES-464 - Add support for @ReadOnlyProperty to prevent properties from being written.
* DATAES-462 - Add support for mapping document scores to entities.
* DATAES-452 - Release 3.1 RC1 (Lovelace).
* DATAES-317 - Add query logging to ElasticsearchTemplate.
Changes in version 3.0.8.RELEASE (2018-06-13)
---------------------------------------------
* DATAES-460 - Avoid pulling in Netty 3 as dependency.
* DATAES-448 - Release 3.0.8 (Kay SR8).
* DATAES-420 - Analyzer of main field ignored when using @MultiField annotation.
* DATAES-312 - NullHandling.NULLS_LAST not working in query.sort.
Changes in version 2.1.13.RELEASE (2018-06-13)
----------------------------------------------
* DATAES-447 - Release 2.1.13 (Ingalls SR13).
Changes in version 3.1.0.M3 (2018-05-17)
----------------------------------------
* DATAES-451 - Adapt to SpEL extension API changes in Spring Data Commons.
* DATAES-440 - Release 3.1 M3 (Lovelace).
* DATAES-422 - Add support for IndicesOptions in search queries.
* DATAES-420 - Analyzer of main field ignored when using @MultiField annotation.
* DATAES-412 - Highlighted fields is not getting passed to the elastic search query.
* DATAES-363 - AbstractElasticsearchRepository.existsById(..) always returns true.
* DATAES-312 - NullHandling.NULLS_LAST not working in query.sort.
* DATAES-198 - @Version has no effect, so therefore is not useful with spring data elasticsearch.
Changes in version 3.0.7.RELEASE (2018-05-08)
---------------------------------------------
* DATAES-438 - Expected a boolean [true/false] for property [index] but got [not_analyzed].
* DATAES-437 - Release 3.0.7 (Kay SR7).
* DATAES-412 - Highlighted fields is not getting passed to the elastic search query.
* DATAES-402 - Paging not working correctly.
* DATAES-363 - AbstractElasticsearchRepository.existsById(..) always returns true.
* DATAES-198 - @Version has no effect, so therefore is not useful with spring data elasticsearch.
Changes in version 2.1.12.RELEASE (2018-05-08)
----------------------------------------------
* DATAES-436 - Release 2.1.12 (Ingalls SR12).
Changes in version 3.1.0.M2 (2018-04-13)
----------------------------------------
* DATAES-439 - Adapt to API changes in Spring Data Commons.
* DATAES-434 - Remove explicit declaration of Jackson library versions.
* DATAES-432 - Export composable repositories via CDI.
* DATAES-427 - Release 3.1 M2 (Lovelace).
Changes in version 3.0.6.RELEASE (2018-04-04)
---------------------------------------------
* DATAES-434 - Remove explicit declaration of Jackson library versions.
* DATAES-430 - Release 3.0.6 (Kay SR6).
Changes in version 2.1.11.RELEASE (2018-04-04)
----------------------------------------------
* DATAES-434 - Remove explicit declaration of Jackson library versions.
* DATAES-424 - Fix line endings.
* DATAES-423 - Release 2.1.11 (Ingalls SR11).
Changes in version 3.0.5.RELEASE (2018-02-28)
---------------------------------------------
* DATAES-429 - Release 3.0.5 (Kay SR5).
Changes in version 3.0.4.RELEASE (2018-02-19)
---------------------------------------------
* DATAES-425 - Release 3.0.4 (Kay SR4).
Changes in version 3.1.0.M1 (2018-02-06)
----------------------------------------
* DATAES-424 - Fix line endings.
+1 -1
View File
@@ -1,4 +1,4 @@
Spring Data Elasticsearch 3.1 M1
Spring Data Elasticsearch 3.1.1
Copyright (c) [2013-2016] Pivotal Software, Inc.
This product is licensed to you under the Apache License, Version 2.0 (the "License").
@@ -0,0 +1,62 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.plugins;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collections;
import java.util.List;
/**
* A classloader that is a union over the parent core classloader and classloaders of extended plugins.
* Cloned from ES repository
* - that file is only available in ES server libs
* - and we need it o create a node client for unittests
*/
public class ExtendedPluginsClassLoader extends ClassLoader {
/** Loaders of plugins extended by a plugin. */
private final List<ClassLoader> extendedLoaders;
private ExtendedPluginsClassLoader(ClassLoader parent, List<ClassLoader> extendedLoaders) {
super(parent);
this.extendedLoaders = Collections.unmodifiableList(extendedLoaders);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
for (ClassLoader loader : extendedLoaders) {
try {
return loader.loadClass(name);
} catch (ClassNotFoundException e) {
// continue
}
}
throw new ClassNotFoundException(name);
}
/**
* Return a new classloader across the parent and extended loaders.
*/
public static ExtendedPluginsClassLoader create(ClassLoader parent, List<ClassLoader> extendedLoaders) {
return AccessController.doPrivileged((PrivilegedAction<ExtendedPluginsClassLoader>)
() -> new ExtendedPluginsClassLoader(parent, extendedLoaders));
}
}
@@ -26,6 +26,7 @@ import static java.util.Arrays.*;
/**
* @author Mohsin Husen
* @author Artur Konczak
* @author Ilkang Na
*/
public class Utils {
@@ -38,13 +39,11 @@ public class Utils {
return new NodeClientFactoryBean.TestNode(
Settings.builder()
.put("transport.type", "netty4")
.put("transport.type", "local")
.put("http.type", "netty4")
.put("path.home", pathHome)
.put("path.data", pathData)
.put("cluster.name", clusterName)
.put("node.max_local_storage_nodes", 100)
.put("script.inline", "true")
.build(), asList(Netty4Plugin.class)).start().client();
}
}
@@ -0,0 +1,90 @@
/*
* Copyright 2018 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.client;
import static org.assertj.core.api.Assertions.*;
import java.util.List;
import org.elasticsearch.common.transport.TransportAddress;
import org.junit.Test;
/**
* Unit tests for {@link ClusterNodes}.
*
* @author Oliver Gierke
*/
public class ClusterNodesUnitTests {
@Test // DATAES-470
public void parsesSingleClusterNode() {
ClusterNodes nodes = ClusterNodes.DEFAULT;
assertThat(nodes).hasSize(1) //
.first().satisfies(it -> {
assertThat(it.getAddress()).isEqualTo("127.0.0.1");
assertThat(it.getPort()).isEqualTo(9300);
});
}
@Test // DATAES-470
public void parsesMultiClusterNode() {
ClusterNodes nodes = ClusterNodes.of("127.0.0.1:1234,10.1.0.1:5678");
assertThat(nodes.stream()).hasSize(2); //
assertThat(nodes.stream()).element(0).satisfies(it -> {
assertThat(it.getAddress()).isEqualTo("127.0.0.1");
assertThat(it.getPort()).isEqualTo(1234);
});
assertThat(nodes.stream()).element(1).satisfies(it -> {
assertThat(it.getAddress()).isEqualTo("10.1.0.1");
assertThat(it.getPort()).isEqualTo(5678);
});
}
@Test // DATAES-470
public void rejectsEmptyHostName() {
assertThatExceptionOfType(IllegalArgumentException.class) //
.isThrownBy(() -> ClusterNodes.of(":8080")) //
.withMessageContaining("host");
}
@Test // DATAES-470
public void rejectsEmptyPort() {
assertThatExceptionOfType(IllegalArgumentException.class) //
.isThrownBy(() -> ClusterNodes.of("localhost:")) //
.withMessageContaining("port");
}
@Test // DATAES-470
public void rejectsMissingPort() {
assertThatExceptionOfType(IllegalArgumentException.class) //
.isThrownBy(() -> ClusterNodes.of("localhost")) //
.withMessageContaining("host:port");
}
@Test // DATAES-470
public void rejectsUnresolvableHost() {
assertThatExceptionOfType(IllegalArgumentException.class) //
.isThrownBy(() -> ClusterNodes.of("mylocalhost:80"));
}
}
@@ -41,6 +41,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Mohsin Husen
* @author Ilkang Na
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:elasticsearch-template-test.xml")
@@ -101,7 +102,7 @@ public class AliasTests {
// then
elasticsearchTemplate.removeAlias(aliasQuery);
aliases = elasticsearchTemplate.queryForAlias(indexName);
assertThat(aliases, is(nullValue()));
assertThat(aliases, anyOf(is(nullValue()), hasSize(0)));
}
/*
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2014 the original author or authors.
* Copyright 2013-2018 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.
@@ -15,15 +15,17 @@
*/
package org.springframework.data.elasticsearch.core;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.assertj.core.api.Assertions.*;
import java.io.IOException;
import java.util.Locale;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.annotation.ReadOnlyProperty;
import org.springframework.data.annotation.Transient;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
import org.springframework.data.elasticsearch.entities.Car;
import org.springframework.data.elasticsearch.entities.GeoEntity;
import org.springframework.data.geo.Point;
@@ -31,6 +33,7 @@ import org.springframework.data.geo.Point;
/**
* @author Artur Konczak
* @author Mohsin Husen
* @author Oliver Gierke
*/
public class DefaultEntityMapperTests {
@@ -41,7 +44,7 @@ public class DefaultEntityMapperTests {
@Before
public void init() {
entityMapper = new DefaultEntityMapper();
entityMapper = new DefaultEntityMapper(new SimpleElasticsearchMappingContext());
}
@Test
@@ -52,7 +55,7 @@ public class DefaultEntityMapperTests {
String jsonResult = entityMapper.mapToString(Car.builder().model(CAR_MODEL).name(CAR_NAME).build());
//Then
assertThat(jsonResult, is(JSON_STRING));
assertThat(jsonResult).isEqualTo(JSON_STRING);
}
@Test
@@ -63,15 +66,14 @@ public class DefaultEntityMapperTests {
Car result = entityMapper.mapToObject(JSON_STRING, Car.class);
//Then
assertThat(result.getName(), is(CAR_NAME));
assertThat(result.getModel(), is(CAR_MODEL));
assertThat(result.getName()).isEqualTo(CAR_NAME);
assertThat(result.getModel()).isEqualTo(CAR_MODEL);
}
@Test
public void shouldMapGeoPointElasticsearchNames() throws IOException {
//given
final Point point = new Point(10, 20);
final int radius = 10;
final String pointAsString = point.getX() + "," + point.getY();
final double[] pointAsArray = {point.getX(), point.getY()};
final GeoEntity geoEntity = GeoEntity.builder()
@@ -81,13 +83,43 @@ public class DefaultEntityMapperTests {
String jsonResult = entityMapper.mapToString(geoEntity);
//then
assertThat(jsonResult, containsString(pointTemplate("pointA", point)));
assertThat(jsonResult, containsString(pointTemplate("pointB", point)));
assertThat(jsonResult, containsString(String.format(Locale.ENGLISH, "\"%s\":\"%s\"", "pointC", pointAsString)));
assertThat(jsonResult, containsString(String.format(Locale.ENGLISH, "\"%s\":[%.1f,%.1f]", "pointD", pointAsArray[0], pointAsArray[1])));
assertThat(jsonResult).contains(pointTemplate("pointA", point));
assertThat(jsonResult).contains(pointTemplate("pointB", point));
assertThat(jsonResult).contains(String.format(Locale.ENGLISH, "\"%s\":\"%s\"", "pointC", pointAsString));
assertThat(jsonResult).contains(String.format(Locale.ENGLISH, "\"%s\":[%.1f,%.1f]", "pointD", pointAsArray[0], pointAsArray[1]));
}
@Test // DATAES-464
public void ignoresReadOnlyProperties() throws IOException {
// given
Sample sample = new Sample();
sample.readOnly = "readOnly";
sample.property = "property";
sample.transientProperty = "transient";
sample.annotatedTransientProperty = "transient";
// when
String result = entityMapper.mapToString(sample);
// then
assertThat(result).contains("\"property\"");
assertThat(result).doesNotContain("readOnly");
assertThat(result).doesNotContain("transientProperty");
assertThat(result).doesNotContain("annotatedTransientProperty");
}
private String pointTemplate(String name, Point point) {
return String.format(Locale.ENGLISH, "\"%s\":{\"lat\":%.1f,\"lon\":%.1f}", name, point.getX(), point.getY());
}
public static class Sample {
public @ReadOnlyProperty String readOnly;
public @Transient String annotatedTransientProperty;
public transient String transientProperty;
public String property;
}
}
@@ -16,17 +16,19 @@
package org.springframework.data.elasticsearch.core;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import com.fasterxml.jackson.databind.util.ArrayIterator;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.get.MultiGetItemResponse;
import org.elasticsearch.action.get.MultiGetResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.document.DocumentField;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHitField;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.Aggregations;
@@ -41,8 +43,10 @@ import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
import org.springframework.data.elasticsearch.entities.Car;
import org.springframework.data.elasticsearch.entities.SampleEntity;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
@@ -50,7 +54,9 @@ import static org.mockito.Mockito.*;
/**
* @author Artur Konczak
* @author Mohsin Husen
* @author Chris White
* @author Mark Paluch
* @author Ilkang Na
*/
public class DefaultResultMapperTests {
@@ -70,7 +76,7 @@ public class DefaultResultMapperTests {
//Given
SearchHit[] hits = {createCarHit("Ford", "Grat"), createCarHit("BMW", "Arrow")};
SearchHits searchHits = mock(SearchHits.class);
when(searchHits.totalHits()).thenReturn(2L);
when(searchHits.getTotalHits()).thenReturn(2L);
when(searchHits.iterator()).thenReturn(new ArrayIterator(hits));
when(response.getHits()).thenReturn(searchHits);
@@ -91,7 +97,7 @@ public class DefaultResultMapperTests {
//Given
SearchHit[] hits = {createCarHit("Ford", "Grat"), createCarHit("BMW", "Arrow")};
SearchHits searchHits = mock(SearchHits.class);
when(searchHits.totalHits()).thenReturn(2L);
when(searchHits.getTotalHits()).thenReturn(2L);
when(searchHits.iterator()).thenReturn(new ArrayIterator(hits));
when(response.getHits()).thenReturn(searchHits);
@@ -109,7 +115,7 @@ public class DefaultResultMapperTests {
//Given
SearchHit[] hits = {createCarPartialHit("Ford", "Grat"), createCarPartialHit("BMW", "Arrow")};
SearchHits searchHits = mock(SearchHits.class);
when(searchHits.totalHits()).thenReturn(2L);
when(searchHits.getTotalHits()).thenReturn(2L);
when(searchHits.iterator()).thenReturn(new ArrayIterator(hits));
when(response.getHits()).thenReturn(searchHits);
@@ -153,6 +159,69 @@ public class DefaultResultMapperTests {
assertThat(result.getId(), is("identifier"));
}
@Test // DATAES-198
public void setsVersionFromGetResponse() {
GetResponse response = mock(GetResponse.class);
when(response.getSourceAsString()).thenReturn("{}");
when(response.getVersion()).thenReturn(1234L);
SampleEntity result = resultMapper.mapResult(response, SampleEntity.class);
assertThat(result, is(notNullValue()));
assertThat(result.getVersion(), is(1234L));
}
@Test // DATAES-198
public void setsVersionFromMultiGetResponse() {
GetResponse response1 = mock(GetResponse.class);
when(response1.getSourceAsString()).thenReturn("{}");
when(response1.isExists()).thenReturn(true);
when(response1.getVersion()).thenReturn(1234L);
GetResponse response2 = mock(GetResponse.class);
when(response2.getSourceAsString()).thenReturn("{}");
when(response2.isExists()).thenReturn(true);
when(response2.getVersion()).thenReturn(5678L);
MultiGetResponse multiResponse = mock(MultiGetResponse.class);
when(multiResponse.getResponses()).thenReturn(new MultiGetItemResponse[] {
new MultiGetItemResponse(response1, null), new MultiGetItemResponse(response2, null) });
LinkedList<SampleEntity> results = resultMapper.mapResults(multiResponse, SampleEntity.class);
assertThat(results, is(notNullValue()));
assertThat(results, hasSize(2));
assertThat(results.get(0).getVersion(), is(1234L));
assertThat(results.get(1).getVersion(), is(5678L));
}
@Test // DATAES-198
public void setsVersionFromSearchResponse() {
SearchHit hit1 = mock(SearchHit.class);
when(hit1.getSourceAsString()).thenReturn("{}");
when(hit1.getVersion()).thenReturn(1234L);
SearchHit hit2 = mock(SearchHit.class);
when(hit2.getSourceAsString()).thenReturn("{}");
when(hit2.getVersion()).thenReturn(5678L);
SearchHits searchHits = mock(SearchHits.class);
when(searchHits.getTotalHits()).thenReturn(2L);
when(searchHits.iterator()).thenReturn(Arrays.asList(hit1, hit2).iterator());
SearchResponse searchResponse = mock(SearchResponse.class);
when(searchResponse.getHits()).thenReturn(searchHits);
AggregatedPage<SampleEntity> results = resultMapper.mapResults(searchResponse, SampleEntity.class,
mock(Pageable.class));
assertThat(results, is(notNullValue()));
assertThat(results.getContent().get(0).getVersion(), is(1234L));
assertThat(results.getContent().get(1).getVersion(), is(5678L));
}
private Aggregation createCarAggregation() {
Aggregation aggregation = mock(Terms.class);
when(aggregation.getName()).thenReturn("Diesel");
@@ -161,13 +230,13 @@ public class DefaultResultMapperTests {
private SearchHit createCarHit(String name, String model) {
SearchHit hit = mock(SearchHit.class);
when(hit.sourceAsString()).thenReturn(createJsonCar(name, model));
when(hit.getSourceAsString()).thenReturn(createJsonCar(name, model));
return hit;
}
private SearchHit createCarPartialHit(String name, String model) {
SearchHit hit = mock(SearchHit.class);
when(hit.sourceAsString()).thenReturn(null);
when(hit.getSourceAsString()).thenReturn(null);
when(hit.getFields()).thenReturn(createCarFields(name, model));
return hit;
}
@@ -180,10 +249,10 @@ public class DefaultResultMapperTests {
return sb.toString();
}
private Map<String, SearchHitField> createCarFields(String name, String model) {
Map<String, SearchHitField> result = new HashMap<>();
result.put("name", new SearchHitField("name", asList(name)));
result.put("model", new SearchHitField("model", asList(model)));
private Map<String, DocumentField> createCarFields(String name, String model) {
Map<String, DocumentField> result = new HashMap<>();
result.put("name", new DocumentField("name", asList(name)));
result.put("model", new DocumentField("model", asList(model)));
return result;
}
@@ -31,6 +31,7 @@ import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
@@ -45,6 +46,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Philipp Jardas
*/
@Ignore(value = "DATAES-421")
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:elasticsearch-template-test.xml")
public class ElasticsearchTemplateParentChildTests {
@@ -66,6 +68,7 @@ public class ElasticsearchTemplateParentChildTests {
elasticsearchTemplate.deleteIndex(ParentEntity.class);
}
@Ignore(value = "DATAES-421")
@Test
public void shouldIndexParentChildEntity() {
// index two parents
@@ -88,6 +91,7 @@ public class ElasticsearchTemplateParentChildTests {
assertThat("parents", parents, contains(hasProperty("id", is(parent1.getId()))));
}
@Ignore(value = "DATAES-421")
@Test
public void shouldUpdateChild() throws Exception {
// index parent and child
@@ -106,6 +110,7 @@ public class ElasticsearchTemplateParentChildTests {
assertThat(response.getShardInfo().getSuccessful(), is(1));
}
@Ignore(value = "DATAES-421")
@Test(expected = RoutingMissingException.class)
public void shouldFailWithRoutingMissingExceptionOnUpdateChildIfNotRoutingSetOnUpdateRequest() throws Exception {
// index parent and child
@@ -121,6 +126,7 @@ public class ElasticsearchTemplateParentChildTests {
update(updateRequest);
}
@Ignore(value = "DATAES-421")
@Test(expected = RoutingMissingException.class)
public void shouldFailWithRoutingMissingExceptionOnUpdateChildIfRoutingOnlySetOnRequestDoc() throws Exception {
// index parent and child
@@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 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.
@@ -28,12 +28,15 @@ import org.elasticsearch.action.get.MultiGetItemResponse;
import org.elasticsearch.action.get.MultiGetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.index.engine.DocumentMissingException;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.hamcrest.Matchers;
import org.junit.Before;
@@ -70,6 +73,11 @@ import static org.springframework.data.elasticsearch.utils.IndexBuilder.*;
* @author Abdul Mohammed
* @author Kevin Leturc
* @author Mason Chan
* @author Chris White
* @author Ilkang Na
* @author Alen Turkovic
* @author Sascha Woo
* @author Jean-Baptiste Nizet
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:elasticsearch-template-test.xml")
@@ -242,6 +250,33 @@ public class ElasticsearchTemplateTests {
assertThat(sampleEntities.getTotalElements(), greaterThanOrEqualTo(1L));
}
// DATAES-422 - Add support for IndicesOptions in search queries
@Test
public void shouldPassIndicesOptionsForGivenSearchQuery() {
// given
String documentId = randomNumeric(5);
SampleEntity sampleEntity = SampleEntity.builder().id(documentId).message("some message")
.version(System.currentTimeMillis()).build();
IndexQuery idxQuery = new IndexQueryBuilder().withIndexName(INDEX_1_NAME)
.withId(sampleEntity.getId())
.withObject(sampleEntity).build();
elasticsearchTemplate.index(idxQuery);
elasticsearchTemplate.refresh(INDEX_1_NAME);
// when
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(matchAllQuery())
.withIndices(INDEX_1_NAME, INDEX_2_NAME)
.withIndicesOptions(IndicesOptions.lenientExpandOpen())
.build();
Page<SampleEntity> entities = elasticsearchTemplate.queryForPage(searchQuery, SampleEntity.class);
// then
assertThat(entities, is(notNullValue()));
assertThat(entities.getTotalElements(), greaterThanOrEqualTo(1L));
}
@Test
public void shouldDoBulkIndex() {
// given
@@ -461,6 +496,129 @@ public class ElasticsearchTemplateTests {
assertThat(sampleEntities.getContent().get(1).getMessage(), is(sampleEntity1.getMessage()));
}
@Test // DATAES-312
public void shouldSortResultsGivenNullFirstSortCriteria() {
// given
List<IndexQuery> indexQueries;
// first document
String documentId = randomNumeric(5);
SampleEntity sampleEntity1 = SampleEntity.builder()
.id(documentId)
.message("abc")
.rate(15)
.version(System.currentTimeMillis())
.build();
// second document
String documentId2 = randomNumeric(5);
SampleEntity sampleEntity2 = SampleEntity.builder()
.id(documentId2)
.message("xyz")
.rate(5)
.version(System.currentTimeMillis())
.build();
// third document
String documentId3 = randomNumeric(5);
SampleEntity sampleEntity3 = SampleEntity.builder()
.id(documentId3)
.rate(10)
.version(System.currentTimeMillis())
.build();
indexQueries = getIndexQueries(Arrays.asList(sampleEntity1, sampleEntity2, sampleEntity3));
elasticsearchTemplate.bulkIndex(indexQueries);
elasticsearchTemplate.refresh(SampleEntity.class);
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(matchAllQuery())
.withPageable(PageRequest.of(0, 10, Sort.by(Sort.Order.asc("message").nullsFirst())))
.build();
// when
Page<SampleEntity> sampleEntities = elasticsearchTemplate.queryForPage(searchQuery, SampleEntity.class);
// then
assertThat(sampleEntities.getTotalElements(), equalTo(3L));
assertThat(sampleEntities.getContent().get(0).getRate(), is(sampleEntity3.getRate()));
assertThat(sampleEntities.getContent().get(1).getMessage(), is(sampleEntity1.getMessage()));
}
@Test // DATAES-312
public void shouldSortResultsGivenNullLastSortCriteria() {
// given
List<IndexQuery> indexQueries;
// first document
String documentId = randomNumeric(5);
SampleEntity sampleEntity1 = SampleEntity.builder()
.id(documentId)
.message("abc")
.rate(15)
.version(System.currentTimeMillis())
.build();
// second document
String documentId2 = randomNumeric(5);
SampleEntity sampleEntity2 = SampleEntity.builder()
.id(documentId2)
.message("xyz")
.rate(5)
.version(System.currentTimeMillis())
.build();
// third document
String documentId3 = randomNumeric(5);
SampleEntity sampleEntity3 = SampleEntity.builder()
.id(documentId3)
.rate(10)
.version(System.currentTimeMillis())
.build();
indexQueries = getIndexQueries(Arrays.asList(sampleEntity1, sampleEntity2, sampleEntity3));
elasticsearchTemplate.bulkIndex(indexQueries);
elasticsearchTemplate.refresh(SampleEntity.class);
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(matchAllQuery())
.withPageable(PageRequest.of(0, 10, Sort.by(Sort.Order.asc("message").nullsLast())))
.build();
// when
Page<SampleEntity> sampleEntities = elasticsearchTemplate.queryForPage(searchQuery, SampleEntity.class);
// then
assertThat(sampleEntities.getTotalElements(), equalTo(3L));
assertThat(sampleEntities.getContent().get(0).getRate(), is(sampleEntity1.getRate()));
assertThat(sampleEntities.getContent().get(1).getMessage(), is(sampleEntity2.getMessage()));
}
@Test // DATAES-467
public void shouldSortResultsByScore() {
// given
List<SampleEntity> entities = Arrays.asList( //
SampleEntity.builder().id("1").message("abc").build(), //
SampleEntity.builder().id("2").message("def").build(), //
SampleEntity.builder().id("3").message("ghi").build());
elasticsearchTemplate.bulkIndex(getIndexQueries(entities));
elasticsearchTemplate.refresh(SampleEntity.class);
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(matchAllQuery())
.withPageable(PageRequest.of(0, 10, Sort.by(Sort.Order.asc("_score"))))
.build();
// when
Page<SampleEntity> sampleEntities = elasticsearchTemplate.queryForPage(searchQuery, SampleEntity.class);
// then
assertThat(sampleEntities.getTotalElements(), equalTo(3L));
}
@Test
public void shouldExecuteStringQuery() {
// given
@@ -649,7 +807,7 @@ public class ElasticsearchTemplateTests {
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
List<String> values = new ArrayList<>();
for (SearchHit searchHit : response.getHits()) {
values.add((String) searchHit.getSource().get("message"));
values.add((String) searchHit.getSourceAsMap().get("message"));
}
return new AggregatedPageImpl<>((List<T>) values);
}
@@ -780,7 +938,7 @@ public class ElasticsearchTemplateTests {
if (response.getHits().getHits().length <= 0) {
return new AggregatedPageImpl<T>(Collections.EMPTY_LIST, response.getScrollId());
}
String message = (String) searchHit.getSource().get("message");
String message = (String) searchHit.getSourceAsMap().get("message");
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(searchHit.getId());
sampleEntity.setMessage(message);
@@ -1192,7 +1350,7 @@ public class ElasticsearchTemplateTests {
}
SampleEntity user = new SampleEntity();
user.setId(searchHit.getId());
user.setMessage((String) searchHit.getSource().get("message"));
user.setMessage((String) searchHit.getSourceAsMap().get("message"));
user.setHighlightedMessage(searchHit.getHighlightFields().get("message").fragments()[0].toString());
chunk.add(user);
}
@@ -1206,6 +1364,140 @@ public class ElasticsearchTemplateTests {
assertThat(sampleEntities.getContent().get(0).getHighlightedMessage(), is(highlightedMessage));
}
@Test // DATAES-412
public void shouldReturnMultipleHighlightFields() {
// given
String documentId = randomNumeric(5);
String actualType = "some test type";
String actualMessage = "some test message";
String highlightedType = "some <em>test</em> type";
String highlightedMessage = "some <em>test</em> message";
SampleEntity sampleEntity = SampleEntity.builder()
.id(documentId)
.type(actualType)
.message(actualMessage)
.version(System.currentTimeMillis())
.build();
IndexQuery indexQuery = getIndexQuery(sampleEntity);
elasticsearchTemplate.index(indexQuery);
elasticsearchTemplate.refresh(SampleEntity.class);
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(
boolQuery()
.must(termQuery("type", "test"))
.must(termQuery("message", "test")))
.withHighlightFields(
new HighlightBuilder.Field("type"),
new HighlightBuilder.Field("message"))
.build();
// when
elasticsearchTemplate.queryForPage(searchQuery, SampleEntity.class, new SearchResultMapper() {
@Override
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
for (SearchHit searchHit : response.getHits()) {
Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
HighlightField highlightFieldType = highlightFields.get("type");
HighlightField highlightFieldMessage = highlightFields.get("message");
// then
assertNotNull(highlightFieldType);
assertNotNull(highlightFieldMessage);
assertThat(highlightFieldType.fragments()[0].toString(), is(highlightedType));
assertThat(highlightFieldMessage.fragments()[0].toString(), is(highlightedMessage));
}
return null;
}
});
}
@Test // DATAES-479
public void shouldHonorTheHighlightBuilderOptions() {
// given
String documentId = randomNumeric(5);
String actualMessage = "some test message with <html> unsafe <script> text";
String highlightedMessage = "some <em>test</em> message with &lt;html&gt; unsafe &lt;script&gt; text";
SampleEntity sampleEntity = SampleEntity.builder()
.id(documentId)
.message(actualMessage)
.version(System.currentTimeMillis())
.build();
IndexQuery indexQuery = getIndexQuery(sampleEntity);
elasticsearchTemplate.index(indexQuery);
elasticsearchTemplate.refresh(SampleEntity.class);
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(termQuery("message", "test"))
.withHighlightBuilder(new HighlightBuilder().encoder("html"))
.withHighlightFields(new HighlightBuilder.Field("message"))
.build();
// when
elasticsearchTemplate.queryForPage(searchQuery, SampleEntity.class, new SearchResultMapper() {
@Override
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
for (SearchHit searchHit : response.getHits()) {
Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
HighlightField highlightFieldMessage = highlightFields.get("message");
// then
assertNotNull(highlightFieldMessage);
assertThat(highlightFieldMessage.fragments()[0].toString(), is(highlightedMessage));
}
return null;
}
});
}
@Test // DATAES-479
public void shouldHighlightIfBuilderSetEvenIfFieldsNotSet() {
// given
String documentId = randomNumeric(5);
String actualMessage = "some test message text";
String highlightedMessage = "some <em>test</em> message text";
SampleEntity sampleEntity = SampleEntity.builder()
.id(documentId)
.message(actualMessage)
.version(System.currentTimeMillis())
.build();
IndexQuery indexQuery = getIndexQuery(sampleEntity);
elasticsearchTemplate.index(indexQuery);
elasticsearchTemplate.refresh(SampleEntity.class);
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(termQuery("message", "test"))
.withHighlightBuilder(new HighlightBuilder().field("message"))
.withHighlightFields(new HighlightBuilder.Field("message"))
.build();
// when
elasticsearchTemplate.queryForPage(searchQuery, SampleEntity.class, new SearchResultMapper() {
@Override
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
for (SearchHit searchHit : response.getHits()) {
Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
HighlightField highlightFieldMessage = highlightFields.get("message");
// then
assertNotNull(highlightFieldMessage);
assertThat(highlightFieldMessage.fragments()[0].toString(), is(highlightedMessage));
}
return null;
}
});
}
@Test
public void shouldDeleteDocumentBySpecifiedTypeUsingDeleteQuery() {
// given
@@ -1256,7 +1548,7 @@ public class ElasticsearchTemplateTests {
for (SearchHit searchHit : response.getHits()) {
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(searchHit.getId());
sampleEntity.setMessage((String) searchHit.getSource().get("message"));
sampleEntity.setMessage((String) searchHit.getSourceAsMap().get("message"));
values.add(sampleEntity);
}
return new AggregatedPageImpl<>((List<T>) values);
@@ -1327,6 +1619,32 @@ public class ElasticsearchTemplateTests {
assertThat(page.getContent().get(0).getMessage(), is("ab"));
}
@Test // DATAES-462
public void shouldReturnScores() {
// given
List<IndexQuery> indexQueries = new ArrayList<>();
indexQueries.add(buildIndex(SampleEntity.builder().id("1").message("ab xz").build()));
indexQueries.add(buildIndex(SampleEntity.builder().id("2").message("bc").build()));
indexQueries.add(buildIndex(SampleEntity.builder().id("3").message("ac xz hi").build()));
elasticsearchTemplate.bulkIndex(indexQueries);
elasticsearchTemplate.refresh(SampleEntity.class);
// when
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(termQuery("message", "xz"))
.withSort(SortBuilders.fieldSort("message"))
.withTrackScores(true)
.build();
AggregatedPage<SampleEntity> page = elasticsearchTemplate.queryForPage(searchQuery, SampleEntity.class);
// then
assertThat(page.getMaxScore(), greaterThan(0f));
assertThat(page.getContent().get(0).getScore(), greaterThan(0f));
}
@Test
public void shouldDoIndexWithoutId() {
@@ -1431,11 +1749,11 @@ public class ElasticsearchTemplateTests {
return null;
}
Map<String, Object> person = new HashMap<>();
person.put("userId", searchHit.getSource().get("userId"));
person.put("email", searchHit.getSource().get("email"));
person.put("title", searchHit.getSource().get("title"));
person.put("firstName", searchHit.getSource().get("firstName"));
person.put("lastName", searchHit.getSource().get("lastName"));
person.put("userId", searchHit.getSourceAsMap().get("userId"));
person.put("email", searchHit.getSourceAsMap().get("email"));
person.put("title", searchHit.getSourceAsMap().get("title"));
person.put("firstName", searchHit.getSourceAsMap().get("firstName"));
person.put("lastName", searchHit.getSourceAsMap().get("lastName"));
chunk.add(person);
}
if (chunk.size() > 0) {
@@ -1942,9 +2260,9 @@ public class ElasticsearchTemplateTests {
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
List<ResultAggregator> values = new ArrayList<>();
for (SearchHit searchHit : response.getHits()) {
String id = String.valueOf(searchHit.getSource().get("id"));
String firstName = StringUtils.isNotEmpty((String) searchHit.getSource().get("firstName")) ? (String) searchHit.getSource().get("firstName") : "";
String lastName = StringUtils.isNotEmpty((String) searchHit.getSource().get("lastName")) ? (String) searchHit.getSource().get("lastName") : "";
String id = String.valueOf(searchHit.getSourceAsMap().get("id"));
String firstName = StringUtils.isNotEmpty((String) searchHit.getSourceAsMap().get("firstName")) ? (String) searchHit.getSourceAsMap().get("firstName") : "";
String lastName = StringUtils.isNotEmpty((String) searchHit.getSourceAsMap().get("lastName")) ? (String) searchHit.getSourceAsMap().get("lastName") : "";
values.add(new ResultAggregator(id, firstName, lastName));
}
return new AggregatedPageImpl<>((List<T>) values);
@@ -1987,13 +2305,17 @@ public class ElasticsearchTemplateTests {
}
private IndexQuery getIndexQuery(SampleEntity sampleEntity) {
return new IndexQueryBuilder().withId(sampleEntity.getId()).withObject(sampleEntity).build();
return new IndexQueryBuilder()
.withId(sampleEntity.getId())
.withObject(sampleEntity)
.withVersion(sampleEntity.getVersion())
.build();
}
private List<IndexQuery> getIndexQueries(List<SampleEntity> sampleEntities) {
List<IndexQuery> indexQueries = new ArrayList<>();
for (SampleEntity sampleEntity : sampleEntities) {
indexQueries.add(new IndexQueryBuilder().withId(sampleEntity.getId()).withObject(sampleEntity).build());
indexQueries.add(getIndexQuery(sampleEntity));
}
return indexQueries;
}
@@ -25,6 +25,7 @@ import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.junit.Test;
@@ -43,6 +44,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
* @author Jakub Vavrik
* @author Mohsin Husen
* @author Keivn Leturc
* @author Nordine Bittich
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:elasticsearch-template-test.xml")
@@ -189,8 +191,28 @@ public class MappingBuilderTests {
elasticsearchTemplate.createIndex(Book.class);
elasticsearchTemplate.putMapping(Book.class);
//when
//then
}
@Test // DATAES-420
public void shouldUseBothAnalyzer() {
//given
elasticsearchTemplate.deleteIndex(Book.class);
elasticsearchTemplate.createIndex(Book.class);
elasticsearchTemplate.putMapping(Book.class);
//when
Map mapping = elasticsearchTemplate.getMapping(Book.class);
Map descriptionMapping = (Map) ((Map) mapping.get("properties")).get("description");
Map prefixDescription = (Map) ((Map) descriptionMapping.get("fields")).get("prefix");
//then
assertThat(prefixDescription.size(), is(3));
assertThat(prefixDescription.get("type"), equalTo("text"));
assertThat(prefixDescription.get("analyzer"), equalTo("stop"));
assertThat(prefixDescription.get("search_analyzer"), equalTo("standard"));
assertThat(descriptionMapping.get("type"), equalTo("text"));
assertThat(descriptionMapping.get("analyzer"), equalTo("whitespace"));
}
}
@@ -0,0 +1,41 @@
/*
* Copyright 2013-2017 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.core.aggregation;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import java.util.Arrays;
/**
* @author Remco Zigterman
*/
public class AggregatedPageImplTest {
@Test
public void constructFacetedPageWithPageable() {
Page<String> page = new AggregatedPageImpl<>(Arrays.asList("Test", "Test 2"), PageRequest.of(0, 2), 10);
assertEquals(10, page.getTotalElements());
assertEquals(2, page.getNumberOfElements());
assertEquals(2, page.getSize());
assertEquals(5, page.getTotalPages());
}
}
@@ -16,7 +16,7 @@
package org.springframework.data.elasticsearch.core.facet;
import static org.springframework.data.elasticsearch.annotations.FieldType.Integer;
import static org.springframework.data.elasticsearch.annotations.FieldType.text;
import static org.springframework.data.elasticsearch.annotations.FieldType.Text;
import java.util.ArrayList;
import java.util.List;
@@ -36,14 +36,14 @@ public class ArticleEntity {
@Id
private String id;
private String title;
@Field(type = text, fielddata = true)
@Field(type = Text, fielddata = true)
private String subject;
@MultiField(
mainField = @Field(type = text),
mainField = @Field(type = Text),
otherFields = {
@InnerField(suffix = "untouched", type = text, store = true, fielddata = true, indexAnalyzer = "keyword"),
@InnerField(suffix = "sort", type = text, store = true, indexAnalyzer = "keyword")
@InnerField(suffix = "untouched", type = Text, store = true, fielddata = true, analyzer = "keyword"),
@InnerField(suffix = "sort", type = Text, store = true, analyzer = "keyword")
}
)
private List<String> authors = new ArrayList<>();
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2018 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.
@@ -15,10 +15,13 @@
*/
package org.springframework.data.elasticsearch.core.mapping;
import static org.assertj.core.api.Assertions.*;
import java.beans.IntrospectionException;
import org.junit.Test;
import org.springframework.data.annotation.Version;
import org.springframework.data.elasticsearch.annotations.Score;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.model.Property;
import org.springframework.data.mapping.model.SimpleTypeHolder;
@@ -30,6 +33,7 @@ import org.springframework.util.ReflectionUtils;
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Mark Paluch
* @author Oliver Gierke
*/
public class SimpleElasticsearchPersistentEntityTests {
@@ -62,6 +66,17 @@ public class SimpleElasticsearchPersistentEntityTests {
// when
entity.addPersistentProperty(persistentProperty2);
}
@Test // DATAES-462
public void rejectsMultipleScoreProperties() {
SimpleElasticsearchMappingContext context = new SimpleElasticsearchMappingContext();
assertThatExceptionOfType(MappingException.class) //
.isThrownBy(() -> context.getRequiredPersistentEntity(TwoScoreProperties.class)) //
.withMessageContaining("first") //
.withMessageContaining("second");
}
private static SimpleElasticsearchPersistentProperty createProperty(SimpleElasticsearchPersistentEntity<?> entity,
String field) {
@@ -106,4 +121,12 @@ public class SimpleElasticsearchPersistentEntityTests {
this.version2 = version2;
}
}
// DATAES-462
static class TwoScoreProperties {
@Score float first;
@Score float second;
}
}
@@ -0,0 +1,44 @@
/*
* Copyright 2018 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.core.mapping;
import static org.assertj.core.api.Assertions.*;
import org.junit.Test;
import org.springframework.data.elasticsearch.annotations.Score;
import org.springframework.data.mapping.MappingException;
/**
* Unit tests for {@link SimpleElasticsearchPersistentProperty}.
*
* @author Oliver Gierke
*/
public class SimpleElasticsearchPersistentPropertyUnitTests {
@Test // DATAES-462
public void rejectsScorePropertyOfTypeOtherthanFloat() {
SimpleElasticsearchMappingContext context = new SimpleElasticsearchMappingContext();
assertThatExceptionOfType(MappingException.class) //
.isThrownBy(() -> context.getRequiredPersistentEntity(InvalidScoreProperty.class)) //
.withMessageContaining("scoreProperty");
}
static class InvalidScoreProperty {
@Score String scoreProperty;
}
}
@@ -29,10 +29,13 @@ import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.InnerField;
import org.springframework.data.elasticsearch.annotations.MultiField;
/**
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Nordine Bittich
*/
@Setter
@Getter
@@ -49,4 +52,11 @@ public class Book {
private Author author;
@Field(type = FieldType.Nested)
private Map<Integer, Collection<String>> buckets = new HashMap<>();
@MultiField(
mainField = @Field(type = FieldType.Text, analyzer = "whitespace"),
otherFields = {
@InnerField(suffix = "prefix", type = FieldType.Text, analyzer = "stop", searchAnalyzer = "standard")
}
)
private String description;
}
@@ -19,7 +19,6 @@ import org.springframework.core.style.ToStringCreator;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldIndex;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.Parent;
@@ -38,7 +37,7 @@ public class ParentEntity {
@Id
private String id;
@Field(type = FieldType.text, store = true)
@Field(type = FieldType.Text, store = true)
private String name;
public ParentEntity() {
@@ -67,10 +66,10 @@ public class ParentEntity {
@Id
private String id;
@Field(type = FieldType.text, store = true)
@Field(type = FieldType.Text, store = true)
@Parent(type = PARENT_TYPE)
private String parentId;
@Field(type = FieldType.text, store = true)
@Field(type = FieldType.Text, store = true)
private String name;
public ChildEntity() {
@@ -1,7 +1,7 @@
package org.springframework.data.elasticsearch.entities;
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
import static org.springframework.data.elasticsearch.annotations.FieldType.text;
import static org.springframework.data.elasticsearch.annotations.FieldType.Text;
import java.util.Date;
@@ -19,7 +19,7 @@ public class SampleDateMappingEntity {
@Id
private String id;
@Field(type = text, index = false, store = true, analyzer = "standard")
@Field(type = Text, index = false, store = true, analyzer = "standard")
private String message;
@Field(type = Date, format = DateFormat.custom, pattern = "dd.MM.yyyy hh:mm")
@@ -15,51 +15,54 @@
*/
package org.springframework.data.elasticsearch.entities;
import java.lang.Double;
import java.lang.Long;
import java.lang.Object;
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Version;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.Score;
import org.springframework.data.elasticsearch.annotations.ScriptedField;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
/**
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Chris White
* @author Sascha Woo
*/
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Builder
@Document(indexName = "test-index-sample", type = "test-type", shards = 1, replicas = 0, refreshInterval = "-1")
public class SampleEntity {
@Id
private String id;
@Field(type = text, store = true, fielddata = true)
@Field(type = Text, store = true, fielddata = true)
private String type;
@Field(type = text, store = true, fielddata = true)
@Field(type = Text, store = true, fielddata = true)
private String message;
private int rate;
@ScriptedField
private Double scriptedRate;
private boolean available;
private String highlightedMessage;
private GeoPoint location;
@Version
private Long version;
@Score
private float score;
@Override
public boolean equals(Object o) {
@@ -48,7 +48,7 @@ public class SampleEntityUUIDKeyed {
@Id
private UUID id;
private String type;
@Field(type = FieldType.text, fielddata = true)
@Field(type = FieldType.Text, fielddata = true)
private String message;
private int rate;
@ScriptedField
@@ -15,7 +15,7 @@
*/
package org.springframework.data.elasticsearch.entities;
import static org.springframework.data.elasticsearch.annotations.FieldType.text;
import static org.springframework.data.elasticsearch.annotations.FieldType.Text;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
@@ -26,7 +26,7 @@ import org.springframework.data.elasticsearch.annotations.Field;
@Document(indexName = "test-index-sample-inherited", type = "mapping", shards = 1, replicas = 0, refreshInterval = "-1")
public class SampleInheritedEntity extends AbstractInheritedEntity {
@Field(type = text, index = false, store = true, analyzer = "standard")
@Field(type = Text, index = false, store = true, analyzer = "standard")
private String message;
public String getMessage() {
@@ -15,7 +15,7 @@
*/
package org.springframework.data.elasticsearch.entities;
import static org.springframework.data.elasticsearch.annotations.FieldType.text;
import static org.springframework.data.elasticsearch.annotations.FieldType.Text;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
@@ -31,7 +31,7 @@ public class SampleMappingEntity {
@Id
private String id;
@Field(type = text, index = false, store = true, analyzer = "standard")
@Field(type = Text, index = false, store = true, analyzer = "standard")
private String message;
private NestedEntity nested;
@@ -54,7 +54,7 @@ public class SampleMappingEntity {
static class NestedEntity {
@Field(type = text)
@Field(type = Text)
private String someField;
public String getSomeField() {
@@ -15,7 +15,7 @@
*/
package org.springframework.data.elasticsearch.entities;
import static org.springframework.data.elasticsearch.annotations.FieldType.text;
import static org.springframework.data.elasticsearch.annotations.FieldType.Text;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
@@ -31,7 +31,7 @@ public class SampleTransientEntity {
@Id
private String id;
@Field(type = text, index = false, store = true, analyzer = "standard")
@Field(type = Text, index = false, store = true, analyzer = "standard")
private String message;
@Transient
@@ -38,6 +38,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
* DynamicSettingAndMappingEntityRepositoryTests
*
* @author Mohsin Husen
* @author Ilkang Na
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:dynamic-settings-test.xml")
@@ -134,7 +135,7 @@ public class DynamicSettingAndMappingEntityRepositoryTests {
String mappings = "{\n" +
" \"test-setting-type\" : {\n" +
" \"properties\" : {\n" +
" \"email\" : {\"type\" : \"string\", \"analyzer\" : \"emailAnalyzer\" }\n" +
" \"email\" : {\"type\" : \"text\", \"analyzer\" : \"emailAnalyzer\" }\n" +
" }\n" +
" }\n" +
"}";
@@ -47,6 +47,7 @@ import static org.springframework.data.domain.Sort.Direction.*;
* @author Mohsin Husen
* @author Mark Paluch
* @author Christoph Strobl
* @author Michael Wirth
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:/simple-repository-test.xml")
@@ -274,6 +275,19 @@ public class SimpleElasticsearchRepositoryTests {
assertEquals(exist, true);
}
@Test // DATAES-363
public void shouldReturnFalseGivenDocumentWithIdDoesNotExist() {
// given
String documentId = randomNumeric(5);
// when
boolean exist = repository.existsById(documentId);
// then
assertThat(exist, is(false));
}
@Test
public void shouldReturnResultsForGivenSearchQuery() {
// given
@@ -45,6 +45,7 @@ import static org.junit.Assert.*;
* @author Mohsin Husen
* @author Mark Paluch
* @author Christoph Strobl
* @author Michael Wirth
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:/simple-repository-test.xml")
@@ -262,6 +263,19 @@ public class UUIDElasticsearchRepositoryTests {
assertEquals(exist, true);
}
@Test // DATAES-363
public void shouldReturnFalseGivenDocumentWithIdDoesNotExist() {
// given
UUID documentId = UUID.randomUUID();
// when
boolean exist = repository.existsById(documentId);
// then
assertThat(exist, is(false));
}
@Test
public void shouldDeleteAll() {
// when
@@ -2,7 +2,7 @@
"test-setting-type": {
"properties": {
"email": {
"type": "string",
"type": "text",
"analyzer": "emailAnalyzer"
}
}
@@ -0,0 +1 @@
mock-maker-inline
@@ -1,3 +1,3 @@
#enabled scripts - this require groovy
script.inline: true
#script.inline: true
#node.max_local_storage_nodes: 100
@@ -1,8 +1,5 @@
{
"synonym-type": {
"_all": {
"enabled": true
},
"properties": {
"text": {
"type": "text",
@@ -22,7 +22,7 @@
description=Lucene expressions integration for Elasticsearch
#
# 'version': plugin's version
version=5.5.0
version=6.2.2
#
# 'name': the plugin name
name=lang-expression
@@ -37,8 +37,14 @@ classname=org.elasticsearch.script.expression.ExpressionPlugin
java.version=1.8
#
# 'elasticsearch.version': version of elasticsearch compiled against
elasticsearch.version=5.5.0
elasticsearch.version=6.2.2
### optional elements for plugins:
#
# 'extended.plugins': other plugins this plugin extends through SPI
extended.plugins=
#
# 'has.native.controller': whether or not the plugin has a native controller
has.native.controller=false
#
# 'requires.keystore': whether or not the plugin needs the elasticsearch keystore be created
requires.keystore=false
@@ -1,44 +0,0 @@
# Elasticsearch plugin descriptor file
# This file must exist as 'plugin-descriptor.properties' in a folder named `elasticsearch`
# inside all plugins.
#
### example plugin for "foo"
#
# foo.zip <-- zip file for the plugin, with this structure:
#|____elasticsearch/
#| |____ <arbitrary name1>.jar <-- classes, resources, dependencies
#| |____ <arbitrary nameN>.jar <-- any number of jars
#| |____ plugin-descriptor.properties <-- example contents below:
#
# classname=foo.bar.BazPlugin
# description=My cool plugin
# version=2.0
# elasticsearch.version=2.0
# java.version=1.7
#
### mandatory elements for all plugins:
#
# 'description': simple summary of the plugin
description=Groovy scripting integration for Elasticsearch
#
# 'version': plugin's version
version=5.5.0
#
# 'name': the plugin name
name=lang-groovy
#
# 'classname': the name of the class to load, fully-qualified.
classname=org.elasticsearch.script.groovy.GroovyPlugin
#
# 'java.version': version of java the code is built against
# use the system property java.specification.version
# version string must be a sequence of nonnegative decimal integers
# separated by "."'s and may have leading zeros
java.version=1.8
#
# 'elasticsearch.version': version of elasticsearch compiled against
elasticsearch.version=5.5.0
### optional elements for plugins:
#
# 'has.native.controller': whether or not the plugin has a native controller
has.native.controller=false
@@ -1,59 +0,0 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
grant {
// needed to generate runtime classes
permission java.lang.RuntimePermission "createClassLoader";
// needed by IndyInterface
permission java.lang.RuntimePermission "getClassLoader";
// needed by groovy engine
permission java.lang.RuntimePermission "accessDeclaredMembers";
permission java.lang.RuntimePermission "accessClassInPackage.sun.reflect";
permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.reflect";
// Allow executing groovy scripts with codesource of /untrusted
permission groovy.security.GroovyCodeSourcePermission "/untrusted";
// Standard set of classes
permission org.elasticsearch.script.ClassPermission "<<STANDARD>>";
// groovy runtime (TODO: clean these up if possible)
permission org.elasticsearch.script.ClassPermission "groovy.grape.GrabAnnotationTransformation";
permission org.elasticsearch.script.ClassPermission "groovy.lang.Binding";
permission org.elasticsearch.script.ClassPermission "groovy.lang.GroovyObject";
permission org.elasticsearch.script.ClassPermission "groovy.lang.GString";
permission org.elasticsearch.script.ClassPermission "groovy.lang.Script";
permission org.elasticsearch.script.ClassPermission "groovy.util.GroovyCollections";
permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.ast.builder.AstBuilderTransformation";
permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.reflection.ClassInfo";
permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.runtime.GStringImpl";
permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.runtime.powerassert.ValueRecorder";
permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.runtime.powerassert.AssertionRenderer";
permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.runtime.ScriptBytecodeAdapter";
permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation";
permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.vmplugin.v7.IndyInterface";
permission org.elasticsearch.script.ClassPermission "sun.reflect.ConstructorAccessorImpl";
permission org.elasticsearch.script.ClassPermission "sun.reflect.MethodAccessorImpl";
permission org.elasticsearch.script.ClassPermission "jdk.internal.reflect.ConstructorAccessorImpl";
permission org.elasticsearch.script.ClassPermission "jdk.internal.reflect.MethodAccessorImpl";
permission org.elasticsearch.script.ClassPermission "groovy.lang.Closure";
permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.runtime.GeneratedClosure";
permission org.elasticsearch.script.ClassPermission "groovy.lang.MetaClass";
permission org.elasticsearch.script.ClassPermission "groovy.lang.Range";
permission org.elasticsearch.script.ClassPermission "groovy.lang.Reference";
};