Files
mixtape/zero.Core/Stores/StoreOperations.Tree.cs
T

193 lines
6.1 KiB
C#
Raw Normal View History

2021-11-24 13:56:08 +01:00
using Raven.Client.Documents;
using Raven.Client.Documents.Indexes;
using Raven.Client.Documents.Linq;
using Raven.Client.Documents.Session;
namespace zero.Stores;
2021-11-27 18:09:27 +01:00
public partial class StoreOperations : IStoreOperations
2021-11-24 13:56:08 +01:00
{
/// <inheritdoc />
2021-12-01 15:54:11 +01:00
public virtual Task<bool> IsAllowedAsChild<T>(T model, string parentId) where T : ZeroIdEntity, ISupportsTrees, new()
2021-11-24 13:56:08 +01:00
{
return Task.FromResult(true);
}
/// <inheritdoc />
2021-12-01 15:54:11 +01:00
public async Task<Paged<T>> LoadChildren<T>(string parentId, int pageNumber, int pageSize, Func<IRavenQueryable<T>, IQueryable<T>> querySelector = default) where T : ZeroIdEntity, ISupportsTrees, new()
2021-11-24 13:56:08 +01:00
{
IRavenQueryable<T> queryable = Session.Query<T>().Where(x => x.ParentId == parentId).Statistics(out QueryStatistics statistics);
querySelector ??= x => x.OrderBy(x => x.Sort);
List<T> result = await querySelector(queryable).Paging(pageNumber, pageSize).ToListAsync();
return new Paged<T>(result, statistics.TotalResults, pageNumber, pageSize);
}
/// <inheritdoc />
public async Task<Paged<T>> LoadChildren<T, TIndex>(string parentId, int pageNumber, int pageSize, Func<IRavenQueryable<T>, IQueryable<T>> querySelector = default)
2021-12-01 15:54:11 +01:00
where T : ZeroIdEntity, ISupportsTrees, new()
2021-11-24 13:56:08 +01:00
where TIndex : AbstractCommonApiForIndexes, new()
{
IRavenQueryable<T> queryable = Session.Query<T, TIndex>().Where(x => x.ParentId == parentId).Statistics(out QueryStatistics statistics);
querySelector ??= x => x.OrderBy(x => x.Sort);
List<T> result = await querySelector(queryable).Paging(pageNumber, pageSize).ToListAsync();
return new Paged<T>(result, statistics.TotalResults, pageNumber, pageSize);
}
/// <inheritdoc />
2021-12-01 15:54:11 +01:00
public async Task<Result<IOrderedEnumerable<T>>> Sort<T>(string[] sortedIds) where T : ZeroIdEntity, ISupportsTrees, new()
2021-11-24 13:56:08 +01:00
{
Dictionary<string, T> items = await Load<T>(sortedIds);
uint index = 0;
// contains multiple parents, therefore fail
if (items.Select(x => x.Value?.ParentId).Distinct().Count() > 1)
{
2021-11-26 15:47:11 +01:00
return Result<IOrderedEnumerable<T>>.Fail("@errors.treeentity.sortingmultipleparents");
2021-11-24 13:56:08 +01:00
}
foreach (var item in items)
{
item.Value.Sort = index;
index += 10;
await Update(item.Value);
}
2021-11-26 15:47:11 +01:00
return Result<IOrderedEnumerable<T>>.Success(items.Select(x => x.Value).OrderByDescending(x => x.Sort));
2021-11-24 13:56:08 +01:00
}
/// <inheritdoc />
2021-12-01 15:54:11 +01:00
public async Task<Result<T>> Move<T>(string id, string newParentId, Func<T, string, Task<bool>> isParentAllowed = null) where T : ZeroIdEntity, ISupportsTrees, new()
2021-11-24 13:56:08 +01:00
{
T model = await Load<T>(id);
T parent = await Load<T>(newParentId);
if (model == null || (!newParentId.IsNullOrEmpty() && parent == null))
{
2021-11-26 15:47:11 +01:00
return Result<T>.Fail("@errors.idnotfound");
2021-11-24 13:56:08 +01:00
}
if (isParentAllowed != null && !await isParentAllowed(model, newParentId))
{
2021-11-26 15:47:11 +01:00
return Result<T>.Fail("@errors.treeentity.parentnotallowed");
2021-11-24 13:56:08 +01:00
}
model.ParentId = parent?.Id;
return await Update(model);
}
/// <inheritdoc />
2021-12-01 15:54:11 +01:00
public Task<Result<T>> Copy<T>(string id, string newParentId, Func<T, string, Task<bool>> isParentAllowed = null) where T : ZeroIdEntity, ISupportsTrees, new() => Copy<T>(id, newParentId, false, isParentAllowed);
2021-11-24 13:56:08 +01:00
/// <inheritdoc />
2021-12-01 15:54:11 +01:00
public Task<Result<T>> CopyWithDescendants<T>(string id, string newParentId, Func<T, string, Task<bool>> isParentAllowed = null) where T : ZeroIdEntity, ISupportsTrees, new() => Copy<T>(id, newParentId, true, isParentAllowed);
2021-11-24 13:56:08 +01:00
/// <summary>
/// Copies an entity (with optional descendants) to a new location
/// </summary>
2021-12-01 15:54:11 +01:00
protected async Task<Result<T>> Copy<T>(string id, string newParentId, bool includeDescendants, Func<T, string, Task<bool>> isParentAllowed = null) where T : ZeroIdEntity, ISupportsTrees, new()
2021-11-24 13:56:08 +01:00
{
T model = await Load<T>(id);
T parent = await Load<T>(newParentId);
if (model == null || (!newParentId.IsNullOrEmpty() && parent == null))
{
2021-11-26 15:47:11 +01:00
return Result<T>.Fail("@errors.idnotfound");
2021-11-24 13:56:08 +01:00
}
string baseId = model.Id;
// update new page properties
model.Id = null;
model.ParentId = parent?.Id;
if (model is ZeroEntity zeroEntity)
{
zeroEntity.IsActive = false;
zeroEntity.CreatedDate = DateTime.Now;
}
// check if new parent is allowed
if (isParentAllowed != null && !await isParentAllowed(model, newParentId))
{
2021-11-26 15:47:11 +01:00
return Result<T>.Fail("@errors.treeentity.parentnotallowed");
2021-11-24 13:56:08 +01:00
}
// recursive function to store descendants
async Task AddChildren(string oldParentId, string newParentId)
{
List<T> children = await Session.Query<T>().Where(x => x.ParentId == oldParentId).ToListAsync();
foreach (T child in children)
{
T clonedChild = ObjectCopycat.Clone(child);
clonedChild.Id = null;
clonedChild.ParentId = newParentId;
if (clonedChild is ZeroEntity zeroEntity)
{
zeroEntity.IsActive = false;
zeroEntity.CreatedDate = DateTime.Now;
}
await Create(clonedChild);
await AddChildren(child.Id, clonedChild.Id);
}
}
if (includeDescendants)
{
await AddChildren(baseId, model.Id);
}
2021-11-26 15:47:11 +01:00
return Result<T>.Success(model);
2021-11-24 13:56:08 +01:00
}
/// <inheritdoc />
2021-12-01 15:54:11 +01:00
public async Task<Result<string[]>> DeleteWithDescendants<T>(T model) where T : ZeroIdEntity, ISupportsTrees, new()
2021-11-24 13:56:08 +01:00
{
List<T> pages = await GetDescendantsAndSelf(model);
if (pages.Count < 1)
{
2021-11-26 15:47:11 +01:00
return Result<string[]>.Fail("@errors.ondelete.idnotfound");
2021-11-24 13:56:08 +01:00
}
2021-11-24 15:49:25 +01:00
await this.Delete(pages.ToArray());
2021-11-26 15:47:11 +01:00
return Result<string[]>.Success(pages.Select(x => x.Id).ToArray());
2021-11-24 13:56:08 +01:00
}
/// <summary>
/// Get an entity with all its descendants
/// </summary>
2021-12-01 15:54:11 +01:00
async Task<List<T>> GetDescendantsAndSelf<T>(T model) where T : ZeroIdEntity, ISupportsTrees, new()
2021-11-24 13:56:08 +01:00
{
List<T> items = new() { model };
async Task AddChildren(T parent)
{
string parentId = parent.Id;
List<T> children = await Session.Query<T>().Where(x => x.ParentId == parentId).ToListAsync();
items.AddRange(children);
foreach (T child in children)
{
await AddChildren(child);
}
}
await AddChildren(model);
return items;
}
}