Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
}
}
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;
}
}
}
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