Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hierarchical Partitioning #407

Draft
wants to merge 32 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
84e9155
Allow compilation fresh clone
xInfinitYz Jan 30, 2024
a8d9451
Merge pull request #1 from xInfinitYz/Core/MakeCompile
xInfinitYz Jan 30, 2024
1737b45
Added PartitionKeyLists
xInfinitYz Feb 3, 2024
0f5f81f
Adding interface hierarchical strategy logic
xInfinitYz Feb 5, 2024
9459538
Save changes
xInfinitYz Feb 6, 2024
4f7e3cd
- Checkpoint default repository
xInfinitYz Feb 10, 2024
3957c20
Save changes
xInfinitYz Feb 10, 2024
5ac5762
- Checkpoint current tests working
xInfinitYz Feb 11, 2024
6818255
Merge branch 'main' into Feature/Hierarchical-PartitionKeys
xInfinitYz Feb 14, 2024
f84d54b
Merge pull request #2 from xInfinitYz/Feature/Hierarchical-PartitionKeys
xInfinitYz Feb 14, 2024
7a49505
Update src/Microsoft.Azure.CosmosRepository/Builders/ContainerOptions…
xInfinitYz Feb 15, 2024
1af9058
Update src/Microsoft.Azure.CosmosRepository/Providers/DefaultCosmosPa…
xInfinitYz Feb 15, 2024
cd2048a
Update src/Microsoft.Azure.CosmosRepository/Providers/ICosmosPartitio…
xInfinitYz Feb 15, 2024
3da89ac
Update src/Microsoft.Azure.CosmosRepository/Repositories/DefaultRepos…
xInfinitYz Feb 15, 2024
9ca24b4
Update src/Microsoft.Azure.CosmosRepository/Repositories/DefaultRepos…
xInfinitYz Feb 15, 2024
84ccff2
Update src/Microsoft.Azure.CosmosRepository/Repositories/DefaultRepos…
xInfinitYz Feb 15, 2024
7d6a3d8
Update src/Microsoft.Azure.CosmosRepository/Repositories/DefaultRepos…
xInfinitYz Feb 15, 2024
8e4780d
Update src/Microsoft.Azure.CosmosRepository/Repositories/DefaultRepos…
xInfinitYz Feb 15, 2024
f3e0d36
Update src/Microsoft.Azure.CosmosRepository/Repositories/DefaultRepos…
xInfinitYz Feb 15, 2024
0a89c79
Update src/Microsoft.Azure.CosmosRepository/Repositories/DefaultRepos…
xInfinitYz Feb 15, 2024
0acb985
Update src/Microsoft.Azure.CosmosRepository/Repositories/DefaultRepos…
xInfinitYz Feb 15, 2024
7a4ace8
Update src/Microsoft.Azure.CosmosRepository/Repositories/DefaultRepos…
xInfinitYz Feb 15, 2024
779eb45
Update src/Microsoft.Azure.CosmosRepository/Repositories/DefaultRepos…
xInfinitYz Feb 15, 2024
1fe41da
Update src/Microsoft.Azure.CosmosRepository/Repositories/DefaultRepos…
xInfinitYz Feb 15, 2024
a3fd25a
Update src/Microsoft.Azure.CosmosRepository/Repositories/DefaultRepos…
xInfinitYz Feb 15, 2024
2cb6092
Update src/Microsoft.Azure.CosmosRepository/Repositories/DefaultRepos…
xInfinitYz Feb 15, 2024
86d2310
Implemented PR suggested changes
xInfinitYz Feb 15, 2024
d4f96b4
Merge branch 'Feature/Hierarchical-PartitionKeys' of https://github.c…
xInfinitYz Feb 15, 2024
3441f65
fix conditional statements
xInfinitYz Feb 18, 2024
be93da4
Exposed partition key method to specification builder.
xInfinitYz May 12, 2024
ff1fb9f
Added partitionkey to specification builder
xInfinitYz May 14, 2024
6d34d3c
Merge branch 'unified' into Feature/Hierarchical-PartitionKeys
xInfinitYz Nov 21, 2024
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
5 changes: 5 additions & 0 deletions src/Microsoft.Azure.CosmosEventSourcing/Items/EventItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ public DomainEvent DomainEvent
/// </summary>
public string PartitionKey { get; set; } = null!;

/// <summary>
/// The values used to partition the event.
/// </summary>
public IEnumerable<string> PartitionKeys { get; set; } = null!;

/// <summary>
/// The name of the event stored.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<IncludeSymbols>false</IncludeSymbols>
<IncludeSource>false</IncludeSource>
<RootNamespace>Microsoft.Azure.CosmosEventSourcing</RootNamespace>
<NoWarn>NU5125</NoWarn>
<NoWarn>NU5125;NU1507</NoWarn>
<Optimize Condition="'$(Configuration)'=='Release'">true</Optimize>
<RepositoryUrl>https://github.com/IEvangelist/azure-cosmos-dotnet-repository</RepositoryUrl>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using Microsoft.Azure.Cosmos;
using Microsoft.Azure.CosmosEventSourcing.Aggregates;
using Microsoft.Azure.CosmosEventSourcing.Exceptions;
using Microsoft.Azure.CosmosEventSourcing.Extensions;
using Microsoft.Azure.CosmosRepository.Paging;

namespace Microsoft.Azure.CosmosEventSourcing.Stores;
Expand All @@ -15,8 +15,7 @@ internal partial class DefaultEventStore<TEventItem>
{
public ValueTask<IEnumerable<TEventItem>> ReadAsync(string partitionKey,
CancellationToken cancellationToken = default) =>
readOnlyRepository.GetAsync(
x => x.PartitionKey == partitionKey,
readOnlyRepository.GetAsync(new PartitionKey(partitionKey),
cancellationToken);

public async ValueTask<TAggregateRoot> ReadAggregateAsync<TAggregateRoot>(
Expand All @@ -25,7 +24,7 @@ public async ValueTask<TAggregateRoot> ReadAggregateAsync<TAggregateRoot>(
where TAggregateRoot : IAggregateRoot
{
IEnumerable<TEventItem> events = await readOnlyRepository.GetAsync(
x => x.PartitionKey == partitionKey,
new PartitionKey(partitionKey),
cancellationToken);

var payloads = events
Expand All @@ -46,7 +45,7 @@ public async ValueTask<TAggregateRoot> ReadAggregateAsync<TAggregateRoot>(
CancellationToken cancellationToken = default) where TAggregateRoot : IAggregateRoot
{
IEnumerable<TEventItem> events = await readOnlyRepository.GetAsync(
x => x.PartitionKey == partitionKey,
new PartitionKey(partitionKey),
cancellationToken);

return rootMapper.MapTo(events);
Expand All @@ -57,9 +56,8 @@ public ValueTask<IEnumerable<TEventItem>> ReadAsync(
Expression<Func<TEventItem, bool>> predicate,
CancellationToken cancellationToken = default) =>
readOnlyRepository.GetAsync(
predicate.Compose(
x => x.PartitionKey == partitionKey,
Expression.AndAlso),
new PartitionKey(partitionKey),
predicate,
cancellationToken);

public async IAsyncEnumerable<TEventItem> StreamAsync(
Expand All @@ -69,13 +67,11 @@ public async IAsyncEnumerable<TEventItem> StreamAsync(
{
string? token = null;

Expression<Func<TEventItem, bool>> expression = eventSource =>
eventSource.PartitionKey == partitionKey;

do
{
IPage<TEventItem> page = await readOnlyRepository.PageAsync(
expression,
new PartitionKey(partitionKey),
predicate: null,
chunkSize,
token,
cancellationToken: cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
<IncludeSymbols>false</IncludeSymbols>
<IncludeSource>false</IncludeSource>
<RootNamespace>Microsoft.Azure.CosmosRepository.AspNetCore</RootNamespace>
<NoWarn>NU5125</NoWarn>
<NoWarn>NU5125;NU1507</NoWarn>
<Optimize Condition="'$(Configuration)'=='Release'">true</Optimize>
<RepositoryUrl>https://github.com/IEvangelist/azure-cosmos-dotnet-repository</RepositoryUrl>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,22 @@ namespace Microsoft.Azure.CosmosRepository.Attributes;
/// conjunction with a <see cref="Newtonsoft.Json.JsonPropertyAttribute"/> on the <see cref="IItem"/> property
/// whose value will act as the partition key. Partition key paths should start with "/",
/// for example "/partition". For more information,
/// see https://docs.microsoft.com/azure/cosmos-db/partitioning-overview.
/// see https://docs.microsoft.com/azure/cosmos-db/partitioning-overview and
/// https://learn.microsoft.com/en-us/azure/cosmos-db/hierarchical-partition-keys
/// </summary>
/// <remarks>
/// By default, "/id" is used.
/// </remarks>
/// <remarks>
/// Constructor accepting the <paramref name="path"/> of the partition key for a given <see cref="IItem"/>.
/// Constructor accepting the <paramref name="paths"/> of the partition keys for a given <see cref="IItem"/>.
/// </remarks>
/// <param name="path"></param>
/// <param name="paths"></param>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public sealed class PartitionKeyPathAttribute(string path) : Attribute
public sealed class PartitionKeyPathAttribute(params string[] paths) : Attribute
{
/// <summary>
/// Gets the path of the parition key.
/// Gets the path values of the parition key.
/// </summary>
public string Path { get; } = path ?? throw new ArgumentNullException(nameof(path), "A path is required.");
public string[] Paths { get; } = paths != null && paths.Length >= 1 ? paths : throw new ArgumentNullException(nameof(paths), "At least one path is required.");
}

Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ public class ContainerOptionsBuilder(Type type)
internal string? Name { get; private set; }

/// <summary>
/// The partition key for the container.
/// The partition keys for the container.
/// </summary>
internal string? PartitionKey { get; private set; }
internal IList<string>? PartitionKeys { get; private set; }

/// <summary>
/// The default time to live for a container.
Expand Down Expand Up @@ -79,7 +79,11 @@ public ContainerOptionsBuilder WithContainer(string name)
/// <exception cref="ArgumentNullException"></exception>
public ContainerOptionsBuilder WithPartitionKey(string partitionKey)
{
PartitionKey = partitionKey ?? throw new ArgumentNullException(nameof(partitionKey));
if (partitionKey is null) throw new ArgumentNullException(nameof(partitionKey));

PartitionKeys ??= [];
PartitionKeys.Add(partitionKey);

return this;
}

Expand Down
9 changes: 7 additions & 2 deletions src/Microsoft.Azure.CosmosRepository/IItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,13 @@ public interface IItem
/// </summary>
string Type { get; set; }

///// <summary>
///// Gets the item's PartitionKey. This string is used to instantiate the <c>Cosmos.PartitionKey</c> struct.
///// </summary>
string PartitionKey { get; }

/// <summary>
/// Gets the item's PartitionKey. This string is used to instantiate the <c>Cosmos.PartitionKey</c> struct.
/// Gets the item PartitionKeys. This string array is used to instantiate the <c>Cosmos.PartitionKeys</c> struct.
/// </summary>
string PartitionKey { get; }
IEnumerable<string> PartitionKeys { get; }
}
24 changes: 20 additions & 4 deletions src/Microsoft.Azure.CosmosRepository/Item.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,16 @@ public abstract class Item : IItem
public string Type { get; set; }

/// <summary>
/// Gets the PartitionKey based on <see cref="GetPartitionKeyValue"/>.
/// Gets the PartitionKey based on <see cref="GetPartitionKeyValues"/> last record.
/// Implemented explicitly to keep out of Item API
/// </summary>
string IItem.PartitionKey => GetPartitionKeyValue();
string IItem.PartitionKey => GetPartitionKeyValues().Last();

/// <summary>
/// Gets the PartitionKeys based on <see cref="GetPartitionKeyValues"/>.
/// Implemented explicitly to keep out of Item API
/// </summary>
IEnumerable<string> IItem.PartitionKeys => GetPartitionKeyValues();

/// <summary>
/// Default constructor, assigns type name to <see cref="Type"/> property.
Expand All @@ -56,10 +62,20 @@ public abstract class Item : IItem

/// <summary>
/// Gets the partition key value for the given <see cref="Item"/> type.
/// When overridden, be sure that the <see cref="PartitionKeyPathAttribute.Path"/> value corresponds
/// to the <see cref="JsonPropertyAttribute.PropertyName"/> value, i.e.; "/partition" and "partition"
/// When overridden, be sure that the <see cref="PartitionKeyPathAttribute.Paths"/> values correspond
/// to the <see cref="JsonPropertyAttribute.PropertyName"/> values, i.e.; "/partition" and "partition"
/// respectively. If these two values do not correspond an error will occur.
/// </summary>
/// <returns>The <see cref="Item.Id"/> unless overridden by the subclass.</returns>
protected virtual string GetPartitionKeyValue() => Id;

/// <summary>
/// Gets the partition key values for the given <see cref="Item"/> type.
/// When overridden, be sure that the <see cref="PartitionKeyPathAttribute.Paths"/> values correspond
/// to the <see cref="JsonPropertyAttribute.PropertyName"/> values, i.e.; "/partition" and "partition"
/// respectively. If all provided key values do not have a matching property with the equivalent name, an error will occur.
/// Make sure to add the latest inner
/// </summary>
/// <returns>The list with <see cref="Item.Id"/> unless overridden by the subclass.</returns>
protected virtual IEnumerable<string> GetPartitionKeyValues() => new string[] { GetPartitionKeyValue() };
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ public static void LogPointReadStarted<TItem>(
string partitionKey) where TItem : IItem =>
LoggerMessageDefinitions.PointReadStarted(logger, typeof(TItem).Name, id, partitionKey, null!);

//Info Logger Extensions
public static void LogPointReadStarted<TItem>(
this ILogger logger,
string id,
IEnumerable<string> partitionKeys) where TItem : IItem =>
LoggerMessageDefinitions.HierarchicalPointReadStarted(logger, typeof(TItem).Name, id, partitionKeys, null!);

public static void LogPointReadExecuted<TItem>(
this ILogger logger,
double ruCharge) where TItem : IItem =>
Expand All @@ -41,4 +48,11 @@ public static void LogItemNotFoundHandled<TItem>(
string partitionKey,
CosmosException e) where TItem : IItem =>
LoggerMessageDefinitions.ItemNotFoundHandled(logger, typeof(TItem).Name, id, partitionKey, e);

public static void LogItemNotFoundHandled<TItem>(
this ILogger logger,
string id,
IEnumerable<string> partitionKeys,
CosmosException e) where TItem : IItem =>
LoggerMessageDefinitions.HierarchicalItemNotFoundHandled(logger, typeof(TItem).Name, id, partitionKeys, e);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ internal static class LoggerMessageDefinitions
"Point read started for item type {CosmosItemType} with id {CosmosItemId} and partitionKey {CosmosItemPartitionKey}"
);

// Info Definitions
internal static readonly Action<ILogger, string, string, IEnumerable<string>, Exception?> HierarchicalPointReadStarted =
LoggerMessage.Define<string, string, IEnumerable<string>>(
LogLevel.Information,
EventIds.CosmosPointReadStarted,
"Point read started for item type {CosmosItemType} with id {CosmosItemId} and partitionKeys {CosmosItemPartitionKeys}"
);

internal static readonly Action<ILogger, string, double, Exception?> PointReadExecuted =
LoggerMessage.Define<string, double>(
LogLevel.Information,
Expand All @@ -46,4 +54,11 @@ internal static class LoggerMessageDefinitions
EventIds.ItemNotFoundHandled,
"CosmosException Status Code 404 handled for item of type {CosmosItemType} with {CosmosItemId} and partition key {CosmosItemPartitionKey}"
);

internal static readonly Action<ILogger, string, string, IEnumerable<string>, Exception?> HierarchicalItemNotFoundHandled =
LoggerMessage.Define<string, string, IEnumerable<string>>(
LogLevel.Information,
EventIds.ItemNotFoundHandled,
"CosmosException Status Code 404 handled for item of type {CosmosItemType} with {CosmosItemId} and partition keys {CosmosItemPartitionKeys}"
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
<IncludeSymbols>false</IncludeSymbols>
<IncludeSource>false</IncludeSource>
<RootNamespace>Microsoft.Azure.CosmosRepository</RootNamespace>
<NoWarn>NU5125</NoWarn>
<NoWarn>NU5125;NU1507</NoWarn>
<Optimize Condition="'$(Configuration)'=='Release'">true</Optimize>
<RepositoryUrl>https://github.com/IEvangelist/azure-cosmos-dotnet-repository</RepositoryUrl>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
Expand Down
83 changes: 55 additions & 28 deletions src/Microsoft.Azure.CosmosRepository/Options/ItemConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,59 @@

namespace Microsoft.Azure.CosmosRepository.Options;

internal class ItemConfiguration(
Type type,
string containerName,
string partitionKeyPath,
UniqueKeyPolicy? uniqueKeyPolicy,
ThroughputProperties? throughputProperties,
int defaultTimeToLive = -1,
bool syncContainerProperties = false,
ChangeFeedOptions? changeFeedOptions = null,
bool useStrictTypeChecking = true)
internal class ItemConfiguration
{
public Type Type { get; } = type;

public string ContainerName { get; } = containerName;

public string PartitionKeyPath { get; } = partitionKeyPath;

public UniqueKeyPolicy? UniqueKeyPolicy { get; } = uniqueKeyPolicy;

public ThroughputProperties? ThroughputProperties { get; } = throughputProperties;

public int DefaultTimeToLive { get; } = defaultTimeToLive;

public bool SyncContainerProperties { get; } = syncContainerProperties;

public ChangeFeedOptions? ChangeFeedOptions { get; } = changeFeedOptions;

public bool UseStrictTypeChecking { get; } = useStrictTypeChecking;
}
public ItemConfiguration(
Type type,
string containerName,
string partitionKeyPath,
UniqueKeyPolicy? uniqueKeyPolicy = null,
ThroughputProperties? throughputProperties = null,
int defaultTimeToLive = -1,
bool syncContainerProperties = false,
ChangeFeedOptions? changeFeedOptions = null,
bool useStrictTypeChecking = true)
: this(type, containerName, new[] { partitionKeyPath }, uniqueKeyPolicy, throughputProperties, defaultTimeToLive, syncContainerProperties, changeFeedOptions, useStrictTypeChecking)
{
}

public ItemConfiguration(
Type type,
string containerName,
IEnumerable<string> partitionKeyPaths,
UniqueKeyPolicy? uniqueKeyPolicy = null,
ThroughputProperties? throughputProperties = null,
int defaultTimeToLive = -1,
bool syncContainerProperties = false,
ChangeFeedOptions? changeFeedOptions = null,
bool useStrictTypeChecking = true)
{
Type = type;
ContainerName = containerName;
PartitionKeyPaths = partitionKeyPaths;
UniqueKeyPolicy = uniqueKeyPolicy;
ThroughputProperties = throughputProperties;
DefaultTimeToLive = defaultTimeToLive;
SyncContainerProperties = syncContainerProperties;
ChangeFeedOptions = changeFeedOptions;
UseStrictTypeChecking = useStrictTypeChecking;
}

public Type Type { get; }

public string ContainerName { get; }

public IEnumerable<string> PartitionKeyPaths { get; }

public UniqueKeyPolicy? UniqueKeyPolicy { get; }

public ThroughputProperties? ThroughputProperties { get; }

public int DefaultTimeToLive { get; }

public bool SyncContainerProperties { get; }

public ChangeFeedOptions? ChangeFeedOptions { get; }

public bool UseStrictTypeChecking { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ private ItemConfiguration AddOptions(Type itemType)
itemType.IsItem();

var containerName = containerNameProvider.GetContainerName(itemType);
var partitionKeyPath = cosmosPartitionKeyPathProvider.GetPartitionKeyPath(itemType);
var partitionKeyPaths = cosmosPartitionKeyPathProvider.GetPartitionKeyPaths(itemType);
UniqueKeyPolicy? uniqueKeyPolicy = cosmosUniqueKeyPolicyProvider.GetUniqueKeyPolicy(itemType);
var timeToLive = containerDefaultTimeToLiveProvider.GetDefaultTimeToLive(itemType);
var sync = syncContainerPropertiesProvider.GetWhetherToSyncContainerProperties(itemType);
Expand All @@ -49,11 +49,12 @@ private ItemConfiguration AddOptions(Type itemType)
return new(
itemType,
containerName,
partitionKeyPath,
partitionKeyPaths,
uniqueKeyPolicy,
throughputProperties,
timeToLive,
sync,
useStrictTypeChecking: useStrictTypeChecking);

}
}
Loading