using Raven.Client.Documents; using Raven.Client.Documents.Indexes; using Raven.Client.Documents.Linq; using Raven.Client.Documents.Session; namespace zero.Stores; public partial class StoreOperations : IStoreOperations { /// public virtual Task IsAllowedAsChild(T model, string parentId) where T : ZeroIdEntity, ISupportsTrees, new() { return Task.FromResult(true); } /// public async Task> LoadChildren(string parentId, int pageNumber, int pageSize, Func, IQueryable> querySelector = default) where T : ZeroIdEntity, ISupportsTrees, new() { IRavenQueryable queryable = Session.Query().Where(x => x.ParentId == parentId).Statistics(out QueryStatistics statistics); querySelector ??= x => x.OrderBy(x => x.Sort); List result = await querySelector(queryable).Paging(pageNumber, pageSize).ToListAsync(); return new Paged(result, statistics.TotalResults, pageNumber, pageSize); } /// public async Task> LoadChildren(string parentId, int pageNumber, int pageSize, Func, IQueryable> querySelector = default) where T : ZeroIdEntity, ISupportsTrees, new() where TIndex : AbstractCommonApiForIndexes, new() { IRavenQueryable queryable = Session.Query().Where(x => x.ParentId == parentId).Statistics(out QueryStatistics statistics); querySelector ??= x => x.OrderBy(x => x.Sort); List result = await querySelector(queryable).Paging(pageNumber, pageSize).ToListAsync(); return new Paged(result, statistics.TotalResults, pageNumber, pageSize); } /// public async Task>> Sort(string[] sortedIds) where T : ZeroIdEntity, ISupportsTrees, new() { Dictionary items = await Load(sortedIds); uint index = 0; // contains multiple parents, therefore fail if (items.Select(x => x.Value?.ParentId).Distinct().Count() > 1) { return Result>.Fail("@errors.treeentity.sortingmultipleparents"); } foreach (var item in items) { item.Value.Sort = index; index += 10; await Update(item.Value); } return Result>.Success(items.Select(x => x.Value).OrderByDescending(x => x.Sort)); } /// public async Task> Move(string id, string newParentId, Func> isParentAllowed = null) where T : ZeroIdEntity, ISupportsTrees, new() { T model = await Load(id); T parent = await Load(newParentId); if (model == null || (!newParentId.IsNullOrEmpty() && parent == null)) { return Result.Fail("@errors.idnotfound"); } if (isParentAllowed != null && !await isParentAllowed(model, newParentId)) { return Result.Fail("@errors.treeentity.parentnotallowed"); } model.ParentId = parent?.Id; return await Update(model); } /// public Task> Copy(string id, string newParentId, Func> isParentAllowed = null) where T : ZeroIdEntity, ISupportsTrees, new() => Copy(id, newParentId, false, isParentAllowed); /// public Task> CopyWithDescendants(string id, string newParentId, Func> isParentAllowed = null) where T : ZeroIdEntity, ISupportsTrees, new() => Copy(id, newParentId, true, isParentAllowed); /// /// Copies an entity (with optional descendants) to a new location /// protected async Task> Copy(string id, string newParentId, bool includeDescendants, Func> isParentAllowed = null) where T : ZeroIdEntity, ISupportsTrees, new() { T model = await Load(id); T parent = await Load(newParentId); if (model == null || (!newParentId.IsNullOrEmpty() && parent == null)) { return Result.Fail("@errors.idnotfound"); } 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)) { return Result.Fail("@errors.treeentity.parentnotallowed"); } // recursive function to store descendants async Task AddChildren(string oldParentId, string newParentId) { List children = await Session.Query().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); } return Result.Success(model); } /// public async Task> DeleteWithDescendants(T model) where T : ZeroIdEntity, ISupportsTrees, new() { List pages = await GetDescendantsAndSelf(model); if (pages.Count < 1) { return Result.Fail("@errors.ondelete.idnotfound"); } await this.Delete(pages.ToArray()); return Result.Success(pages.Select(x => x.Id).ToArray()); } /// /// Get an entity with all its descendants /// async Task> GetDescendantsAndSelf(T model) where T : ZeroIdEntity, ISupportsTrees, new() { List items = new() { model }; async Task AddChildren(T parent) { string parentId = parent.Id; List children = await Session.Query().Where(x => x.ParentId == parentId).ToListAsync(); items.AddRange(children); foreach (T child in children) { await AddChildren(child); } } await AddChildren(model); return items; } }