Skip to content

Commit

Permalink
Query: Adds Computed Property SDK Support (#3761)
Browse files Browse the repository at this point in the history
* Initial commit

* Restored settings.json changes.

* Update

* Addressed comments; still need to be tested using Emulator.

* Fixes after test run.

* Ignored the computed property tests based on the sync this morning (to allow for preview release).

* Suite0 fixes.

* Test update.

* Suite0 fixes
  • Loading branch information
adityasa authored May 4, 2023
1 parent 9ba3f75 commit 17203a5
Show file tree
Hide file tree
Showing 11 changed files with 1,162 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Fluent
{
using System;
using System.Collections.ObjectModel;

/// <summary>
/// Computed Properties fluent definition.
/// </summary>
/// <seealso cref="ComputedProperty"/>
#if PREVIEW
public
#else
internal
#endif
class ComputedPropertiesDefinition<T>
{
private readonly Collection<ComputedProperty> computedProperties = new Collection<ComputedProperty>();
private readonly T parent;
private readonly Action<Collection<ComputedProperty>> attachCallback;

internal ComputedPropertiesDefinition(
T parent,
Action<Collection<ComputedProperty>> attachCallback)
{
this.parent = parent;
this.attachCallback = attachCallback;
}

/// <summary>
/// Adds a computed property to the current <see cref="ComputedPropertiesDefinition{T}"/>
/// </summary>
/// <param name="name">Name of the computed property</param>
/// <param name="query">Query for the computed property values</param>
/// <returns>An instance of the current <see cref="ComputedPropertiesDefinition{T}"/></returns>
public ComputedPropertiesDefinition<T> WithComputedProperty(string name, string query)
{
this.computedProperties.Add(
new ComputedProperty
{
Name = name,
Query = query
});

return this;
}

/// <summary>
/// Applies the current definition to the parent.
/// </summary>
/// <returns>An instance of the parent.</returns>
public T Attach()
{
this.attachCallback(this.computedProperties);
return this.parent;
}
}
}
35 changes: 35 additions & 0 deletions Microsoft.Azure.Cosmos/src/Fluent/Settings/ContainerDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
namespace Microsoft.Azure.Cosmos.Fluent
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

/// <summary>
/// Azure Cosmos container fluent definition.
Expand All @@ -18,6 +20,7 @@ public abstract class ContainerDefinition<T>
private IndexingPolicy indexingPolicy;
private string timeToLivePropertyPath;
private PartitionKeyDefinitionVersion? partitionKeyDefinitionVersion = null;
private Collection<ComputedProperty> computedProperties;

/// <summary>
/// Creates an instance for unit-testing
Expand Down Expand Up @@ -123,6 +126,28 @@ public IndexingPolicyDefinition<T> WithIndexingPolicy()
(indexingPolicy) => this.WithIndexingPolicy(indexingPolicy));
}

/// <summary>
/// <see cref="Cosmos.ComputedProperty"/> definition for Azure Cosmos container.
/// </summary>
/// <returns>An instance of <see cref="ComputedPropertiesDefinition{T}"/>.</returns>
#if PREVIEW
public
#else
internal
#endif
ComputedPropertiesDefinition<T> WithComputedProperties()
{
if (this.computedProperties != null)
{
// Overwrite
throw new NotSupportedException();
}

return new ComputedPropertiesDefinition<T>(
(T)this,
(computedProperties) => this.WithComputedProperties(computedProperties));
}

/// <summary>
/// Applies the current Fluent definition and creates a container configuration.
/// </summary>
Expand Down Expand Up @@ -152,6 +177,11 @@ public ContainerProperties Build()
containerProperties.PartitionKeyDefinitionVersion = this.partitionKeyDefinitionVersion.Value;
}

if (this.computedProperties != null)
{
containerProperties.ComputedProperties = this.computedProperties;
}

containerProperties.ValidateRequiredProperties();

return containerProperties;
Expand All @@ -161,5 +191,10 @@ private void WithIndexingPolicy(IndexingPolicy indexingPolicy)
{
this.indexingPolicy = indexingPolicy;
}

private void WithComputedProperties(Collection<ComputedProperty> computedProperties)
{
this.computedProperties = computedProperties;
}
}
}
53 changes: 53 additions & 0 deletions Microsoft.Azure.Cosmos/src/Resource/Settings/ComputedProperty.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------

namespace Microsoft.Azure.Cosmos
{
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

/// <summary>
/// Represents a computed property definition in a Cosmos DB collection.
/// </summary>
#if PREVIEW
public
#else
internal
#endif
sealed class ComputedProperty
{
/// <summary>
/// Gets or sets the name of the computed property.
/// </summary>
/// <value>
/// The name of the computed property.
/// </value>
/// <remarks>
/// Name of the computed property should be chosen such that it does not collide with any existing or future document properties.
/// </remarks>
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }

/// <summary>
/// Gets or sets the query for the computed property.
/// </summary>
/// <value>
/// The query used to evaluate the value for the computed property.
/// </value>
/// <remarks>
/// For example:
/// SELECT VALUE LOWER(c.firstName) FROM c
/// </remarks>
[JsonProperty(PropertyName = "query")]
public string Query { get; set; }

/// <summary>
/// This contains additional values for scenarios where the SDK is not aware of new fields.
/// This ensures that if resource is read and updated none of the fields will be lost in the process.
/// </summary>
[JsonExtensionData]
internal IDictionary<string, JToken> AdditionalProperties { get; private set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ public class ContainerProperties
[JsonProperty(PropertyName = "clientEncryptionPolicy", NullValueHandling = NullValueHandling.Ignore)]
private ClientEncryptionPolicy clientEncryptionPolicyInternal;

[JsonProperty(PropertyName = "computedProperties", NullValueHandling = NullValueHandling.Ignore)]
private Collection<ComputedProperty> computedProperties;

/// <summary>
/// This contains additional values for scenarios where the SDK is not aware of new fields.
/// This ensures that if resource is read and updated none of the fields will be lost in the process.
Expand Down Expand Up @@ -286,6 +289,48 @@ public IndexingPolicy IndexingPolicy
}
}

/// <summary>
/// Gets or sets the collection containing <see cref="ComputedProperty"/> objects in the container.
/// </summary>
/// <value>
/// The collection containing <see cref="ComputedProperty"/> objects associated with the container.
/// </value>

/// <summary>
/// Gets or sets the collection containing <see cref="ComputedProperty"/> objects in the container.
/// </summary>
/// <value>
/// The collection containing <see cref="ComputedProperty"/> objects associated with the container.
/// </value>
[JsonIgnore]
#if PREVIEW
public
#else
internal
#endif
Collection<ComputedProperty> ComputedProperties
{
get
{
if (this.computedProperties == null)
{
this.computedProperties = new Collection<ComputedProperty>();
}

return this.computedProperties;
}

set
{
if (value == null)
{
throw new ArgumentException($"{nameof(value)}");
}

this.computedProperties = value;
}
}

/// <summary>
/// Gets the <see cref="ChangeFeedPolicy"/> associated with the container from the Azure Cosmos DB service.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
namespace Microsoft.Azure.Cosmos
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using Microsoft.VisualStudio.TestTools.UnitTesting;

class ComputedPropertyComparer : IEqualityComparer<ComputedProperty>
{
public static void AssertAreEqual(Collection<ComputedProperty> expected, Collection<ComputedProperty> actual)
{
int expectedCount = expected?.Count ?? 0;
int actualCount = actual?.Count ?? 0;
Assert.AreEqual(expectedCount, actualCount);

for (int i = 0; i < expectedCount; i++)
{
AssertAreEqual(expected[i], actual[i]);
}
}

public static void AssertAreEqual(ComputedProperty expected, ComputedProperty actual)
{
ComputedPropertyComparer comparer = new ComputedPropertyComparer();
Assert.IsTrue(comparer.Equals(expected, actual), $"Expected: {ToString(expected)}{Environment.NewLine}Actual:{ToString(actual)}");
}

private static string ToString(ComputedProperty computedProperty) => $@"""Name"":""{computedProperty.Name}"", ""Query"":""{computedProperty.Query}""";

public bool Equals(ComputedProperty x, ComputedProperty y)
{
if (x == null) return y == null;
if (y == null) return false;

return (x.Name?.Equals(y.Name) == true) &&
(x.Query?.Equals(y.Query) == true);
}

public int GetHashCode([DisallowNull] ComputedProperty obj)
{
return obj.GetHashCode();
}
}
}
Loading

0 comments on commit 17203a5

Please sign in to comment.