diff --git a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/rx/MaterializedViewTest.java b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/rx/MaterializedViewTest.java new file mode 100644 index 000000000000..76fd2d10500c --- /dev/null +++ b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/rx/MaterializedViewTest.java @@ -0,0 +1,224 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.cosmos.rx; + +import com.azure.cosmos.implementation.DocumentCollection; +import com.azure.cosmos.implementation.ImplementationBridgeHelpers; +import com.azure.cosmos.implementation.Utils; +import com.azure.cosmos.models.CosmosMaterializedView; +import com.azure.cosmos.models.CosmosMaterializedViewDefinition; +import com.azure.cosmos.models.CosmosContainerProperties; +import com.azure.cosmos.models.ModelBridgeInternal; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.testng.annotations.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class MaterializedViewTest { + + protected static final int TIMEOUT = 30000; + + private final ObjectMapper simpleObjectMapper = Utils.getSimpleObjectMapper(); + + private static final ImplementationBridgeHelpers + .CosmosContainerPropertiesHelper + .CosmosContainerPropertiesAccessor containerPropertiesAccessor = + ImplementationBridgeHelpers + .CosmosContainerPropertiesHelper + .getCosmosContainerPropertiesAccessor(); + + /** Helper: create CosmosContainerProperties from a raw JSON string (simulates server response). */ + private CosmosContainerProperties fromJson(String json) { + return containerPropertiesAccessor.create(new DocumentCollection(json)); + } + + // ------------------------------------------------------------------------- + // CosmosMaterializedViewDefinition – getter/setter tests + // ------------------------------------------------------------------------- + + @Test(groups = {"unit"}, timeOut = TIMEOUT) + public void containerProperties_setAndGetMaterializedViewDefinition() { + CosmosContainerProperties containerProperties = + new CosmosContainerProperties("testContainer", "/pk"); + + CosmosMaterializedViewDefinition definition = new CosmosMaterializedViewDefinition() + .setSourceCollectionId("gsi-src") + .setDefinition("SELECT c.customerId, c.emailAddress FROM c"); + + containerProperties.setMaterializedViewDefinition(definition); + + CosmosMaterializedViewDefinition retrieved = containerProperties.getMaterializedViewDefinition(); + assertThat(retrieved).isNotNull(); + assertThat(retrieved.getSourceCollectionId()).isEqualTo("gsi-src"); + assertThat(retrieved.getDefinition()).isEqualTo("SELECT c.customerId, c.emailAddress FROM c"); + } + + @Test(groups = {"unit"}, timeOut = TIMEOUT) + public void containerProperties_setMaterializedViewDefinition_nullThrows() { + CosmosContainerProperties containerProperties = + new CosmosContainerProperties("testContainer", "/pk"); + + assertThatThrownBy(() -> containerProperties.setMaterializedViewDefinition(null)) + .isInstanceOf(NullPointerException.class) + .hasMessageContaining("cosmosMaterializedViewDefinition cannot be null"); + } + + @Test(groups = {"unit"}, timeOut = TIMEOUT) + public void containerProperties_setMaterializedViewDefinition_returnsThis() { + CosmosContainerProperties containerProperties = + new CosmosContainerProperties("testContainer", "/pk"); + + CosmosMaterializedViewDefinition definition = new CosmosMaterializedViewDefinition() + .setSourceCollectionId("gsi-src"); + + assertThat(containerProperties.setMaterializedViewDefinition(definition)) + .isSameAs(containerProperties); + } + + // ------------------------------------------------------------------------- + // Serialization / Deserialization round-trip + // ------------------------------------------------------------------------- + + @Test(groups = {"unit"}, timeOut = TIMEOUT) + public void materializedViewDefinition_serializesToJson() throws Exception { + CosmosContainerProperties containerProperties = + new CosmosContainerProperties("testContainer", "/pk"); + + CosmosMaterializedViewDefinition definition = new CosmosMaterializedViewDefinition() + .setSourceCollectionId("gsi-src") + .setDefinition("SELECT c.customerId, c.emailAddress FROM c"); + + // Simulate the RID resolution that CosmosAsyncDatabase performs during createContainer + ModelBridgeInternal.setMaterializedViewDefinitionSourceCollectionRid(definition, "TughAMEOdUI="); + + containerProperties.setMaterializedViewDefinition(definition); + + // Serialize via DocumentCollection.toJson() which calls populatePropertyBag() + String json = ModelBridgeInternal.getResource(containerProperties).toJson(); + + ObjectNode jsonNode = (ObjectNode) simpleObjectMapper.readTree(json); + ObjectNode mvDefNode = (ObjectNode) jsonNode.get("materializedViewDefinition"); + + assertThat(mvDefNode).isNotNull(); + assertThat(mvDefNode.get("sourceCollectionRid").asText()).isEqualTo("TughAMEOdUI="); + assertThat(mvDefNode.get("definition").asText()) + .isEqualTo("SELECT c.customerId, c.emailAddress FROM c"); + } + + @Test(groups = {"unit"}, timeOut = TIMEOUT) + public void materializedViewDefinition_deserializesFromJson() { + String json = "{" + + "\"id\":\"testContainer\"," + + "\"partitionKey\":{\"paths\":[\"/pk\"],\"kind\":\"Hash\"}," + + "\"materializedViewDefinition\":{" + + "\"sourceCollectionId\":\"gsi-src\"," + + "\"sourceCollectionRid\":\"TughAMEOdUI=\"," + + "\"definition\":\"SELECT c.customerId, c.emailAddress FROM c\"" + + "}" + + "}"; + + CosmosContainerProperties containerProperties = fromJson(json); + + CosmosMaterializedViewDefinition definition = containerProperties.getMaterializedViewDefinition(); + assertThat(definition).isNotNull(); + assertThat(definition.getSourceCollectionId()).isEqualTo("gsi-src"); + assertThat(definition.getDefinition()).isEqualTo("SELECT c.customerId, c.emailAddress FROM c"); + assertThat(definition.getSourceCollectionRid()).isEqualTo("TughAMEOdUI="); + assertThat(definition.getStatus()).isNull(); + } + + @Test(groups = {"unit"}, timeOut = TIMEOUT) + public void materializedViewDefinition_deserializesStatusFromJson() { + String json = "{" + + "\"id\":\"testContainer\"," + + "\"partitionKey\":{\"paths\":[\"/pk\"],\"kind\":\"Hash\"}," + + "\"materializedViewDefinition\":{" + + "\"sourceCollectionRid\":\"TughAMEOdUI=\"," + + "\"definition\":\"SELECT c.customerId, c.emailAddress FROM c\"," + + "\"status\":\"Initialized\"" + + "}" + + "}"; + + CosmosContainerProperties containerProperties = fromJson(json); + + CosmosMaterializedViewDefinition definition = containerProperties.getMaterializedViewDefinition(); + assertThat(definition).isNotNull(); + assertThat(definition.getSourceCollectionRid()).isEqualTo("TughAMEOdUI="); + assertThat(definition.getStatus()).isEqualTo("Initialized"); + } + + @Test(groups = {"unit"}, timeOut = TIMEOUT) + public void materializedViewDefinition_fullRoundTrip() throws Exception { + // Set on container properties using the new public API + CosmosContainerProperties original = new CosmosContainerProperties("testContainer", "/pk"); + CosmosMaterializedViewDefinition definition = new CosmosMaterializedViewDefinition() + .setSourceCollectionId("gsi-src") + .setDefinition("SELECT c.customerId, c.emailAddress FROM c"); + + // Simulate the RID resolution that CosmosAsyncDatabase performs during createContainer + ModelBridgeInternal.setMaterializedViewDefinitionSourceCollectionRid(definition, "TughAMEOdUI="); + + original.setMaterializedViewDefinition(definition); + + // Serialize via DocumentCollection.toJson() + String json = ModelBridgeInternal.getResource(original).toJson(); + + // Deserialize back using the same path as server responses + CosmosContainerProperties deserialized = fromJson(json); + + CosmosMaterializedViewDefinition deserializedDef = deserialized.getMaterializedViewDefinition(); + assertThat(deserializedDef).isNotNull(); + assertThat(deserializedDef.getSourceCollectionId()).isEqualTo("gsi-src"); + assertThat(deserializedDef.getDefinition()).isEqualTo("SELECT c.customerId, c.emailAddress FROM c"); + + // Verify the RID round-tripped correctly through the wire format + ObjectNode jsonNode = (ObjectNode) simpleObjectMapper.readTree(json); + ObjectNode mvDefNode = (ObjectNode) jsonNode.get("materializedViewDefinition"); + assertThat(mvDefNode.get("sourceCollectionRid").asText()).isEqualTo("TughAMEOdUI="); + } + + + // ------------------------------------------------------------------------- + // getMaterializedViews – read-only list from server response + // ------------------------------------------------------------------------- + + @Test(groups = {"unit"}, timeOut = TIMEOUT) + public void getMaterializedViews_returnsEmptyListWhenAbsent() { + CosmosContainerProperties containerProperties = + new CosmosContainerProperties("testContainer", "/pk"); + + List views = containerProperties.getMaterializedViews(); + assertThat(views).isNotNull(); + assertThat(views).isEmpty(); + } + + @Test(groups = {"unit"}, timeOut = TIMEOUT) + public void getMaterializedViews_deserializesFromServerResponse() { + String json = "{" + + "\"id\":\"src-container\"," + + "\"partitionKey\":{\"paths\":[\"/pk\"],\"kind\":\"Hash\"}," + + "\"materializedViews\":[" + + "{\"id\":\"gsi_testcontainer1\",\"_rid\":\"TughAMEOdUI=\"}," + + "{\"id\":\"gsi_testcontainer2\",\"_rid\":\"AbcdEFGhIJk=\"}" + + "]" + + "}"; + + CosmosContainerProperties containerProperties = fromJson(json); + + List views = containerProperties.getMaterializedViews(); + assertThat(views).isNotNull(); + assertThat(views).hasSize(2); + + assertThat(views.get(0).getId()).isEqualTo("gsi_testcontainer1"); + assertThat(views.get(0).getResourceId()).isEqualTo("TughAMEOdUI="); + + assertThat(views.get(1).getId()).isEqualTo("gsi_testcontainer2"); + assertThat(views.get(1).getResourceId()).isEqualTo("AbcdEFGhIJk="); + } + +} diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosAsyncDatabase.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosAsyncDatabase.java index ce4de922f157..79785434e9a6 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosAsyncDatabase.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosAsyncDatabase.java @@ -22,6 +22,7 @@ import com.azure.cosmos.models.CosmosContainerResponse; import com.azure.cosmos.models.CosmosDatabaseRequestOptions; import com.azure.cosmos.models.CosmosDatabaseResponse; +import com.azure.cosmos.models.CosmosMaterializedViewDefinition; import com.azure.cosmos.models.CosmosQueryRequestOptions; import com.azure.cosmos.models.CosmosUserProperties; import com.azure.cosmos.models.CosmosUserResponse; @@ -1395,10 +1396,26 @@ private Mono createContainerInternal( String spanName = "createContainer." + containerProperties.getId(); RequestOptions nonNullRequestOptions = options != null ? ModelBridgeInternal.toRequestOptions(options) : new RequestOptions(); - Mono responseMono = getDocClientWrapper() - .createCollection(this.getLink(), ModelBridgeInternal.getV2Collection(containerProperties), - nonNullRequestOptions) - .map(ModelBridgeInternal::createCosmosContainerResponse).single(); + + CosmosMaterializedViewDefinition mvDefinition = containerProperties.getMaterializedViewDefinition(); + Mono ridResolution; + if (mvDefinition != null && mvDefinition.getSourceCollectionId() != null) { + ridResolution = this.getContainer(mvDefinition.getSourceCollectionId()) + .read() + .flatMap(sourceContainerResponse -> { + String rid = sourceContainerResponse.getProperties().getResourceId(); + ModelBridgeInternal.setMaterializedViewDefinitionSourceCollectionRid(mvDefinition, rid); + return Mono.empty(); + }); + } else { + ridResolution = Mono.empty(); + } + + Mono responseMono = ridResolution + .then(Mono.defer(() -> getDocClientWrapper() + .createCollection(this.getLink(), ModelBridgeInternal.getV2Collection(containerProperties), + nonNullRequestOptions) + .map(ModelBridgeInternal::createCosmosContainerResponse).single())); return this.client.getDiagnosticsProvider().traceEnabledCosmosResponsePublisher( responseMono, diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/Constants.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/Constants.java index 75b63c8a956d..7c411dc3ce36 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/Constants.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/Constants.java @@ -156,6 +156,14 @@ public static final class Properties { public static final String VECTOR_INDEXING_SEARCH_LIST_SIZE = "indexingSearchListSize"; public static final String VECTOR_INDEX_SHARD_KEYS = "vectorIndexShardKeys"; + // Materialized View Definition + public static final String MATERIALIZED_VIEW_DEFINITION = "materializedViewDefinition"; + public static final String MATERIALIZED_VIEW_SOURCE_COLLECTION_ID = "sourceCollectionId"; + public static final String MATERIALIZED_VIEW_SOURCE_COLLECTION_RID = "sourceCollectionRid"; + public static final String MATERIALIZED_VIEW_QUERY_DEFINITION = "definition"; + public static final String MATERIALIZED_VIEW_STATUS = "status"; + public static final String MATERIALIZED_VIEWS = "materializedViews"; + // Unique index. public static final String UNIQUE_KEY_POLICY = "uniqueKeyPolicy"; public static final String UNIQUE_KEYS = "uniqueKeys"; diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/DocumentCollection.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/DocumentCollection.java index 50d903ebfa7a..9aa1ae2b096b 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/DocumentCollection.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/DocumentCollection.java @@ -9,6 +9,8 @@ import com.azure.cosmos.models.ClientEncryptionPolicy; import com.azure.cosmos.models.ComputedProperty; import com.azure.cosmos.models.ConflictResolutionPolicy; +import com.azure.cosmos.models.CosmosMaterializedViewDefinition; +import com.azure.cosmos.models.CosmosMaterializedView; import com.azure.cosmos.models.CosmosFullTextPolicy; import com.azure.cosmos.models.CosmosVectorEmbeddingPolicy; import com.azure.cosmos.models.IndexingPolicy; @@ -24,6 +26,7 @@ import java.io.ObjectOutputStream; import java.util.Collection; import java.util.Collections; +import java.util.List; import static com.azure.cosmos.implementation.guava25.base.Preconditions.checkNotNull; @@ -45,6 +48,7 @@ public final class DocumentCollection extends Resource { private ClientEncryptionPolicy clientEncryptionPolicyInternal; private CosmosVectorEmbeddingPolicy cosmosVectorEmbeddingPolicy; private CosmosFullTextPolicy cosmosFullTextPolicy; + private CosmosMaterializedViewDefinition cosmosMaterializedViewDefinition; /** * Constructor. @@ -469,6 +473,48 @@ public void setFullTextPolicy(CosmosFullTextPolicy value) { this.set(Constants.Properties.FULL_TEXT_POLICY, value); } + /** + * Gets the materialized view definition for this container in the Azure Cosmos DB service. + * + * @return the CosmosMaterializedViewDefinition + */ + public CosmosMaterializedViewDefinition getMaterializedViewDefinition() { + if (this.cosmosMaterializedViewDefinition == null) { + if (super.has(Constants.Properties.MATERIALIZED_VIEW_DEFINITION)) { + this.cosmosMaterializedViewDefinition = super.getObject( + Constants.Properties.MATERIALIZED_VIEW_DEFINITION, + CosmosMaterializedViewDefinition.class); + } + } + return this.cosmosMaterializedViewDefinition; + } + + /** + * Sets the materialized view definition for this container in the Azure Cosmos DB service. + * + * @param value the CosmosMaterializedViewDefinition + */ + public void setMaterializedViewDefinition(CosmosMaterializedViewDefinition value) { + checkNotNull(value, "cosmosMaterializedViewDefinition cannot be null"); + this.cosmosMaterializedViewDefinition = value; + this.set(Constants.Properties.MATERIALIZED_VIEW_DEFINITION, value); + } + + /** + * Gets the read-only list of materialized views derived from this container. + * This property is populated only when reading a container response from the Azure Cosmos DB service. + * + * @return the list of {@link CosmosMaterializedView}, or an empty list if none are present. + */ + public List getMaterializedViews() { + List results = + super.getList(Constants.Properties.MATERIALIZED_VIEWS, CosmosMaterializedView.class); + if (results == null) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(results); + } + public void populatePropertyBag() { super.populatePropertyBag(); if (this.indexingPolicy == null) { diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/JsonSerializable.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/JsonSerializable.java index c06d8a6bc1ed..9410cae88fd1 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/JsonSerializable.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/JsonSerializable.java @@ -13,6 +13,7 @@ import com.azure.cosmos.models.ChangeFeedPolicy; import com.azure.cosmos.models.CompositePath; import com.azure.cosmos.models.ConflictResolutionPolicy; +import com.azure.cosmos.models.CosmosMaterializedViewDefinition; import com.azure.cosmos.models.ExcludedPath; import com.azure.cosmos.models.IncludedPath; import com.azure.cosmos.models.IndexingPolicy; @@ -815,7 +816,8 @@ static boolean containsJsonSerializable(Class c) { || SqlParameter.class.equals(c) || SqlQuerySpec.class.equals(c) || UniqueKey.class.equals(c) - || UniqueKeyPolicy.class.equals(c); + || UniqueKeyPolicy.class.equals(c) + || CosmosMaterializedViewDefinition.class.equals(c); } @Override diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/CosmosContainerProperties.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/CosmosContainerProperties.java index f24579861b6b..98e0a1174470 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/CosmosContainerProperties.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/CosmosContainerProperties.java @@ -393,6 +393,51 @@ public CosmosContainerProperties setFullTextPolicy(CosmosFullTextPolicy value) { return this; } + /** + * Gets the materialized view definition for this container in the Azure Cosmos DB service. + * A materialized view is derived from a source container and is defined by a SQL-like query. + * + * @return the CosmosMaterializedViewDefinition + */ + public CosmosMaterializedViewDefinition getMaterializedViewDefinition() { + return this.documentCollection.getMaterializedViewDefinition(); + } + + /** + * Sets the materialized view definition for this container in the Azure Cosmos DB service. + * A materialized view is derived from a source container and is defined by a SQL-like query. + *

+ * Example: + *

{@code
+     * CosmosMaterializedViewDefinition mvDef = new CosmosMaterializedViewDefinition()
+     *     .setSourceCollectionId("gsi-src")
+     *     .setDefinition("SELECT c.customerId, c.emailAddress FROM c");
+     * containerProperties.setMaterializedViewDefinition(mvDef);
+     * }
+ * + * @param value the CosmosMaterializedViewDefinition to be used. + * @return the CosmosContainerProperties. + */ + public CosmosContainerProperties setMaterializedViewDefinition(CosmosMaterializedViewDefinition value) { + this.documentCollection.setMaterializedViewDefinition(value); + return this; + } + + /** + * Gets the read-only list of materialized view containers derived from this container. + * This property is populated only when reading a container response from the Azure Cosmos DB service. + *

+ * Example JSON representation in the response: + *

{@code
+     * "materializedViews": [{ "id": "gsi_testcontainer1", "_rid": "TughAMEOdUI=" }]
+     * }
+ * + * @return the list of {@link CosmosMaterializedView}, or an empty list if none are present. + */ + public List getMaterializedViews() { + return this.documentCollection.getMaterializedViews(); + } + Resource getResource() { return this.documentCollection; } diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/CosmosMaterializedView.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/CosmosMaterializedView.java new file mode 100644 index 000000000000..d989cefd1a15 --- /dev/null +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/CosmosMaterializedView.java @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.cosmos.models; + +import com.azure.cosmos.implementation.Constants; +import com.azure.cosmos.implementation.Utils; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; + +/** + * Represents an entry in the read-only {@code materializedViews} list returned from the Azure Cosmos DB service + * when reading a container. Each entry identifies a materialized view container derived from the source container. + *

+ * Example JSON representation: + *

{@code
+ * "materializedViews": [{ "id": "gsi_testcontainer1", "_rid": "TughAMEOdUI=" }]
+ * }
+ */ +public final class CosmosMaterializedView { + + @JsonProperty(Constants.Properties.ID) + private String id; + + @JsonProperty(Constants.Properties.R_ID) + private String resourceId; + + /** + * Constructor + */ + public CosmosMaterializedView() { + } + + /** + * Gets the id of the materialized view container. + * + * @return the id of the materialized view container. + */ + public String getId() { + return id; + } + + /** + * Gets the resource id (_rid) of the materialized view container. + * + * @return the resource id of the materialized view container. + */ + public String getResourceId() { + return resourceId; + } + + @Override + public String toString() { + try { + return Utils.getSimpleObjectMapper().writeValueAsString(this); + } catch (JsonProcessingException e) { + throw new IllegalStateException("Unable to convert object to string", e); + } + } +} diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/CosmosMaterializedViewDefinition.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/CosmosMaterializedViewDefinition.java new file mode 100644 index 000000000000..e74a1e07fac6 --- /dev/null +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/CosmosMaterializedViewDefinition.java @@ -0,0 +1,125 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.cosmos.models; + +import com.azure.cosmos.implementation.Constants; +import com.azure.cosmos.implementation.JsonSerializable; +import com.azure.cosmos.implementation.Utils; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * Represents the materialized view definition for a container in the Azure Cosmos DB service. + * A materialized view is derived from a source container and is defined by a SQL-like query. + *

+ * Example: + *

{@code
+ * CosmosMaterializedViewDefinition definition = new CosmosMaterializedViewDefinition()
+ *     .setSourceCollectionId("gsi-src")
+ *     .setDefinition("SELECT c.customerId, c.emailAddress FROM c");
+ * }
+ */ +public final class CosmosMaterializedViewDefinition { + + private final JsonSerializable jsonSerializable; + + /** + * Constructor + */ + public CosmosMaterializedViewDefinition() { + this.jsonSerializable = new JsonSerializable(); + } + + /** + * Constructor. + * + * @param objectNode the {@link ObjectNode} that represents the materialized view definition. + */ + CosmosMaterializedViewDefinition(ObjectNode objectNode) { + this.jsonSerializable = new JsonSerializable(objectNode); + } + + /** + * Gets the source collection id for the materialized view. + * + * @return the source collection id. + */ + public String getSourceCollectionId() { + return this.jsonSerializable.getString(Constants.Properties.MATERIALIZED_VIEW_SOURCE_COLLECTION_ID); + } + + /** + * Sets the source collection id for the materialized view. + * The SDK will automatically resolve this collection id to its resource id (RID) + * during container creation. + * + * @param sourceCollectionId the source collection id. + * @return CosmosMaterializedViewDefinition + */ + public CosmosMaterializedViewDefinition setSourceCollectionId(String sourceCollectionId) { + this.jsonSerializable.set(Constants.Properties.MATERIALIZED_VIEW_SOURCE_COLLECTION_ID, sourceCollectionId); + return this; + } + + void setSourceCollectionRidInternal(String sourceCollectionRid) { + this.jsonSerializable.set(Constants.Properties.MATERIALIZED_VIEW_SOURCE_COLLECTION_RID, sourceCollectionRid); + } + + /** + * Gets the source collection resource id (RID) for the materialized view as returned by the server. + * This is a read-only field populated from server responses. + * + * @return the source collection resource id. + */ + public String getSourceCollectionRid() { + return this.jsonSerializable.getString(Constants.Properties.MATERIALIZED_VIEW_SOURCE_COLLECTION_RID); + } + + /** + * Gets the build status of the materialized view as returned by the server. + * This is a read-only field populated from server responses. + * + * @return the materialized view build status. + */ + public String getStatus() { + return this.jsonSerializable.getString(Constants.Properties.MATERIALIZED_VIEW_STATUS); + } + + /** + * Gets the query definition for the materialized view. + * + * @return the query definition. + */ + public String getDefinition() { + return this.jsonSerializable.getString(Constants.Properties.MATERIALIZED_VIEW_QUERY_DEFINITION); + } + + /** + * Sets the query definition for the materialized view. + * + * @param definition the query definition (e.g. {@code "SELECT c.customerId, c.emailAddress FROM c"}). + * @return CosmosMaterializedViewDefinition + */ + public CosmosMaterializedViewDefinition setDefinition(String definition) { + this.jsonSerializable.set(Constants.Properties.MATERIALIZED_VIEW_QUERY_DEFINITION, definition); + return this; + } + + void populatePropertyBag() { + this.jsonSerializable.populatePropertyBag(); + } + + JsonSerializable getJsonSerializable() { + return this.jsonSerializable; + } + + @Override + public String toString() { + try { + return Utils.getSimpleObjectMapper().writeValueAsString(this); + } catch (JsonProcessingException e) { + throw new IllegalStateException("Unable to convert object to string", e); + } + } +} diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/ModelBridgeInternal.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/ModelBridgeInternal.java index e698e0664c41..6d6c3973a32d 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/ModelBridgeInternal.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/ModelBridgeInternal.java @@ -135,6 +135,12 @@ public static DocumentCollection getV2Collection(CosmosContainerProperties conta return containerProperties.getV2Collection(); } + @Warning(value = INTERNAL_USE_ONLY_WARNING) + public static void setMaterializedViewDefinitionSourceCollectionRid( + CosmosMaterializedViewDefinition definition, String sourceCollectionRid) { + definition.setSourceCollectionRidInternal(sourceCollectionRid); + } + @Warning(value = INTERNAL_USE_ONLY_WARNING) public static List getCosmosContainerPropertiesFromV2Results(List results) { return CosmosContainerProperties.getFromV2Results(results); @@ -451,6 +457,8 @@ public static void populatePropertyBag(T t) { ((UniqueKey) t).populatePropertyBag(); } else if (t instanceof UniqueKeyPolicy) { ((UniqueKeyPolicy) t).populatePropertyBag(); + } else if (t instanceof CosmosMaterializedViewDefinition) { + ((CosmosMaterializedViewDefinition) t).populatePropertyBag(); } else { throw new IllegalArgumentException("populatePropertyBag method does not exists in class " + t.getClass()); } @@ -488,6 +496,8 @@ public static JsonSerializable getJsonSerializable(T t) { return ((UniqueKey) t).getJsonSerializable(); } else if (t instanceof UniqueKeyPolicy) { return ((UniqueKeyPolicy) t).getJsonSerializable(); + } else if (t instanceof CosmosMaterializedViewDefinition) { + return ((CosmosMaterializedViewDefinition) t).getJsonSerializable(); } else { throw new IllegalArgumentException("getJsonSerializable method does not exists in class " + t.getClass()); }