Skip to content

Commit f463b5a

Browse files
check if index policy has changed on repo initialization and update if needed (Azure#17985)
* check if index policy has changed on repo initialization and update if needed * undo changes to DocumentCollection. CosmosContainerReplace test case to have stronger assertions. Fix checkstyle issues * fix CosmosContainerTest and add integration test for updating index on startup * add reactive support * fix checkstyle issues. Add headers to new files * fix logic for determining if index policy has changed. implement equals in necessary classes. Add more test cases * add missing header. remove unneeded repos * remove unused autowire * add missing javadoc and fi style issue * remove unused autowired client * make class fully static
1 parent ccdcf2d commit f463b5a

File tree

15 files changed

+499
-7
lines changed

15 files changed

+499
-7
lines changed

sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/ExcludedPath.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import com.azure.cosmos.implementation.JsonSerializable;
88
import com.fasterxml.jackson.databind.node.ObjectNode;
99

10+
import java.util.Objects;
11+
1012
/**
1113
* Represents an excluded path of the IndexingPolicy in the Azure Cosmos DB database service.
1214
*/
@@ -57,4 +59,19 @@ void populatePropertyBag() {
5759
}
5860

5961
JsonSerializable getJsonSerializable() { return this.jsonSerializable; }
62+
63+
@Override
64+
public boolean equals(Object o) {
65+
if (this == o) return true;
66+
67+
if (o == null || getClass() != o.getClass()) return false;
68+
69+
ExcludedPath that = (ExcludedPath) o;
70+
return Objects.equals(jsonSerializable, that.jsonSerializable);
71+
}
72+
73+
@Override
74+
public int hashCode() {
75+
return Objects.hash(jsonSerializable);
76+
}
6077
}

sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/IncludedPath.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.util.ArrayList;
1919
import java.util.List;
20+
import java.util.Objects;
2021

2122
/**
2223
* Represents an included path of the IndexingPolicy in the Azure Cosmos DB database service.
@@ -135,4 +136,19 @@ void populatePropertyBag() {
135136
}
136137

137138
JsonSerializable getJsonSerializable() { return this.jsonSerializable; }
139+
140+
@Override
141+
public boolean equals(Object o) {
142+
if (this == o) return true;
143+
144+
if (o == null || getClass() != o.getClass()) return false;
145+
146+
IncludedPath that = (IncludedPath) o;
147+
return Objects.equals(jsonSerializable, that.jsonSerializable);
148+
}
149+
150+
@Override
151+
public int hashCode() {
152+
return Objects.hash(jsonSerializable);
153+
}
138154
}

sdk/cosmos/azure-cosmos/src/test/java/com/azure/cosmos/CosmosContainerTest.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,6 @@ public void deleteContainer_withOptions() throws Exception {
223223

224224
@Test(groups = { "emulator" }, timeOut = TIMEOUT)
225225
public void replace() throws Exception {
226-
227226
String collectionName = UUID.randomUUID().toString();
228227
CosmosContainerProperties containerProperties = getCollectionDefinition(collectionName);
229228
CosmosContainerRequestOptions options = new CosmosContainerRequestOptions();
@@ -235,17 +234,20 @@ public void replace() throws Exception {
235234

236235
CosmosContainerResponse replaceResponse = createdDatabase.getContainer(containerProperties.getId())
237236
.replace(containerResponse.getProperties().setIndexingPolicy(
238-
new IndexingPolicy().setIndexingMode(IndexingMode.CONSISTENT)));
237+
new IndexingPolicy().setAutomatic(false).setIndexingMode(IndexingMode.NONE)));
239238
assertThat(replaceResponse.getProperties().getIndexingPolicy().getIndexingMode())
240-
.isEqualTo(IndexingMode.CONSISTENT);
239+
.isEqualTo(IndexingMode.NONE);
240+
assertThat(replaceResponse.getProperties().getIndexingPolicy().isAutomatic())
241+
.isEqualTo(false);
241242

242-
CosmosContainerResponse replaceResponse1 = createdDatabase.getContainer(containerProperties.getId())
243+
replaceResponse = createdDatabase.getContainer(containerProperties.getId())
243244
.replace(containerResponse.getProperties().setIndexingPolicy(
244-
new IndexingPolicy().setIndexingMode(IndexingMode.CONSISTENT)),
245+
new IndexingPolicy().setAutomatic(true).setIndexingMode(IndexingMode.CONSISTENT)),
245246
options);
246-
assertThat(replaceResponse1.getProperties().getIndexingPolicy().getIndexingMode())
247+
assertThat(replaceResponse.getProperties().getIndexingPolicy().getIndexingMode())
247248
.isEqualTo(IndexingMode.CONSISTENT);
248-
249+
assertThat(replaceResponse.getProperties().getIndexingPolicy().isAutomatic())
250+
.isEqualTo(true);
249251
}
250252

251253
@Test(groups = { "emulator" }, timeOut = TIMEOUT)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
package com.azure.spring.data.cosmos.domain;
4+
5+
import com.azure.spring.data.cosmos.core.mapping.Container;
6+
import com.azure.spring.data.cosmos.core.mapping.CosmosIndexingPolicy;
7+
import org.springframework.data.annotation.Id;
8+
9+
@Container
10+
@CosmosIndexingPolicy(includePaths = {"/field/?"}, excludePaths = {"/*", "/\"_etag\"/?"})
11+
public class ComplexIndexPolicyEntity {
12+
13+
@Id
14+
String id;
15+
16+
String field;
17+
18+
public String getId() {
19+
return id;
20+
}
21+
22+
public void setId(String id) {
23+
this.id = id;
24+
}
25+
26+
public String getField() {
27+
return field;
28+
}
29+
30+
public void setField(String field) {
31+
this.field = field;
32+
}
33+
34+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
package com.azure.spring.data.cosmos.domain;
4+
5+
import com.azure.spring.data.cosmos.core.mapping.Container;
6+
import com.azure.spring.data.cosmos.core.mapping.CosmosIndexingPolicy;
7+
import org.springframework.data.annotation.Id;
8+
9+
@Container
10+
@CosmosIndexingPolicy
11+
public class IndexPolicyEntity {
12+
13+
@Id
14+
String id;
15+
16+
String field;
17+
18+
public String getId() {
19+
return id;
20+
}
21+
22+
public void setId(String id) {
23+
this.id = id;
24+
}
25+
26+
public String getField() {
27+
return field;
28+
}
29+
30+
public void setField(String field) {
31+
this.field = field;
32+
}
33+
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
package com.azure.spring.data.cosmos.repository.integration;
4+
5+
import com.azure.cosmos.models.CosmosContainerProperties;
6+
import com.azure.cosmos.models.ExcludedPath;
7+
import com.azure.cosmos.models.IncludedPath;
8+
import com.azure.cosmos.models.IndexingMode;
9+
import com.azure.cosmos.models.IndexingPolicy;
10+
import com.azure.spring.data.cosmos.core.CosmosTemplate;
11+
import com.azure.spring.data.cosmos.domain.ComplexIndexPolicyEntity;
12+
import com.azure.spring.data.cosmos.domain.IndexPolicyEntity;
13+
import com.azure.spring.data.cosmos.repository.TestRepositoryConfig;
14+
import com.azure.spring.data.cosmos.repository.support.CosmosEntityInformation;
15+
import com.azure.spring.data.cosmos.repository.support.SimpleCosmosRepository;
16+
import org.junit.Test;
17+
import org.junit.runner.RunWith;
18+
import org.mockito.Mockito;
19+
import org.springframework.beans.factory.annotation.Autowired;
20+
import org.springframework.context.ApplicationContext;
21+
import org.springframework.test.context.ContextConfiguration;
22+
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
23+
24+
import java.util.Collections;
25+
26+
import static org.assertj.core.api.Assertions.assertThat;
27+
28+
@RunWith(SpringJUnit4ClassRunner.class)
29+
@ContextConfiguration(classes = TestRepositoryConfig.class)
30+
public class IndexPolicyUpdateIT {
31+
32+
@Autowired
33+
CosmosTemplate template;
34+
35+
@Autowired
36+
ApplicationContext context;
37+
38+
CosmosEntityInformation<IndexPolicyEntity, String> defaultIndexPolicyEntityInformation = new CosmosEntityInformation<>(IndexPolicyEntity.class);
39+
40+
CosmosEntityInformation<ComplexIndexPolicyEntity, String> complexIndexPolicyEntityInformation = new CosmosEntityInformation<>(ComplexIndexPolicyEntity.class);
41+
42+
@Test
43+
public void testIndexPolicyUpdatesOnRepoInitialization() {
44+
// set index policy from entity annotation
45+
new SimpleCosmosRepository<>(defaultIndexPolicyEntityInformation, template);
46+
47+
// get original index policy
48+
CosmosContainerProperties properties = template.getContainerProperties(defaultIndexPolicyEntityInformation.getContainerName());
49+
50+
// assert
51+
assertThat(properties.getIndexingPolicy().getIncludedPaths().size()).isEqualTo(1);
52+
assertThat(properties.getIndexingPolicy().getIncludedPaths().get(0).getPath()).isEqualTo("/*");
53+
assertThat(properties.getIndexingPolicy().getExcludedPaths().size()).isEqualTo(1);
54+
assertThat(properties.getIndexingPolicy().getExcludedPaths().get(0).getPath()).isEqualTo("/\"_etag\"/?");
55+
assertThat(properties.getIndexingPolicy().isAutomatic()).isEqualTo(true);
56+
assertThat(properties.getIndexingPolicy().getIndexingMode()).isEqualTo(IndexingMode.CONSISTENT);
57+
58+
// set new index policy
59+
IndexingPolicy newIndexPolicy = new IndexingPolicy();
60+
newIndexPolicy.setIncludedPaths(Collections.singletonList(new IncludedPath("/field/?")));
61+
newIndexPolicy.setExcludedPaths(Collections.singletonList(new ExcludedPath("/*")));
62+
63+
// apply new index policy
64+
CosmosEntityInformation<IndexPolicyEntity, String> spyEntityInformation = Mockito.spy(defaultIndexPolicyEntityInformation);
65+
Mockito.doReturn(newIndexPolicy).when(spyEntityInformation).getIndexingPolicy();
66+
new SimpleCosmosRepository<>(spyEntityInformation, template);
67+
68+
// retrieve updated index policy
69+
properties = template.getContainerProperties(defaultIndexPolicyEntityInformation.getContainerName());
70+
71+
// assert
72+
assertThat(properties.getIndexingPolicy().getIncludedPaths().size()).isEqualTo(1);
73+
assertThat(properties.getIndexingPolicy().getIncludedPaths().get(0).getPath()).isEqualTo("/field/?");
74+
assertThat(properties.getIndexingPolicy().getExcludedPaths().size()).isEqualTo(2);
75+
assertThat(properties.getIndexingPolicy().getExcludedPaths().get(0).getPath()).isEqualTo("/*");
76+
assertThat(properties.getIndexingPolicy().getExcludedPaths().get(1).getPath()).isEqualTo("/\"_etag\"/?");
77+
assertThat(properties.getIndexingPolicy().isAutomatic()).isEqualTo(true);
78+
assertThat(properties.getIndexingPolicy().getIndexingMode()).isEqualTo(IndexingMode.CONSISTENT);
79+
}
80+
81+
@Test
82+
public void testContainerReplaceShouldNotOccurIfDefaultIndexIsUnchanged() {
83+
new SimpleCosmosRepository<>(defaultIndexPolicyEntityInformation, template);
84+
CosmosTemplate spyTemplate = Mockito.spy(template);
85+
new SimpleCosmosRepository<>(defaultIndexPolicyEntityInformation, spyTemplate);
86+
Mockito.verify(spyTemplate, Mockito.never()).replaceContainerProperties(Mockito.any(), Mockito.any());
87+
}
88+
89+
@Test
90+
public void testContainerReplaceShouldNotOccurIfComplexIndexIsUnchanged() {
91+
new SimpleCosmosRepository<>(complexIndexPolicyEntityInformation, template);
92+
CosmosTemplate spyTemplate = Mockito.spy(template);
93+
new SimpleCosmosRepository<>(complexIndexPolicyEntityInformation, spyTemplate);
94+
Mockito.verify(spyTemplate, Mockito.never()).replaceContainerProperties(Mockito.any(), Mockito.any());
95+
}
96+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
package com.azure.spring.data.cosmos.repository.integration;
4+
5+
import com.azure.cosmos.models.CosmosContainerProperties;
6+
import com.azure.cosmos.models.ExcludedPath;
7+
import com.azure.cosmos.models.IncludedPath;
8+
import com.azure.cosmos.models.IndexingMode;
9+
import com.azure.cosmos.models.IndexingPolicy;
10+
import com.azure.spring.data.cosmos.core.ReactiveCosmosTemplate;
11+
import com.azure.spring.data.cosmos.domain.ComplexIndexPolicyEntity;
12+
import com.azure.spring.data.cosmos.domain.IndexPolicyEntity;
13+
import com.azure.spring.data.cosmos.repository.TestRepositoryConfig;
14+
import com.azure.spring.data.cosmos.repository.support.CosmosEntityInformation;
15+
import com.azure.spring.data.cosmos.repository.support.SimpleReactiveCosmosRepository;
16+
import org.junit.Test;
17+
import org.junit.runner.RunWith;
18+
import org.mockito.Mockito;
19+
import org.springframework.beans.factory.annotation.Autowired;
20+
import org.springframework.context.ApplicationContext;
21+
import org.springframework.test.context.ContextConfiguration;
22+
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
23+
24+
import java.util.Collections;
25+
26+
import static org.assertj.core.api.Assertions.assertThat;
27+
28+
@RunWith(SpringJUnit4ClassRunner.class)
29+
@ContextConfiguration(classes = TestRepositoryConfig.class)
30+
public class ReactiveIndexPolicyUpdateIT {
31+
32+
@Autowired
33+
ReactiveCosmosTemplate template;
34+
35+
@Autowired
36+
ApplicationContext context;
37+
38+
CosmosEntityInformation<IndexPolicyEntity, String> defaultIndexPolicyEntityInformation = new CosmosEntityInformation<>(IndexPolicyEntity.class);
39+
40+
CosmosEntityInformation<ComplexIndexPolicyEntity, String> complexIndexPolicyEntityInformation = new CosmosEntityInformation<>(ComplexIndexPolicyEntity.class);
41+
42+
@Test
43+
public void testIndexPolicyUpdatesOnRepoInitialization() {
44+
// set index policy based on entity annotation
45+
new SimpleReactiveCosmosRepository<>(defaultIndexPolicyEntityInformation, template);
46+
47+
// get original index policy
48+
CosmosContainerProperties properties = template.getContainerProperties(defaultIndexPolicyEntityInformation.getContainerName()).block();
49+
50+
// assert
51+
assertThat(properties.getIndexingPolicy().getIncludedPaths().size()).isEqualTo(1);
52+
assertThat(properties.getIndexingPolicy().getIncludedPaths().get(0).getPath()).isEqualTo("/*");
53+
assertThat(properties.getIndexingPolicy().getExcludedPaths().size()).isEqualTo(1);
54+
assertThat(properties.getIndexingPolicy().getExcludedPaths().get(0).getPath()).isEqualTo("/\"_etag\"/?");
55+
assertThat(properties.getIndexingPolicy().isAutomatic()).isEqualTo(true);
56+
assertThat(properties.getIndexingPolicy().getIndexingMode()).isEqualTo(IndexingMode.CONSISTENT);
57+
58+
// set new index policy
59+
IndexingPolicy newIndexPolicy = new IndexingPolicy();
60+
newIndexPolicy.setIncludedPaths(Collections.singletonList(new IncludedPath("/field/?")));
61+
newIndexPolicy.setExcludedPaths(Collections.singletonList(new ExcludedPath("/*")));
62+
63+
// apply new index policy
64+
CosmosEntityInformation<IndexPolicyEntity, String> spyEntityInformation = Mockito.spy(defaultIndexPolicyEntityInformation);
65+
Mockito.doReturn(newIndexPolicy).when(spyEntityInformation).getIndexingPolicy();
66+
new SimpleReactiveCosmosRepository<>(spyEntityInformation, template);
67+
68+
// retrieve updated index policy
69+
properties = template.getContainerProperties(defaultIndexPolicyEntityInformation.getContainerName()).block();
70+
71+
// assert
72+
assertThat(properties.getIndexingPolicy().getIncludedPaths().size()).isEqualTo(1);
73+
assertThat(properties.getIndexingPolicy().getIncludedPaths().get(0).getPath()).isEqualTo("/field/?");
74+
assertThat(properties.getIndexingPolicy().getExcludedPaths().size()).isEqualTo(2);
75+
assertThat(properties.getIndexingPolicy().getExcludedPaths().get(0).getPath()).isEqualTo("/*");
76+
assertThat(properties.getIndexingPolicy().getExcludedPaths().get(1).getPath()).isEqualTo("/\"_etag\"/?");
77+
assertThat(properties.getIndexingPolicy().isAutomatic()).isEqualTo(true);
78+
assertThat(properties.getIndexingPolicy().getIndexingMode()).isEqualTo(IndexingMode.CONSISTENT);
79+
}
80+
81+
@Test
82+
public void testContainerReplaceShouldNotOccurIfIndexIsUnchanged() {
83+
new SimpleReactiveCosmosRepository<>(defaultIndexPolicyEntityInformation, template);
84+
ReactiveCosmosTemplate spyTemplate = Mockito.spy(template);
85+
new SimpleReactiveCosmosRepository<>(defaultIndexPolicyEntityInformation, spyTemplate);
86+
Mockito.verify(spyTemplate, Mockito.never()).replaceContainerProperties(Mockito.any(), Mockito.any());
87+
}
88+
89+
@Test
90+
public void testContainerReplaceShouldNotOccurIfComplexIndexIsUnchanged() {
91+
new SimpleReactiveCosmosRepository<>(complexIndexPolicyEntityInformation, template);
92+
ReactiveCosmosTemplate spyTemplate = Mockito.spy(template);
93+
new SimpleReactiveCosmosRepository<>(complexIndexPolicyEntityInformation, spyTemplate);
94+
Mockito.verify(spyTemplate, Mockito.never()).replaceContainerProperties(Mockito.any(), Mockito.any());
95+
}
96+
97+
}

sdk/cosmos/azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/core/CosmosOperations.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,23 @@ public interface CosmosOperations {
3333
*/
3434
CosmosContainerProperties createContainerIfNotExists(CosmosEntityInformation<?, ?> information);
3535

36+
/**
37+
* Get properties for specified container
38+
*
39+
* @param containerName String
40+
* @return CosmosContainerProperties
41+
*/
42+
CosmosContainerProperties getContainerProperties(String containerName);
43+
44+
/**
45+
* Replace container properties for the specified container
46+
*
47+
* @param containerName String
48+
* @param properties CosmosContainerProperties
49+
* @return CosmosContainerProperties
50+
*/
51+
CosmosContainerProperties replaceContainerProperties(String containerName, CosmosContainerProperties properties);
52+
3653
/**
3754
* Find the DocumentQuery, find all the items specified by domain type.
3855
*

0 commit comments

Comments
 (0)