mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-07-12 21:33:27 +00:00
Add support for index aliases.
Original Pull Request #2905 Closes #2599
This commit is contained in:
parent
86e0e660be
commit
fbe54e485b
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2024 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
|
||||
*
|
||||
* https://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.annotations;
|
||||
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Identifies an alias for the index.
|
||||
*
|
||||
* @author Youssef Aouichaoui
|
||||
* @since 5.4
|
||||
*/
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE})
|
||||
@Repeatable(Aliases.class)
|
||||
public @interface Alias {
|
||||
/**
|
||||
* @return Index alias name. Alias for {@link #alias}.
|
||||
*/
|
||||
@AliasFor("alias")
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* @return Index alias name. Alias for {@link #value}.
|
||||
*/
|
||||
@AliasFor("value")
|
||||
String alias() default "";
|
||||
|
||||
/**
|
||||
* @return Query used to limit documents the alias can access.
|
||||
*/
|
||||
Filter filter() default @Filter;
|
||||
|
||||
/**
|
||||
* @return Used to route indexing operations to a specific shard.
|
||||
*/
|
||||
String indexRouting() default "";
|
||||
|
||||
/**
|
||||
* @return Used to route indexing and search operations to a specific shard.
|
||||
*/
|
||||
String routing() default "";
|
||||
|
||||
/**
|
||||
* @return Used to route search operations to a specific shard.
|
||||
*/
|
||||
String searchRouting() default "";
|
||||
|
||||
/**
|
||||
* @return Is the alias hidden?
|
||||
*/
|
||||
boolean isHidden() default false;
|
||||
|
||||
/**
|
||||
* @return Is it the 'write index' for the alias?
|
||||
*/
|
||||
boolean isWriteIndex() default false;
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2024 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
|
||||
*
|
||||
* https://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.annotations;
|
||||
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Container annotation that aggregates several {@link Alias} annotations.
|
||||
*
|
||||
* @author Youssef Aouichaoui
|
||||
* @see Alias
|
||||
* @since 5.4
|
||||
*/
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE})
|
||||
public @interface Aliases {
|
||||
Alias[] value();
|
||||
}
|
@ -100,6 +100,13 @@ public @interface Document {
|
||||
*/
|
||||
boolean storeVersionInSource() default true;
|
||||
|
||||
/**
|
||||
* Aliases for the index.
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
Alias[] aliases() default {};
|
||||
|
||||
/**
|
||||
* @since 4.3
|
||||
*/
|
||||
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2024 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
|
||||
*
|
||||
* https://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.annotations;
|
||||
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
/**
|
||||
* Query used to limit documents.
|
||||
*
|
||||
* @author Youssef Aouichaoui
|
||||
* @since 5.4
|
||||
*/
|
||||
public @interface Filter {
|
||||
/**
|
||||
* @return Query used to limit documents. Alias for {@link #query}.
|
||||
*/
|
||||
@AliasFor("query")
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* @return Query used to limit documents. Alias for {@link #value}.
|
||||
*/
|
||||
@AliasFor("value")
|
||||
String query() default "";
|
||||
}
|
@ -21,6 +21,7 @@ import co.elastic.clients.elasticsearch.indices.*;
|
||||
import co.elastic.clients.transport.ElasticsearchTransport;
|
||||
import co.elastic.clients.transport.endpoints.BooleanResponse;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@ -46,6 +47,8 @@ import org.springframework.data.elasticsearch.core.index.GetIndexTemplateRequest
|
||||
import org.springframework.data.elasticsearch.core.index.GetTemplateRequest;
|
||||
import org.springframework.data.elasticsearch.core.index.PutIndexTemplateRequest;
|
||||
import org.springframework.data.elasticsearch.core.index.PutTemplateRequest;
|
||||
import org.springframework.data.elasticsearch.core.mapping.Alias;
|
||||
import org.springframework.data.elasticsearch.core.mapping.CreateIndexSettings;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.lang.Nullable;
|
||||
@ -137,11 +140,14 @@ public class IndicesTemplate extends ChildTemplate<ElasticsearchTransport, Elast
|
||||
|
||||
protected boolean doCreate(IndexCoordinates indexCoordinates, Map<String, Object> settings,
|
||||
@Nullable Document mapping) {
|
||||
Set<Alias> aliases = (boundClass != null) ? getAliasesFor(boundClass) : new HashSet<>();
|
||||
CreateIndexSettings indexSettings = CreateIndexSettings.builder(indexCoordinates)
|
||||
.withAliases(aliases)
|
||||
.withSettings(settings)
|
||||
.withMapping(mapping)
|
||||
.build();
|
||||
|
||||
Assert.notNull(indexCoordinates, "indexCoordinates must not be null");
|
||||
Assert.notNull(settings, "settings must not be null");
|
||||
|
||||
CreateIndexRequest createIndexRequest = requestConverter.indicesCreateRequest(indexCoordinates, settings, mapping);
|
||||
CreateIndexRequest createIndexRequest = requestConverter.indicesCreateRequest(indexSettings);
|
||||
CreateIndexResponse createIndexResponse = execute(client -> client.create(createIndexRequest));
|
||||
return Boolean.TRUE.equals(createIndexResponse.acknowledged());
|
||||
}
|
||||
@ -449,5 +455,14 @@ public class IndicesTemplate extends ChildTemplate<ElasticsearchTransport, Elast
|
||||
public IndexCoordinates getIndexCoordinatesFor(Class<?> clazz) {
|
||||
return getRequiredPersistentEntity(clazz).getIndexCoordinates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Alias} of the provided class.
|
||||
*
|
||||
* @param clazz provided class that can be used to extract aliases.
|
||||
*/
|
||||
public Set<Alias> getAliasesFor(Class<?> clazz) {
|
||||
return getRequiredPersistentEntity(clazz).getAliases();
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
@ -21,9 +21,12 @@ import co.elastic.clients.elasticsearch._types.AcknowledgedResponseBase;
|
||||
import co.elastic.clients.elasticsearch.indices.*;
|
||||
import co.elastic.clients.transport.ElasticsearchTransport;
|
||||
import co.elastic.clients.transport.endpoints.BooleanResponse;
|
||||
import org.springframework.data.elasticsearch.core.mapping.Alias;
|
||||
import org.springframework.data.elasticsearch.core.mapping.CreateIndexSettings;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
@ -130,8 +133,14 @@ public class ReactiveIndicesTemplate
|
||||
|
||||
private Mono<Boolean> doCreate(IndexCoordinates indexCoordinates, Map<String, Object> settings,
|
||||
@Nullable Document mapping) {
|
||||
Set<Alias> aliases = (boundClass != null) ? getAliasesFor(boundClass) : new HashSet<>();
|
||||
CreateIndexSettings indexSettings = CreateIndexSettings.builder(indexCoordinates)
|
||||
.withAliases(aliases)
|
||||
.withSettings(settings)
|
||||
.withMapping(mapping)
|
||||
.build();
|
||||
|
||||
CreateIndexRequest createIndexRequest = requestConverter.indicesCreateRequest(indexCoordinates, settings, mapping);
|
||||
CreateIndexRequest createIndexRequest = requestConverter.indicesCreateRequest(indexSettings);
|
||||
Mono<CreateIndexResponse> createIndexResponse = Mono.from(execute(client -> client.create(createIndexRequest)));
|
||||
return createIndexResponse.map(CreateIndexResponse::acknowledged);
|
||||
}
|
||||
@ -435,6 +444,15 @@ public class ReactiveIndicesTemplate
|
||||
return elasticsearchConverter.getMappingContext().getRequiredPersistentEntity(clazz).getIndexCoordinates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Alias} of the provided class.
|
||||
*
|
||||
* @param clazz provided class that can be used to extract aliases.
|
||||
*/
|
||||
private Set<Alias> getAliasesFor(Class<?> clazz) {
|
||||
return elasticsearchConverter.getMappingContext().getRequiredPersistentEntity(clazz).getAliases();
|
||||
}
|
||||
|
||||
private Class<?> checkForBoundClass() {
|
||||
if (boundClass == null) {
|
||||
throw new InvalidDataAccessApiUsageException("IndexOperations are not bound");
|
||||
|
@ -88,6 +88,8 @@ import org.springframework.data.elasticsearch.core.index.GetIndexTemplateRequest
|
||||
import org.springframework.data.elasticsearch.core.index.GetTemplateRequest;
|
||||
import org.springframework.data.elasticsearch.core.index.PutIndexTemplateRequest;
|
||||
import org.springframework.data.elasticsearch.core.index.PutTemplateRequest;
|
||||
import org.springframework.data.elasticsearch.core.mapping.Alias;
|
||||
import org.springframework.data.elasticsearch.core.mapping.CreateIndexSettings;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
@ -170,7 +172,7 @@ class RequestConverter extends AbstractQueryProcessor {
|
||||
}));
|
||||
}
|
||||
|
||||
private Alias.Builder buildAlias(AliasActionParameters parameters, Alias.Builder aliasBuilder) {
|
||||
private co.elastic.clients.elasticsearch.indices.Alias.Builder buildAlias(AliasActionParameters parameters, co.elastic.clients.elasticsearch.indices.Alias.Builder aliasBuilder) {
|
||||
|
||||
if (parameters.getRouting() != null) {
|
||||
aliasBuilder.routing(parameters.getRouting());
|
||||
@ -234,17 +236,25 @@ class RequestConverter extends AbstractQueryProcessor {
|
||||
return new ExistsRequest.Builder().index(Arrays.asList(indexCoordinates.getIndexNames())).build();
|
||||
}
|
||||
|
||||
public CreateIndexRequest indicesCreateRequest(IndexCoordinates indexCoordinates, Map<String, Object> settings,
|
||||
@Nullable Document mapping) {
|
||||
|
||||
Assert.notNull(indexCoordinates, "indexCoordinates must not be null");
|
||||
Assert.notNull(settings, "settings must not be null");
|
||||
public CreateIndexRequest indicesCreateRequest(CreateIndexSettings indexSettings) {
|
||||
Map<String, co.elastic.clients.elasticsearch.indices.Alias> aliases = new HashMap<>();
|
||||
for (Alias alias : indexSettings.getAliases()) {
|
||||
co.elastic.clients.elasticsearch.indices.Alias esAlias = co.elastic.clients.elasticsearch.indices.Alias.of(ab -> ab.filter(getQuery(alias.getFilter(), null))
|
||||
.routing(alias.getRouting())
|
||||
.indexRouting(alias.getIndexRouting())
|
||||
.searchRouting(alias.getSearchRouting())
|
||||
.isHidden(alias.getHidden())
|
||||
.isWriteIndex(alias.getWriteIndex())
|
||||
);
|
||||
aliases.put(alias.getAlias(), esAlias);
|
||||
}
|
||||
|
||||
// note: the new client does not support the index.storeType anymore
|
||||
return new CreateIndexRequest.Builder() //
|
||||
.index(indexCoordinates.getIndexName()) //
|
||||
.settings(indexSettings(settings)) //
|
||||
.mappings(typeMapping(mapping)) //
|
||||
.index(indexSettings.getIndexCoordinates().getIndexName()) //
|
||||
.aliases(aliases)
|
||||
.settings(indexSettings(indexSettings.getSettings())) //
|
||||
.mappings(typeMapping(indexSettings.getMapping())) //
|
||||
.build();
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright 2024 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
|
||||
*
|
||||
* https://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 org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Immutable Value object encapsulating index alias(es).
|
||||
*
|
||||
* @author Youssef Aouichaoui
|
||||
* @since 5.4
|
||||
*/
|
||||
public class Alias {
|
||||
/**
|
||||
* Alias name for the index.
|
||||
*/
|
||||
private final String alias;
|
||||
|
||||
/**
|
||||
* Query used to limit documents the alias can access.
|
||||
*/
|
||||
@Nullable
|
||||
private final Query filter;
|
||||
|
||||
/**
|
||||
* Used to route indexing operations to a specific shard.
|
||||
*/
|
||||
@Nullable
|
||||
private final String indexRouting;
|
||||
|
||||
/**
|
||||
* Used to route search operations to a specific shard.
|
||||
*/
|
||||
@Nullable
|
||||
private final String searchRouting;
|
||||
|
||||
/**
|
||||
* Used to route indexing and search operations to a specific shard.
|
||||
*/
|
||||
@Nullable
|
||||
private final String routing;
|
||||
|
||||
/**
|
||||
* The alias is hidden?
|
||||
* By default, this is set to {@code false}.
|
||||
*/
|
||||
@Nullable
|
||||
private final Boolean isHidden;
|
||||
|
||||
/**
|
||||
* The index is the 'write index' for the alias?
|
||||
* By default, this is set to {@code false}.
|
||||
*/
|
||||
@Nullable
|
||||
private final Boolean isWriteIndex;
|
||||
|
||||
private Alias(Builder builder) {
|
||||
this.alias = builder.alias;
|
||||
|
||||
this.filter = builder.filter;
|
||||
|
||||
this.indexRouting = builder.indexRouting;
|
||||
this.searchRouting = builder.searchRouting;
|
||||
this.routing = builder.routing;
|
||||
|
||||
this.isHidden = builder.isHidden;
|
||||
this.isWriteIndex = builder.isWriteIndex;
|
||||
}
|
||||
|
||||
public String getAlias() {
|
||||
return alias;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Query getFilter() {
|
||||
return filter;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getIndexRouting() {
|
||||
return indexRouting;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getSearchRouting() {
|
||||
return searchRouting;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getRouting() {
|
||||
return routing;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Boolean getHidden() {
|
||||
return isHidden;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Boolean getWriteIndex() {
|
||||
return isWriteIndex;
|
||||
}
|
||||
|
||||
public static Builder builder(String alias) {
|
||||
return new Builder(alias);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof Alias that)) return false;
|
||||
|
||||
return Objects.equals(alias, that.alias) && Objects.equals(filter, that.filter)
|
||||
&& Objects.equals(indexRouting, that.indexRouting)
|
||||
&& Objects.equals(searchRouting, that.searchRouting)
|
||||
&& Objects.equals(routing, that.routing)
|
||||
&& Objects.equals(isHidden, that.isHidden)
|
||||
&& Objects.equals(isWriteIndex, that.isWriteIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(alias, filter, indexRouting, searchRouting, routing, isHidden, isWriteIndex);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private final String alias;
|
||||
|
||||
@Nullable
|
||||
private Query filter;
|
||||
|
||||
@Nullable
|
||||
private String indexRouting;
|
||||
@Nullable
|
||||
private String searchRouting;
|
||||
@Nullable
|
||||
private String routing;
|
||||
|
||||
@Nullable
|
||||
private Boolean isHidden;
|
||||
@Nullable
|
||||
private Boolean isWriteIndex;
|
||||
|
||||
public Builder(String alias) {
|
||||
Assert.notNull(alias, "alias must not be null");
|
||||
this.alias = alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query used to limit documents the alias can access.
|
||||
*/
|
||||
public Builder withFilter(@Nullable Query filter) {
|
||||
this.filter = filter;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to route indexing operations to a specific shard.
|
||||
*/
|
||||
public Builder withIndexRouting(@Nullable String indexRouting) {
|
||||
if (indexRouting != null && !indexRouting.trim().isEmpty()) {
|
||||
this.indexRouting = indexRouting;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to route search operations to a specific shard.
|
||||
*/
|
||||
public Builder withSearchRouting(@Nullable String searchRouting) {
|
||||
if (searchRouting != null && !searchRouting.trim().isEmpty()) {
|
||||
this.searchRouting = searchRouting;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to route indexing and search operations to a specific shard.
|
||||
*/
|
||||
public Builder withRouting(@Nullable String routing) {
|
||||
if (routing != null && !routing.trim().isEmpty()) {
|
||||
this.routing = routing;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The alias is hidden?
|
||||
* By default, this is set to {@code false}.
|
||||
*/
|
||||
public Builder withHidden(@Nullable Boolean hidden) {
|
||||
isHidden = hidden;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The index is the 'write index' for the alias?
|
||||
* By default, this is set to {@code false}.
|
||||
*/
|
||||
public Builder withWriteIndex(@Nullable Boolean writeIndex) {
|
||||
isWriteIndex = writeIndex;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Alias build() {
|
||||
return new Alias(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2024 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
|
||||
*
|
||||
* https://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 org.springframework.data.elasticsearch.core.document.Document;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Encapsulating index mapping fields, settings, and index alias(es).
|
||||
*
|
||||
* @author Youssef Aouichaoui
|
||||
* @since 5.3
|
||||
*/
|
||||
public class CreateIndexSettings {
|
||||
private final IndexCoordinates indexCoordinates;
|
||||
private final Set<Alias> aliases;
|
||||
|
||||
@Nullable
|
||||
private final Map<String, Object> settings;
|
||||
|
||||
@Nullable
|
||||
private final Document mapping;
|
||||
|
||||
private CreateIndexSettings(Builder builder) {
|
||||
this.indexCoordinates = builder.indexCoordinates;
|
||||
this.aliases = builder.aliases;
|
||||
|
||||
this.settings = builder.settings;
|
||||
this.mapping = builder.mapping;
|
||||
}
|
||||
|
||||
public static Builder builder(IndexCoordinates indexCoordinates) {
|
||||
return new Builder(indexCoordinates);
|
||||
}
|
||||
|
||||
public IndexCoordinates getIndexCoordinates() {
|
||||
return indexCoordinates;
|
||||
}
|
||||
|
||||
public Alias[] getAliases() {
|
||||
return aliases.toArray(Alias[]::new);
|
||||
}
|
||||
|
||||
public Map<String, Object> getSettings() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Document getMapping() {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private IndexCoordinates indexCoordinates;
|
||||
private final Set<Alias> aliases = new HashSet<>();
|
||||
|
||||
@Nullable
|
||||
private Map<String, Object> settings;
|
||||
|
||||
@Nullable
|
||||
private Document mapping;
|
||||
|
||||
public Builder(IndexCoordinates indexCoordinates) {
|
||||
Assert.notNull(indexCoordinates, "indexCoordinates must not be null");
|
||||
this.indexCoordinates = indexCoordinates;
|
||||
}
|
||||
|
||||
public Builder withAlias(Alias alias) {
|
||||
Assert.notNull(alias, "alias must not be null");
|
||||
this.aliases.add(alias);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withAliases(Set<Alias> aliases) {
|
||||
Assert.notNull(aliases, "aliases must not be null");
|
||||
this.aliases.addAll(aliases);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withSettings(Map<String, Object> settings) {
|
||||
Assert.notNull(settings, "settings must not be null");
|
||||
this.settings = settings;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withMapping(@Nullable Document mapping) {
|
||||
this.mapping = mapping;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public CreateIndexSettings build() {
|
||||
return new CreateIndexSettings(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -25,6 +25,8 @@ import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.model.FieldNamingStrategy;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* ElasticsearchPersistentEntity
|
||||
*
|
||||
@ -42,6 +44,14 @@ public interface ElasticsearchPersistentEntity<T> extends PersistentEntity<T, El
|
||||
|
||||
IndexCoordinates getIndexCoordinates();
|
||||
|
||||
/**
|
||||
* Retrieves the aliases associated with the current entity.
|
||||
*
|
||||
* @return Returns a set of aliases of the {@link PersistentEntity}.
|
||||
* @since 5.4
|
||||
*/
|
||||
Set<Alias> getAliases();
|
||||
|
||||
short getShards();
|
||||
|
||||
short getReplicas();
|
||||
|
@ -15,7 +15,9 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.mapping;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@ -31,6 +33,8 @@ import org.springframework.data.elasticsearch.annotations.Routing;
|
||||
import org.springframework.data.elasticsearch.annotations.Setting;
|
||||
import org.springframework.data.elasticsearch.core.index.Settings;
|
||||
import org.springframework.data.elasticsearch.core.join.JoinField;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.core.query.StringQuery;
|
||||
import org.springframework.data.mapping.MappingException;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.model.BasicPersistentEntity;
|
||||
@ -80,6 +84,7 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
|
||||
private final ConcurrentHashMap<String, Expression> routingExpressions = new ConcurrentHashMap<>();
|
||||
private @Nullable String routing;
|
||||
private final ContextConfiguration contextConfiguration;
|
||||
private final Set<Alias> aliases = new HashSet<>();
|
||||
|
||||
private final ConcurrentHashMap<String, Expression> indexNameExpressions = new ConcurrentHashMap<>();
|
||||
private final Lazy<EvaluationContext> indexNameEvaluationContext = Lazy.of(this::getIndexNameEvaluationContext);
|
||||
@ -112,6 +117,7 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
|
||||
this.dynamic = document.dynamic();
|
||||
this.storeIdInSource = document.storeIdInSource();
|
||||
this.storeVersionInSource = document.storeVersionInSource();
|
||||
buildAliases();
|
||||
} else {
|
||||
this.dynamic = Dynamic.INHERIT;
|
||||
this.storeIdInSource = true;
|
||||
@ -138,6 +144,11 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
|
||||
return resolve(IndexCoordinates.of(getIndexName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Alias> getAliases() {
|
||||
return aliases;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getIndexStoreType() {
|
||||
@ -615,4 +626,36 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
|
||||
public Dynamic dynamic() {
|
||||
return dynamic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Building once the aliases for the current document.
|
||||
*/
|
||||
private void buildAliases() {
|
||||
// Clear the existing aliases.
|
||||
aliases.clear();
|
||||
|
||||
if (document != null) {
|
||||
for (org.springframework.data.elasticsearch.annotations.Alias alias : document.aliases()) {
|
||||
if (alias.value().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Query query = null;
|
||||
if (!alias.filter().value().isEmpty()) {
|
||||
query = new StringQuery(alias.filter().value());
|
||||
}
|
||||
|
||||
aliases.add(
|
||||
Alias.builder(alias.value())
|
||||
.withFilter(query)
|
||||
.withIndexRouting(alias.indexRouting())
|
||||
.withSearchRouting(alias.searchRouting())
|
||||
.withRouting(alias.routing())
|
||||
.withHidden(alias.isHidden())
|
||||
.withWriteIndex(alias.isWriteIndex())
|
||||
.build()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,11 +16,14 @@
|
||||
package org.springframework.data.elasticsearch.core.index;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.data.elasticsearch.annotations.FieldType.Text;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.assertj.core.api.SoftAssertions;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@ -30,13 +33,18 @@ import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.elasticsearch.annotations.Alias;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.annotations.Filter;
|
||||
import org.springframework.data.elasticsearch.annotations.Mapping;
|
||||
import org.springframework.data.elasticsearch.annotations.Setting;
|
||||
import org.springframework.data.elasticsearch.client.elc.Queries;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.IndexInformation;
|
||||
import org.springframework.data.elasticsearch.core.IndexOperations;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.StringQuery;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
|
||||
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
||||
import org.springframework.lang.Nullable;
|
||||
@ -171,6 +179,29 @@ public abstract class IndexOperationsIntegrationTests {
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateIndexWithAliases() {
|
||||
// Given
|
||||
indexNameProvider.increment();
|
||||
String indexName = indexNameProvider.indexName();
|
||||
indexOperations = operations.indexOps(EntityWithAliases.class);
|
||||
indexOperations.createWithMapping();
|
||||
|
||||
// When
|
||||
Map<String, Set<AliasData>> aliases = indexOperations.getAliasesForIndex(indexName);
|
||||
|
||||
// Then
|
||||
AliasData result = aliases.values().stream().findFirst().orElse(new HashSet<>()).stream().findFirst().orElse(null);
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.getAlias()).isEqualTo("first_alias");
|
||||
assertThat(result.getFilterQuery()).asInstanceOf(InstanceOfAssertFactories.type(StringQuery.class))
|
||||
.extracting(StringQuery::getSource)
|
||||
.asString()
|
||||
.contains(Queries.wrapperQuery("""
|
||||
{"bool" : {"must" : {"term" : {"type" : "abc"}}}}
|
||||
""").query());
|
||||
}
|
||||
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
@Setting(settingPath = "settings/test-settings.json")
|
||||
@Mapping(mappingPath = "mappings/test-mappings.json")
|
||||
@ -186,4 +217,31 @@ public abstract class IndexOperationsIntegrationTests {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}", aliases = {
|
||||
@Alias(value = "first_alias", filter =@Filter("""
|
||||
{"bool" : {"must" : {"term" : {"type" : "abc"}}}}
|
||||
"""))
|
||||
})
|
||||
private static class EntityWithAliases {
|
||||
@Nullable private @Id String id;
|
||||
@Field(type = Text) private String type;
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(@Nullable String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,12 +17,19 @@ package org.springframework.data.elasticsearch.core.index;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.skyscreamer.jsonassert.JSONAssert.*;
|
||||
import static org.springframework.data.elasticsearch.annotations.FieldType.Text;
|
||||
import static org.springframework.data.elasticsearch.core.IndexOperationsAdapter.*;
|
||||
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.springframework.data.elasticsearch.annotations.Alias;
|
||||
import org.springframework.data.elasticsearch.annotations.Filter;
|
||||
import org.springframework.data.elasticsearch.client.elc.Queries;
|
||||
import org.springframework.data.elasticsearch.core.query.StringQuery;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.json.JSONException;
|
||||
@ -346,6 +353,33 @@ public abstract class ReactiveIndexOperationsIntegrationTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateIndexWithAliases() {
|
||||
// Given
|
||||
indexNameProvider.increment();
|
||||
String indexName = indexNameProvider.indexName();
|
||||
indexOperations = operations.indexOps(EntityWithAliases.class);
|
||||
blocking(indexOperations).createWithMapping();
|
||||
|
||||
// When
|
||||
|
||||
// Then
|
||||
indexOperations.getAliasesForIndex(indexName)
|
||||
.as(StepVerifier::create)
|
||||
.assertNext(aliases -> {
|
||||
AliasData result = aliases.values().stream().findFirst().orElse(new HashSet<>()).stream().findFirst().orElse(null);
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.getAlias()).isEqualTo("first_alias");
|
||||
assertThat(result.getFilterQuery()).asInstanceOf(InstanceOfAssertFactories.type(StringQuery.class))
|
||||
.extracting(StringQuery::getSource)
|
||||
.asString()
|
||||
.contains(Queries.wrapperQuery("""
|
||||
{"bool" : {"must" : {"term" : {"type" : "abc"}}}}
|
||||
""").query());
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
@Setting(shards = 3, replicas = 2, refreshInterval = "4s")
|
||||
static class Entity {
|
||||
@ -401,4 +435,31 @@ public abstract class ReactiveIndexOperationsIntegrationTests {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}", aliases = {
|
||||
@Alias(value = "first_alias", filter =@Filter("""
|
||||
{"bool" : {"must" : {"term" : {"type" : "abc"}}}}
|
||||
"""))
|
||||
})
|
||||
private static class EntityWithAliases {
|
||||
@Nullable private @Id String id;
|
||||
@Field(type = Text) private String type;
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(@Nullable String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user