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 GetHierarchy(string id) where T : ZeroIdEntity, ISupportsTrees, new() where TIndex : ZeroTreeHierarchyIndex, new() { ZeroTreeHierarchyIndexResult result = await Session.Query() .ProjectInto() .Include(x => x.Path) .Include(x => x.Id) .FirstOrDefaultAsync(x => x.Id == id); if (result == null) { return Array.Empty(); } List ids = result.Path ?? new(); ids.Add(id); return (await Session.LoadAsync(ids)).Select(x => x.Value).ToArray(); } /// 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); var query = querySelector(queryable).Paging(pageNumber, pageSize); List result = await query.ToListAsync(); return new Paged(result, statistics.TotalResults, pageNumber, pageSize); } /// 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 = ObjectCopycat.Clone(await Load(id)); T parent = await Load(newParentId); if (model == null || (!newParentId.IsNullOrEmpty() && parent == null)) { return Result.Fail("@errors.idnotfound"); } string baseId = model.Id; string originalParentId = model.ParentId; // update new page properties model.Id = null; model.ParentId = parent?.Id; if (model is ZeroEntity zeroEntity) { zeroEntity.IsActive = false; zeroEntity.CreatedDate = DateTime.Now; // update page name in case they are on the same level string name = zeroEntity.Name; if (originalParentId == parent?.Id || await Session.Query().AnyAsync(x => x.Name == name && x.ParentId == newParentId)) { zeroEntity.Name += " (copy #" + IdGenerator.Create(4) + ")"; } } // check if new parent is allowed if (isParentAllowed != null && !await isParentAllowed(model, newParentId)) { return Result.Fail("@errors.treeentity.parentnotallowed"); } Result result = await Create(model); // recursive function to store descendants if (includeDescendants) { List children = await Session.Query().Where(x => x.ParentId == baseId).ToListAsync(); foreach (T child in children) { await Copy(child.Id, model.Id, true, isParentAllowed); } } return result; } /// 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; } }