Microsoft.Azure.Cosmos/src/Resource/Settings/IndexingPolicy.cs (382 lines of code) (raw):

//------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ namespace Microsoft.Azure.Cosmos { using System; using System.Collections.Generic; using System.Collections.ObjectModel; using Microsoft.Azure.Documents; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; /// <summary> /// Represents the indexing policy configuration for a collection in the Azure Cosmos DB service. /// </summary> /// <remarks> /// Indexing policies can used to configure which properties (JSON paths) are included/excluded, whether the index is updated consistently /// or offline (lazy), automatic vs. opt-in per-document, as well as the precision and type of index per path. /// <para> /// Refer to https://docs.microsoft.com/azure/cosmos-db/index-policy for additional information on how to specify /// indexing policies. /// </para> /// </remarks> /// <seealso cref="ContainerProperties"/> public sealed class IndexingPolicy { internal const string DefaultPath = "/*"; /// <summary> /// Initializes a new instance of the <see cref="IndexingPolicy"/> class for the Azure Cosmos DB service. /// </summary> /// <remarks> /// Indexing mode is set to consistent. /// </remarks> public IndexingPolicy() { this.Automatic = true; this.IndexingMode = IndexingMode.Consistent; } /// <summary> /// Gets or sets a value that indicates whether automatic indexing is enabled for a collection in the Azure Cosmos DB service. /// </summary> /// <remarks> /// In automatic indexing, documents can be explicitly excluded from indexing using <see cref="T:Microsoft.Azure.Documents.Client.RequestOptions"/>. /// In manual indexing, documents can be explicitly included. /// </remarks> /// <value> /// True, if automatic indexing is enabled; otherwise, false. /// </value> [JsonProperty(PropertyName = Constants.Properties.Automatic)] public bool Automatic { get; set; } /// <summary> /// Gets or sets the indexing mode (consistent or lazy) in the Azure Cosmos DB service. /// </summary> /// <value> /// One of the values of the <see cref="T:Microsoft.Azure.Cosmos.IndexingMode"/> enumeration. /// </value> [JsonProperty(PropertyName = Constants.Properties.IndexingMode)] [JsonConverter(typeof(StringEnumConverter))] public IndexingMode IndexingMode { get; set; } /// <summary> /// Gets the collection containing <see cref="IncludedPath"/> objects in the Azure Cosmos DB service. /// </summary> /// <value> /// The collection containing <see cref="IncludedPath"/> objects. /// </value> [JsonProperty(PropertyName = Constants.Properties.IncludedPaths)] public Collection<IncludedPath> IncludedPaths { get; internal set; } = new Collection<IncludedPath>(); /// <summary> /// Gets the collection containing <see cref="ExcludedPath"/> objects in the Azure Cosmos DB service. /// </summary> /// <value> /// The collection containing <see cref="ExcludedPath"/> objects. /// </value> [JsonProperty(PropertyName = Constants.Properties.ExcludedPaths)] public Collection<ExcludedPath> ExcludedPaths { get; internal set; } = new Collection<ExcludedPath>(); /// <summary> /// Gets the composite indexes for additional indexes /// </summary> /// <example> /// <![CDATA[ /// "composite": [ /// [ /// { /// "path": "/joining_year", /// "order": "ascending" /// }, /// { /// "path": "/level", /// "order": "descending" /// } /// ], /// [ /// { /// "path": "/country" /// }, /// { /// "path": "/city" /// } /// ] /// ] /// ]]> /// </example> [JsonProperty(PropertyName = Constants.Properties.CompositeIndexes)] public Collection<Collection<CompositePath>> CompositeIndexes { get; internal set; } = new Collection<Collection<CompositePath>>(); /// <summary> /// Collection of spatial index definitions to be used /// </summary> [JsonProperty(PropertyName = Constants.Properties.SpatialIndexes)] public Collection<SpatialPath> SpatialIndexes { get; internal set; } = new Collection<SpatialPath>(); /// <summary> /// Gets the vector indexes for additional indexes /// </summary> /// <example> /// <![CDATA[ /// "vectorIndexes": [ /// { /// "path": "/vector1", /// "type": "diskANN" /// }, /// { /// "path": "/vector2", /// "type": "flat " /// }, /// { /// "path": "/vector3", /// "type": "quantizedFlat" /// } /// ] /// ]]> /// </example> [JsonProperty(PropertyName = "vectorIndexes", NullValueHandling = NullValueHandling.Ignore)] public Collection<VectorIndexPath> VectorIndexes { get; set; } = new Collection<VectorIndexPath>(); /// <summary> /// Gets the full text indexes /// </summary> /// <example> /// <![CDATA[ /// "fullTextIndexes": [ /// { /// "path": "/v1", /// }, /// { /// "path": "/v2", /// }, /// { /// "path": "/v3", /// } /// ] /// ]]> /// </example> [JsonProperty(PropertyName = "fullTextIndexes", NullValueHandling = NullValueHandling.Ignore)] public Collection<FullTextIndexPath> FullTextIndexes{ get; set; } = new Collection<FullTextIndexPath>(); /// <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; } #if INTERNAL /// <summary> /// Indexing policy annotation. /// </summary> [JsonProperty(PropertyName = "annotation", NullValueHandling = NullValueHandling.Ignore)] public string Annotation { get; set; } #endif #region EqualityComparers internal sealed class CompositePathEqualityComparer : IEqualityComparer<CompositePath> { public static readonly CompositePathEqualityComparer Singleton = new CompositePathEqualityComparer(); public bool Equals(CompositePath compositePath1, CompositePath compositePath2) { if (Object.ReferenceEquals(compositePath1, compositePath2)) { return true; } if (compositePath1 == null || compositePath2 == null) { return false; } if (compositePath1.Path == compositePath2.Path && compositePath1.Order == compositePath2.Order && compositePath1.AdditionalProperties.EqualsTo(compositePath2.AdditionalProperties)) { return true; } return false; } public int GetHashCode(CompositePath compositePath) { if (compositePath == null) { return 0; } return compositePath.Path.GetHashCode() ^ compositePath.Order.GetHashCode(); } } internal sealed class CompositePathsEqualityComparer : IEqualityComparer<HashSet<CompositePath>> { public static readonly CompositePathsEqualityComparer Singleton = new CompositePathsEqualityComparer(); private static readonly CompositePathEqualityComparer compositePathEqualityComparer = new CompositePathEqualityComparer(); public bool Equals(HashSet<CompositePath> compositePaths1, HashSet<CompositePath> compositePaths2) { if (Object.ReferenceEquals(compositePaths1, compositePaths2)) { return true; } if (compositePaths1 == null || compositePaths2 == null) { return false; } return compositePaths1.SetEquals(compositePaths2); } public int GetHashCode(HashSet<CompositePath> obj) { if (obj == null) { return 0; } int hashCode = 0; foreach (CompositePath compositePath in obj) { hashCode ^= compositePathEqualityComparer.GetHashCode(compositePath); } return hashCode; } } private sealed class CompositeIndexesEqualityComparer : IEqualityComparer<Collection<Collection<CompositePath>>> { private static readonly CompositePathEqualityComparer compositePathEqualityComparer = new CompositePathEqualityComparer(); private static readonly CompositePathsEqualityComparer compositePathsEqualityComparer = new CompositePathsEqualityComparer(); public bool Equals(Collection<Collection<CompositePath>> compositeIndexes1, Collection<Collection<CompositePath>> compositeIndexes2) { if (Object.ReferenceEquals(compositeIndexes1, compositeIndexes2)) { return true; } if (compositeIndexes1 == null || compositeIndexes2 == null) { return false; } HashSet<HashSet<CompositePath>> hashedCompositeIndexes1 = new HashSet<HashSet<CompositePath>>(compositePathsEqualityComparer); HashSet<HashSet<CompositePath>> hashedCompositeIndexes2 = new HashSet<HashSet<CompositePath>>(compositePathsEqualityComparer); foreach (Collection<CompositePath> compositePaths in compositeIndexes1) { HashSet<CompositePath> hashedCompositePaths = new HashSet<CompositePath>(compositePaths, compositePathEqualityComparer); hashedCompositeIndexes1.Add(hashedCompositePaths); } foreach (Collection<CompositePath> compositePaths in compositeIndexes2) { HashSet<CompositePath> hashedCompositePaths = new HashSet<CompositePath>(compositePaths, compositePathEqualityComparer); hashedCompositeIndexes2.Add(hashedCompositePaths); } return hashedCompositeIndexes1.SetEquals(hashedCompositeIndexes2); } public int GetHashCode(Collection<Collection<CompositePath>> compositeIndexes) { int hashCode = 0; foreach (Collection<CompositePath> compositePaths in compositeIndexes) { HashSet<CompositePath> hashedCompositePaths = new HashSet<CompositePath>(compositePaths, compositePathEqualityComparer); hashCode ^= compositePathsEqualityComparer.GetHashCode(hashedCompositePaths); } return hashCode; } } internal sealed class SpatialSpecEqualityComparer : IEqualityComparer<SpatialPath> { public static readonly SpatialSpecEqualityComparer Singleton = new SpatialSpecEqualityComparer(); public bool Equals(SpatialPath spatialSpec1, SpatialPath spatialSpec2) { if (object.ReferenceEquals(spatialSpec1, spatialSpec2)) { return true; } if (spatialSpec1 == null || spatialSpec2 == null) { return false; } if (spatialSpec1.Path != spatialSpec2.Path) { return false; } HashSet<SpatialType> hashedSpatialTypes1 = new HashSet<SpatialType>(spatialSpec1.SpatialTypes); HashSet<SpatialType> hashedSpatialTypes2 = new HashSet<SpatialType>(spatialSpec2.SpatialTypes); if (!hashedSpatialTypes1.SetEquals(hashedSpatialTypes2) || !spatialSpec1.AdditionalProperties.EqualsTo(spatialSpec2.AdditionalProperties)) { return false; } return true; } public int GetHashCode(SpatialPath spatialSpec) { int hashCode = 0; hashCode ^= spatialSpec.Path.GetHashCode(); foreach (SpatialType spatialType in spatialSpec.SpatialTypes) { hashCode ^= spatialType.GetHashCode(); } return hashCode; } } internal sealed class AdditionalSpatialIndexesEqualityComparer : IEqualityComparer<Collection<SpatialPath>> { private static readonly SpatialSpecEqualityComparer spatialSpecEqualityComparer = new SpatialSpecEqualityComparer(); public bool Equals(Collection<SpatialPath> additionalSpatialIndexes1, Collection<SpatialPath> additionalSpatialIndexes2) { if (object.ReferenceEquals(additionalSpatialIndexes1, additionalSpatialIndexes2)) { return true; } if (additionalSpatialIndexes1 == null || additionalSpatialIndexes2 == null) { return false; } HashSet<SpatialPath> hashedAdditionalSpatialIndexes1 = new HashSet<SpatialPath>(additionalSpatialIndexes1, spatialSpecEqualityComparer); HashSet<SpatialPath> hashedAdditionalSpatialIndexes2 = new HashSet<SpatialPath>(additionalSpatialIndexes2, spatialSpecEqualityComparer); return hashedAdditionalSpatialIndexes1.SetEquals(hashedAdditionalSpatialIndexes2); } public int GetHashCode(Collection<SpatialPath> additionalSpatialIndexes) { int hashCode = 0; foreach (SpatialPath spatialSpec in additionalSpatialIndexes) { hashCode ^= spatialSpecEqualityComparer.GetHashCode(spatialSpec); } return hashCode; } } internal sealed class IndexEqualityComparer : IEqualityComparer<Cosmos.Index> { public static readonly IndexEqualityComparer Comparer = new IndexEqualityComparer(); public bool Equals(Index index1, Index index2) { if (object.ReferenceEquals(index1, index2)) { return true; } if (index1 == null || index2 == null) { return false; } if (index1.Kind != index2.Kind || !index1.AdditionalProperties.EqualsTo(index2.AdditionalProperties)) { return false; } switch (index1.Kind) { case IndexKind.Hash: if (((HashIndex)index1).Precision != ((HashIndex)index2).Precision) { return false; } if (((HashIndex)index1).DataType != ((HashIndex)index2).DataType) { return false; } break; case IndexKind.Range: if (((RangeIndex)index1).Precision != ((RangeIndex)index2).Precision) { return false; } if (((RangeIndex)index1).DataType != ((RangeIndex)index2).DataType) { return false; } break; case IndexKind.Spatial: if (((SpatialIndex)index1).DataType != ((SpatialIndex)index2).DataType) { return false; } break; default: throw new ArgumentException($"Unexpected Kind: {index1.Kind}"); } return true; } public int GetHashCode(Index index) { int hashCode = 0; hashCode ^= (int)index.Kind; switch (index.Kind) { case IndexKind.Hash: hashCode = hashCode ^ ((HashIndex)index).Precision ?? 0; hashCode ^= ((HashIndex)index).DataType.GetHashCode(); break; case IndexKind.Range: hashCode = hashCode ^ ((RangeIndex)index).Precision ?? 0; hashCode ^= ((RangeIndex)index).DataType.GetHashCode(); break; case IndexKind.Spatial: hashCode ^= ((SpatialIndex)index).DataType.GetHashCode(); break; default: throw new ArgumentException($"Unexpected Kind: {index.Kind}"); } return hashCode; } } internal sealed class IncludedPathEqualityComparer : IEqualityComparer<IncludedPath> { public static readonly IncludedPathEqualityComparer Singleton = new IncludedPathEqualityComparer(); private static readonly IndexEqualityComparer indexEqualityComparer = new IndexEqualityComparer(); public bool Equals(IncludedPath includedPath1, IncludedPath includedPath2) { if (Object.ReferenceEquals(includedPath1, includedPath2)) { return true; } if (includedPath1 == null || includedPath2 == null) { return false; } if (includedPath1.Path != includedPath2.Path || !includedPath1.AdditionalProperties.EqualsTo(includedPath2.AdditionalProperties)) { return false; } HashSet<Cosmos.Index> indexes1 = new HashSet<Cosmos.Index>(includedPath1.Indexes, indexEqualityComparer); HashSet<Cosmos.Index> indexes2 = new HashSet<Cosmos.Index>(includedPath2.Indexes, indexEqualityComparer); return indexes1.SetEquals(indexes2); } public int GetHashCode(IncludedPath includedPath) { int hashCode = 0; hashCode ^= includedPath.Path.GetHashCode(); foreach (Index index in includedPath.Indexes) { hashCode ^= indexEqualityComparer.GetHashCode(index); } return hashCode; } } internal sealed class ExcludedPathEqualityComparer : IEqualityComparer<ExcludedPath> { public static readonly ExcludedPathEqualityComparer Singleton = new ExcludedPathEqualityComparer(); public bool Equals(ExcludedPath excludedPath1, ExcludedPath excludedPath2) { if (Object.ReferenceEquals(excludedPath1, excludedPath2)) { return true; } if (excludedPath1 == null || excludedPath2 == null) { return false; } return excludedPath1.Path == excludedPath2.Path && excludedPath1.AdditionalProperties.EqualsTo(excludedPath2.AdditionalProperties); } public int GetHashCode(ExcludedPath excludedPath1) { return excludedPath1.Path.GetHashCode(); } } internal sealed class IndexingPolicyEqualityComparer : IEqualityComparer<IndexingPolicy> { public static readonly IndexingPolicyEqualityComparer Singleton = new IndexingPolicyEqualityComparer(); private static readonly IncludedPathEqualityComparer includedPathEqualityComparer = new IncludedPathEqualityComparer(); private static readonly ExcludedPathEqualityComparer excludedPathEqualityComparer = new ExcludedPathEqualityComparer(); private static readonly CompositeIndexesEqualityComparer compositeIndexesEqualityComparer = new CompositeIndexesEqualityComparer(); private static readonly AdditionalSpatialIndexesEqualityComparer additionalSpatialIndexesEqualityComparer = new AdditionalSpatialIndexesEqualityComparer(); public bool Equals(IndexingPolicy indexingPolicy1, IndexingPolicy indexingPolicy2) { if (Object.ReferenceEquals(indexingPolicy1, indexingPolicy2)) { return true; } bool isEqual = true; isEqual &= indexingPolicy1 != null && indexingPolicy2 != null; isEqual &= indexingPolicy1.Automatic == indexingPolicy2.Automatic; isEqual &= indexingPolicy1.IndexingMode == indexingPolicy2.IndexingMode; isEqual &= compositeIndexesEqualityComparer.Equals(indexingPolicy1.CompositeIndexes, indexingPolicy2.CompositeIndexes); isEqual &= additionalSpatialIndexesEqualityComparer.Equals(indexingPolicy1.SpatialIndexes, indexingPolicy2.SpatialIndexes); isEqual &= indexingPolicy1.AdditionalProperties.EqualsTo(indexingPolicy2.AdditionalProperties); HashSet<IncludedPath> includedPaths1 = new HashSet<IncludedPath>(indexingPolicy1.IncludedPaths, includedPathEqualityComparer); HashSet<IncludedPath> includedPaths2 = new HashSet<IncludedPath>(indexingPolicy2.IncludedPaths, includedPathEqualityComparer); isEqual &= includedPaths1.SetEquals(includedPaths2); HashSet<ExcludedPath> excludedPaths1 = new HashSet<ExcludedPath>(indexingPolicy1.ExcludedPaths, excludedPathEqualityComparer); HashSet<ExcludedPath> excludedPaths2 = new HashSet<ExcludedPath>(indexingPolicy2.ExcludedPaths, excludedPathEqualityComparer); isEqual &= excludedPaths1.SetEquals(excludedPaths2); return isEqual; } public int GetHashCode(IndexingPolicy indexingPolicy) { int hashCode = 0; hashCode ^= indexingPolicy.Automatic.GetHashCode(); hashCode ^= indexingPolicy.IndexingMode.GetHashCode(); hashCode ^= compositeIndexesEqualityComparer.GetHashCode(indexingPolicy.CompositeIndexes); hashCode ^= additionalSpatialIndexesEqualityComparer.GetHashCode(indexingPolicy.SpatialIndexes); foreach (IncludedPath includedPath in indexingPolicy.IncludedPaths) { hashCode ^= includedPathEqualityComparer.GetHashCode(includedPath); } foreach (ExcludedPath excludedPath in indexingPolicy.ExcludedPaths) { hashCode ^= excludedPathEqualityComparer.GetHashCode(excludedPath); } return hashCode; } } #endregion } }