Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 415464a957 | |||
| 3be999e23d | |||
| eb59b657d2 | |||
| 076303bf2f | |||
| 3d2b5a0191 | |||
| 2497fcd64d | |||
| 0fbb4ca225 | |||
| 37bc97d5eb | |||
| 26eba03116 | |||
| 55bf1f233d | |||
| c9fbdb7126 | |||
| a1d508c656 | |||
| 04308394dd | |||
| be80d7eae4 | |||
| de1c28979d | |||
| bd668e6c2a | |||
| ba899a0017 | |||
| 8181c713c7 | |||
| c6419a1f05 | |||
| 09ee8f9026 | |||
| 13cfb039b5 |
@@ -4,12 +4,12 @@
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-elasticsearch</artifactId>
|
||||
<version>3.0.7.RELEASE</version>
|
||||
<version>3.0.9.RELEASE</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>2.0.7.RELEASE</version>
|
||||
<version>2.0.9.RELEASE</version>
|
||||
</parent>
|
||||
|
||||
<name>Spring Data Elasticsearch</name>
|
||||
@@ -21,7 +21,7 @@
|
||||
<commonslang>2.6</commonslang>
|
||||
<elasticsearch>5.5.0</elasticsearch>
|
||||
<log4j>2.8.2</log4j>
|
||||
<springdata.commons>2.0.7.RELEASE</springdata.commons>
|
||||
<springdata.commons>2.0.9.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>
|
||||
@@ -81,9 +76,19 @@
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.elasticsearch.plugin</groupId>
|
||||
<artifactId>transport-netty3-client</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.elasticsearch.plugin</groupId>
|
||||
<artifactId>transport-netty4-client</artifactId>
|
||||
<version>${elasticsearch}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>log4j-over-slf4j</artifactId>
|
||||
@@ -162,14 +167,6 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<!-- required by elasticsearch -->
|
||||
<groupId>org.elasticsearch.plugin</groupId>
|
||||
<artifactId>transport-netty4-client</artifactId>
|
||||
<version>${elasticsearch}</version>
|
||||
<!--<scope>test</scope>-->
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
|
||||
@@ -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,103 @@
|
||||
/*
|
||||
* 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.InetSocketTransportAddress;
|
||||
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.0.9
|
||||
*/
|
||||
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 InetSocketTransportAddress(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-3
@@ -15,10 +15,12 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.client;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
|
||||
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,7 +33,7 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import static java.util.Arrays.*;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* NodeClientFactoryBean
|
||||
@@ -96,7 +98,7 @@ public class NodeClientFactoryBean implements FactoryBean<Client>, InitializingB
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
+122
-23
@@ -15,14 +15,27 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.client;
|
||||
|
||||
import static org.apache.commons.lang.StringUtils.*;
|
||||
import io.netty.util.ThreadDeathWatcher;
|
||||
import io.netty.util.concurrent.GlobalEventExecutor;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.elasticsearch.client.transport.TransportClient;
|
||||
import org.elasticsearch.common.SuppressForbidden;
|
||||
import org.elasticsearch.common.network.NetworkModule;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.transport.InetSocketTransportAddress;
|
||||
import org.elasticsearch.index.reindex.ReindexPlugin;
|
||||
import org.elasticsearch.join.ParentJoinPlugin;
|
||||
import org.elasticsearch.percolator.PercolatorPlugin;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.script.mustache.MustachePlugin;
|
||||
import org.elasticsearch.transport.Netty4Plugin;
|
||||
import org.elasticsearch.transport.client.PreBuiltTransportClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -30,6 +43,7 @@ import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* TransportClientFactoryBean
|
||||
@@ -38,12 +52,12 @@ import org.springframework.util.Assert;
|
||||
* @author Mohsin Husen
|
||||
* @author Jakub Vavrik
|
||||
* @author Piotr Betkier
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
|
||||
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 ClusterNodes clusterNodes = ClusterNodes.of("127.0.0.1:9300");
|
||||
private String clusterName = "elasticsearch";
|
||||
private Boolean clientTransportSniff = true;
|
||||
private Boolean clientIgnoreClusterName = Boolean.FALSE;
|
||||
@@ -51,8 +65,6 @@ public class TransportClientFactoryBean implements FactoryBean<TransportClient>,
|
||||
private String clientNodesSamplerInterval = "5s";
|
||||
private TransportClient client;
|
||||
private Properties properties;
|
||||
static final String COLON = ":";
|
||||
static final String COMMA = ",";
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
@@ -88,16 +100,12 @@ public class TransportClientFactoryBean implements FactoryBean<TransportClient>,
|
||||
|
||||
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 = new SpringDataTransportClient(settings());
|
||||
|
||||
clusterNodes.stream() //
|
||||
.peek(it -> logger.info("Adding transport node : " + it.toString())) //
|
||||
.forEach(client::addTransportAddress);
|
||||
|
||||
client.connectedNodes();
|
||||
}
|
||||
|
||||
@@ -105,17 +113,14 @@ public class TransportClientFactoryBean implements FactoryBean<TransportClient>,
|
||||
if (properties != null) {
|
||||
return Settings.builder().put(properties).build();
|
||||
}
|
||||
return Settings.builder()
|
||||
.put("cluster.name", clusterName)
|
||||
.put("client.transport.sniff", clientTransportSniff)
|
||||
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();
|
||||
.put("client.transport.nodes_sampler_interval", clientNodesSamplerInterval).build();
|
||||
}
|
||||
|
||||
public void setClusterNodes(String clusterNodes) {
|
||||
this.clusterNodes = clusterNodes;
|
||||
this.clusterNodes = ClusterNodes.of(clusterNodes);
|
||||
}
|
||||
|
||||
public void setClusterName(String clusterName) {
|
||||
@@ -153,4 +158,98 @@ public class TransportClientFactoryBean implements FactoryBean<TransportClient>,
|
||||
public void setProperties(Properties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pretty exact copy of {@link PreBuiltTransportClient} except that we're inspecting the classpath for Netty
|
||||
* dependencies to only include the ones available. {@link PreBuiltTransportClient} expects both Netty 3 and Netty 4
|
||||
* to be present.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @see https://github.com/elastic/elasticsearch/issues/31240
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static class SpringDataTransportClient extends TransportClient {
|
||||
|
||||
/**
|
||||
* Netty wants to do some unwelcome things like use unsafe and replace a private field, or use a poorly considered
|
||||
* buffer recycler. This method disables these things by default, but can be overridden by setting the corresponding
|
||||
* system properties.
|
||||
*/
|
||||
private static void initializeNetty() {
|
||||
/*
|
||||
* We disable three pieces of Netty functionality here:
|
||||
* - we disable Netty from being unsafe
|
||||
* - we disable Netty from replacing the selector key set
|
||||
* - we disable Netty from using the recycler
|
||||
*
|
||||
* While permissions are needed to read and set these, the permissions needed here are innocuous and thus should simply be granted
|
||||
* rather than us handling a security exception here.
|
||||
*/
|
||||
setSystemPropertyIfUnset("io.netty.noUnsafe", Boolean.toString(true));
|
||||
setSystemPropertyIfUnset("io.netty.noKeySetOptimization", Boolean.toString(true));
|
||||
setSystemPropertyIfUnset("io.netty.recycler.maxCapacityPerThread", Integer.toString(0));
|
||||
}
|
||||
|
||||
@SuppressForbidden(reason = "set system properties to configure Netty")
|
||||
private static void setSystemPropertyIfUnset(final String key, final String value) {
|
||||
final String currentValue = System.getProperty(key);
|
||||
if (currentValue == null) {
|
||||
System.setProperty(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static final List<String> OPTIONAL_DEPENDENCIES = Arrays.asList( //
|
||||
"org.elasticsearch.transport.Netty3Plugin", //
|
||||
"org.elasticsearch.transport.Netty4Plugin");
|
||||
|
||||
private static final Collection<Class<? extends Plugin>> PRE_INSTALLED_PLUGINS;
|
||||
|
||||
static {
|
||||
|
||||
initializeNetty();
|
||||
|
||||
List<Class<? extends Plugin>> plugins = new ArrayList<>();
|
||||
boolean found = false;
|
||||
|
||||
for (String dependency : OPTIONAL_DEPENDENCIES) {
|
||||
try {
|
||||
plugins.add((Class<? extends Plugin>) ClassUtils.forName(dependency,
|
||||
SpringDataTransportClient.class.getClassLoader()));
|
||||
found = true;
|
||||
} catch (ClassNotFoundException | LinkageError e) {}
|
||||
}
|
||||
|
||||
Assert.state(found,
|
||||
"Neither Netty 3 or Netty 4 plugin found on the classpath. One of them is required to run the transport client!");
|
||||
|
||||
plugins.add(ReindexPlugin.class);
|
||||
plugins.add(PercolatorPlugin.class);
|
||||
plugins.add(MustachePlugin.class);
|
||||
plugins.add(ParentJoinPlugin.class);
|
||||
|
||||
PRE_INSTALLED_PLUGINS = Collections.unmodifiableList(plugins);
|
||||
}
|
||||
|
||||
public SpringDataTransportClient(Settings settings) {
|
||||
super(settings, PRE_INSTALLED_PLUGINS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
if (NetworkModule.TRANSPORT_TYPE_SETTING.exists(settings) == false
|
||||
|| NetworkModule.TRANSPORT_TYPE_SETTING.get(settings).equals(Netty4Plugin.NETTY_TRANSPORT_NAME)) {
|
||||
try {
|
||||
GlobalEventExecutor.INSTANCE.awaitInactivity(5, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
try {
|
||||
ThreadDeathWatcher.awaitInactivity(5, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,10 @@
|
||||
*/
|
||||
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.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Artur Konczak
|
||||
@@ -33,7 +32,7 @@ public abstract class AbstractResultMapper implements ResultsMapper {
|
||||
}
|
||||
|
||||
public <T> T mapEntity(String source, Class<T> clazz) {
|
||||
if (isBlank(source)) {
|
||||
if (StringUtils.isEmpty(source)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
|
||||
@@ -23,7 +23,6 @@ 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;
|
||||
@@ -40,6 +39,7 @@ import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersiste
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonEncoding;
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
@@ -84,7 +84,7 @@ public class DefaultResultMapper extends AbstractResultMapper {
|
||||
for (SearchHit hit : response.getHits()) {
|
||||
if (hit != null) {
|
||||
T result = null;
|
||||
if (StringUtils.isNotBlank(hit.sourceAsString())) {
|
||||
if (StringUtils.hasText(hit.sourceAsString())) {
|
||||
result = mapEntity(hit.sourceAsString(), clazz);
|
||||
} else {
|
||||
result = mapEntity(hit.getFields().values(), clazz);
|
||||
|
||||
+88
-39
@@ -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,6 +32,7 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import org.elasticsearch.action.ListenableActionFuture;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
|
||||
@@ -44,6 +51,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;
|
||||
@@ -59,7 +67,10 @@ 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 +95,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
|
||||
@@ -108,10 +128,14 @@ import static org.springframework.util.CollectionUtils.isEmpty;
|
||||
* @author Chris White
|
||||
* @author Mark Paluch
|
||||
* @author Sascha Woo
|
||||
* @author Ted Liang
|
||||
*/
|
||||
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;
|
||||
@@ -175,13 +199,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.hasText(mappingPath)) {
|
||||
String mappings = readFileFromClasspath(mappingPath);
|
||||
if (isNotBlank(mappings)) {
|
||||
if (StringUtils.hasText(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);
|
||||
@@ -312,7 +336,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);
|
||||
}
|
||||
|
||||
@@ -335,11 +359,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());
|
||||
}
|
||||
|
||||
@@ -350,7 +371,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());
|
||||
}
|
||||
|
||||
@@ -561,9 +582,9 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
|
||||
}
|
||||
|
||||
private UpdateRequestBuilder prepareUpdate(UpdateQuery query) {
|
||||
String indexName = isNotBlank(query.getIndexName()) ? query.getIndexName()
|
||||
String indexName = StringUtils.hasText(query.getIndexName()) ? query.getIndexName()
|
||||
: getPersistentEntityFor(query.getClazz()).getIndexName();
|
||||
String type = isNotBlank(query.getType()) ? query.getType()
|
||||
String type = StringUtils.hasText(query.getType()) ? query.getType()
|
||||
: getPersistentEntityFor(query.getClazz()).getIndexType();
|
||||
Assert.notNull(indexName, "No index defined for Query");
|
||||
Assert.notNull(type, "No type define for Query");
|
||||
@@ -663,9 +684,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.hasText(deleteQuery.getIndex()) ? deleteQuery.getIndex()
|
||||
: getPersistentEntityFor(clazz).getIndexName();
|
||||
String typeName = isNotBlank(deleteQuery.getType()) ? deleteQuery.getType()
|
||||
String typeName = StringUtils.hasText(deleteQuery.getType()) ? deleteQuery.getType()
|
||||
: getPersistentEntityFor(clazz).getIndexType();
|
||||
Integer pageSize = deleteQuery.getPageSize() != null ? deleteQuery.getPageSize() : 1000;
|
||||
Long scrollTimeInMillis = deleteQuery.getScrollTimeInMillis() != null ? deleteQuery.getScrollTimeInMillis()
|
||||
@@ -766,7 +787,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
|
||||
requestBuilder.setPostFilter(elasticsearchFilter);
|
||||
}
|
||||
|
||||
return getSearchResponse(requestBuilder.execute());
|
||||
return getSearchResponse(requestBuilder);
|
||||
}
|
||||
|
||||
private SearchResponse doScroll(SearchRequestBuilder requestBuilder, SearchQuery searchQuery) {
|
||||
@@ -778,7 +799,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) {
|
||||
@@ -822,8 +843,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.hasText(query.getIndexName()) ? query.getIndexName() : persistentEntity.getIndexName();
|
||||
String type = StringUtils.hasText(query.getType()) ? query.getType() : persistentEntity.getIndexType();
|
||||
|
||||
Assert.notNull(indexName, "No 'indexName' defined for MoreLikeThisQuery");
|
||||
Assert.notNull(type, "No 'type' defined for MoreLikeThisQuery");
|
||||
@@ -904,7 +925,16 @@ 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(SearchRequestBuilder requestBuilder) {
|
||||
|
||||
if (QUERY_LOGGER.isDebugEnabled()) {
|
||||
QUERY_LOGGER.debug(requestBuilder.toString());
|
||||
}
|
||||
|
||||
return getSearchResponse(requestBuilder.execute());
|
||||
}
|
||||
|
||||
private SearchResponse getSearchResponse(ListenableActionFuture<SearchResponse> response) {
|
||||
@@ -918,13 +948,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.hasText(settingPath)) {
|
||||
String settings = readFileFromClasspath(settingPath);
|
||||
if (isNotBlank(settings)) {
|
||||
if (StringUtils.hasText(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)));
|
||||
@@ -1003,8 +1033,27 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1016,15 +1065,15 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
|
||||
|
||||
private IndexRequestBuilder prepareIndex(IndexQuery query) {
|
||||
try {
|
||||
String indexName = isBlank(query.getIndexName())
|
||||
String indexName = StringUtils.isEmpty(query.getIndexName())
|
||||
? retrieveIndexNameFromPersistentEntity(query.getObject().getClass())[0] : query.getIndexName();
|
||||
String type = isBlank(query.getType()) ? retrieveTypeFromPersistentEntity(query.getObject().getClass())[0]
|
||||
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);
|
||||
@@ -1074,11 +1123,11 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
|
||||
aliasAction.filter(query.getFilterBuilder());
|
||||
} else if (query.getFilter() != null) {
|
||||
aliasAction.filter(query.getFilter());
|
||||
} else if (isNotBlank(query.getRouting())) {
|
||||
} else if (StringUtils.hasText(query.getRouting())) {
|
||||
aliasAction.routing(query.getRouting());
|
||||
} else if (isNotBlank(query.getSearchRouting())) {
|
||||
} else if (StringUtils.hasText(query.getSearchRouting())) {
|
||||
aliasAction.searchRouting(query.getSearchRouting());
|
||||
} else if (isNotBlank(query.getIndexRouting())) {
|
||||
} else if (StringUtils.hasText(query.getIndexRouting())) {
|
||||
aliasAction.indexRouting(query.getIndexRouting());
|
||||
}
|
||||
return client.admin().indices().prepareAliases().addAliasAction(aliasAction).execute().actionGet().isAcknowledged();
|
||||
@@ -1198,14 +1247,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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,11 +15,15 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.*;
|
||||
import static org.springframework.util.StringUtils.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
@@ -37,9 +41,7 @@ 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 static org.elasticsearch.common.xcontent.XContentFactory.*;
|
||||
import static org.springframework.util.StringUtils.*;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Rizwan Idrees
|
||||
@@ -51,6 +53,7 @@ import static org.springframework.util.StringUtils.*;
|
||||
* @author Pavel Luhin
|
||||
* @author Mark Paluch
|
||||
* @author Sascha Woo
|
||||
* @author Nordine Bittich
|
||||
*/
|
||||
class MappingBuilder {
|
||||
|
||||
@@ -87,7 +90,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 +121,7 @@ class MappingBuilder {
|
||||
|
||||
if (field.isAnnotationPresent(Mapping.class)) {
|
||||
String mappingPath = field.getAnnotation(Mapping.class).mappingPath();
|
||||
if (isNotBlank(mappingPath)) {
|
||||
if (StringUtils.hasText(mappingPath)) {
|
||||
ClassPathResource mappings = new ClassPathResource(mappingPath);
|
||||
if (mappings.exists()) {
|
||||
xContentBuilder.rawField(field.getName(), mappings.getInputStream());
|
||||
@@ -136,7 +139,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 +205,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.hasText(annotation.searchAnalyzer())) {
|
||||
xContentBuilder.field(FIELD_SEARCH_ANALYZER, annotation.searchAnalyzer());
|
||||
}
|
||||
if (isNotBlank(annotation.analyzer())) {
|
||||
if (StringUtils.hasText(annotation.analyzer())) {
|
||||
xContentBuilder.field(FIELD_INDEX_ANALYZER, annotation.analyzer());
|
||||
}
|
||||
}
|
||||
@@ -221,83 +224,101 @@ class MappingBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.hasText(analyzer)) {
|
||||
builder.field(FIELD_INDEX_ANALYZER, analyzer);
|
||||
}
|
||||
if (isNotBlank(fieldAnnotation.analyzer())) {
|
||||
xContentBuilder.field(FIELD_INDEX_ANALYZER, fieldAnnotation.analyzer());
|
||||
if (StringUtils.hasText(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().name().toLowerCase());
|
||||
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) {
|
||||
|
||||
+2
-2
@@ -16,13 +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;
|
||||
|
||||
|
||||
/**
|
||||
@@ -54,7 +54,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.hasText(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());
|
||||
|
||||
+3
-3
@@ -19,12 +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;
|
||||
|
||||
|
||||
/**
|
||||
@@ -77,7 +77,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.hasText(keyField) ? keyField : this.field;
|
||||
rangeBuilder.field(field);
|
||||
|
||||
for (Entry entry : entries) {
|
||||
@@ -86,7 +86,7 @@ public class RangeFacetRequest extends AbstractFacetRequest {
|
||||
}
|
||||
|
||||
rangeBuilder.subAggregation(AggregationBuilders.extendedStats(INTERNAL_STATS).field(field));
|
||||
if(StringUtils.isNotBlank(valueField)){
|
||||
if(StringUtils.hasText(valueField)){
|
||||
rangeBuilder.subAggregation(AggregationBuilders.sum(RANGE_INTERNAL_SUM).field(valueField));
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -16,11 +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;
|
||||
|
||||
|
||||
/**
|
||||
@@ -47,7 +47,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.hasText(field) && fields == null, "Please select field or fields on which to build the facets !!!");
|
||||
return AggregationBuilders.extendedStats(getName()).field(field);
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -16,8 +16,6 @@
|
||||
|
||||
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;
|
||||
@@ -26,6 +24,8 @@ import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilde
|
||||
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;
|
||||
|
||||
|
||||
/**
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ public class TermFacetRequest extends AbstractFacetRequest {
|
||||
default:
|
||||
termsBuilder.order(Terms.Order.count(true));
|
||||
}
|
||||
if (ArrayUtils.isNotEmpty(excludeTerms)) {
|
||||
if (!ObjectUtils.isEmpty(excludeTerms)) {
|
||||
termsBuilder.includeExclude(new IncludeExclude(null,excludeTerms));
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ public class TermFacetRequest extends AbstractFacetRequest {
|
||||
termsBuilder.size(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(regex)) {
|
||||
if (StringUtils.hasText(regex)) {
|
||||
termsBuilder.includeExclude(new IncludeExclude(new RegExp(regex),null));
|
||||
}
|
||||
|
||||
|
||||
@@ -15,9 +15,14 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.query;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.elasticsearch.core.geo.GeoBox;
|
||||
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
||||
@@ -25,6 +30,7 @@ import org.springframework.data.geo.Box;
|
||||
import org.springframework.data.geo.Distance;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Criteria is the central class when constructing queries. It follows more or less a fluent API style, which allows to
|
||||
@@ -42,8 +48,8 @@ public class Criteria {
|
||||
"field=" + field.getName() +
|
||||
", boost=" + boost +
|
||||
", negating=" + negating +
|
||||
", queryCriteria=" + StringUtils.join(queryCriteria, '|') +
|
||||
", filterCriteria=" + StringUtils.join(filterCriteria, '|') +
|
||||
", queryCriteria=" + StringUtils.collectionToDelimitedString(queryCriteria, "|") +
|
||||
", filterCriteria=" + StringUtils.collectionToDelimitedString(filterCriteria, "|") +
|
||||
'}';
|
||||
}
|
||||
|
||||
@@ -437,7 +443,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.hasText(geoLocation), "geoLocation value must not be null");
|
||||
filterCriteria.add(new CriteriaEntry(OperationKey.WITHIN, new Object[]{geoLocation, distance}));
|
||||
return this;
|
||||
}
|
||||
@@ -474,8 +480,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.hasText(topLeftGeohash), "topLeftGeohash must not be empty");
|
||||
Assert.isTrue(StringUtils.hasText(bottomRightGeohash), "bottomRightGeohash must not be empty");
|
||||
filterCriteria.add(new CriteriaEntry(OperationKey.BBOX, new Object[]{topLeftGeohash, bottomRightGeohash}));
|
||||
return this;
|
||||
}
|
||||
@@ -502,7 +508,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,6 +1,53 @@
|
||||
Spring Data Elasticsearch Changelog
|
||||
===================================
|
||||
|
||||
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].
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Spring Data Elasticsearch 3.0.7
|
||||
Spring Data Elasticsearch 3.0.9
|
||||
Copyright (c) [2013-2016] Pivotal Software, Inc.
|
||||
|
||||
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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 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"));
|
||||
}
|
||||
}
|
||||
+124
-1
@@ -464,6 +464,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
|
||||
@@ -2070,4 +2193,4 @@ public class ElasticsearchTemplateTests {
|
||||
this.lastName = lastName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,8 +42,8 @@ public class ArticleEntity {
|
||||
@MultiField(
|
||||
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<>();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user