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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-12-16 13:55:09 +01:00
|
|
|
/// <inheritdoc />
|
|
|
|
|
public async Task<T[]> GetHierarchy<T, TIndex>(string id)
|
|
|
|
|
where T : ZeroIdEntity, ISupportsTrees, new()
|
|
|
|
|
where TIndex : ZeroTreeHierarchyIndex<T>, new()
|
|
|
|
|
{
|
|
|
|
|
ZeroTreeHierarchyIndexResult result = await Session.Query<ZeroTreeHierarchyIndexResult, TIndex>()
|
|
|
|
|
.ProjectInto<ZeroTreeHierarchyIndexResult>()
|
|
|
|
|
.Include<ZeroTreeHierarchyIndexResult, T>(x => x.Path)
|
|
|
|
|
.Include<ZeroTreeHierarchyIndexResult, T>(x => x.Id)
|
|
|
|
|
.FirstOrDefaultAsync(x => x.Id == id);
|
|
|
|
|
|
|
|
|
|
if (result == null)
|
|
|
|
|
{
|
|
|
|
|
return Array.Empty<T>();
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-16 22:26:59 +01:00
|
|
|
List<string> ids = result.Path ?? new();
|
2021-12-16 13:55:09 +01:00
|
|
|
ids.Add(id);
|
|
|
|
|
|
|
|
|
|
return (await Session.LoadAsync<T>(ids)).Select(x => x.Value).ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-11-24 13:56:08 +01:00
|
|
|
/// <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);
|
|
|
|
|
|
2021-12-16 16:59:27 +01:00
|
|
|
var query = querySelector(queryable).Paging(pageNumber, pageSize);
|
|
|
|
|
|
|
|
|
|
List<T> result = await query.ToListAsync();
|
2021-11-24 13:56:08 +01:00
|
|
|
return new Paged<T>(result, statistics.TotalResults, pageNumber, pageSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <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
|
|
|
{
|
2022-01-13 13:13:29 +01:00
|
|
|
T model = ObjectCopycat.Clone(await Load<T>(id));
|
2021-11-24 13:56:08 +01:00
|
|
|
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;
|
2022-01-13 13:13:29 +01:00
|
|
|
string originalParentId = model.ParentId;
|
2021-11-24 13:56:08 +01:00
|
|
|
|
|
|
|
|
// update new page properties
|
|
|
|
|
model.Id = null;
|
|
|
|
|
model.ParentId = parent?.Id;
|
|
|
|
|
|
|
|
|
|
if (model is ZeroEntity zeroEntity)
|
|
|
|
|
{
|
|
|
|
|
zeroEntity.IsActive = false;
|
|
|
|
|
zeroEntity.CreatedDate = DateTime.Now;
|
2022-01-13 13:13:29 +01:00
|
|
|
|
|
|
|
|
// update page name in case they are on the same level
|
|
|
|
|
string name = zeroEntity.Name;
|
|
|
|
|
if (originalParentId == parent?.Id || await Session.Query<Page>().AnyAsync(x => x.Name == name && x.ParentId == newParentId))
|
|
|
|
|
{
|
|
|
|
|
zeroEntity.Name += " (copy #" + IdGenerator.Create(4) + ")";
|
|
|
|
|
}
|
2021-11-24 13:56:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
}
|
|
|
|
|
|
2022-01-13 13:13:29 +01:00
|
|
|
Result<T> result = await Create(model);
|
|
|
|
|
|
2021-11-24 13:56:08 +01:00
|
|
|
// recursive function to store descendants
|
2022-01-13 13:13:29 +01:00
|
|
|
if (includeDescendants)
|
2021-11-24 13:56:08 +01:00
|
|
|
{
|
2022-01-13 13:13:29 +01:00
|
|
|
List<T> children = await Session.Query<T>().Where(x => x.ParentId == baseId).ToListAsync();
|
2021-11-24 13:56:08 +01:00
|
|
|
|
|
|
|
|
foreach (T child in children)
|
|
|
|
|
{
|
2022-01-13 13:13:29 +01:00
|
|
|
await Copy(child.Id, model.Id, true, isParentAllowed);
|
2021-11-24 13:56:08 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-13 13:13:29 +01:00
|
|
|
return result;
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|