using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; using LCU.API.Internal.DocumentLib.Containers; using LCU.API.Internal.DocumentLib.Utilities; using LCU.Library.Internal.DocumentIngestorModels.Models.Pagination; using LCU.Library.Internal.DocumentModels.Requests.Search; using Microsoft.Azure.Cosmos.Linq; using MongoDB.Bson; using MongoDB.Driver; namespace LCU.API.Internal.DocumentLib.Repositories; public class DocumentInformationRepository : IDocumentInformationRepository { private readonly IMongoCollection _documents; public DocumentInformationRepository(IMongoCollectionFactory collectionFactory) { _documents = collectionFactory.GetCollection(); } public async Task GetDocumentInformation(Guid documentId) { return await _documents.Find(doc => doc.DocumentId == documentId) .SingleOrDefaultAsync(); } public async Task> SearchDocumentInformation(DocumentInformationFilter filter, int? skip = null, int? take = null, List? sortDefinition = null) { var pipelineFilter = FilterFactory(filter); var sorter = Sorter.GetSorter(sortDefinition); if (sorter != null) pipelineFilter = pipelineFilter.Sort(sorter); if (skip.HasValue) pipelineFilter = pipelineFilter.Skip(skip.Value); if (take.HasValue) pipelineFilter = pipelineFilter.Limit(take.Value); var documentsInformation = await _documents.AggregateAsync(pipelineFilter); return await documentsInformation.ToListAsync(); } public async Task CountDocuments(DocumentInformationFilter filter) { return (await _documents.AggregateAsync(FilterFactory(filter).Count())).Single().Count; } public async Task GetDocumentInformationByBlobId(Guid documentBlobId) { return await _documents .Find(doc => doc.DocumentBlobId == documentBlobId) .SingleOrDefaultAsync(); } public async Task SaveDocumentInformation(DocumentInformation document) { await _documents.InsertOneAsync(document); return document; } public async Task UpdateDocumentInformation(Guid documentId, DocumentInformation newDocument) { await _documents.FindOneAndReplaceAsync(doc => doc.DocumentId == documentId, newDocument); return newDocument; } public async Task RemoveDocumentInformation(Guid documentId) { return await _documents.FindOneAndDeleteAsync(document => document.DocumentId == documentId); } public async Task> GetDocumentsForCleanup(GetDocumentCleanupRequest request) { IList> filterConcat = new List>(); foreach(var documentCodeDetail in request.DocumentCodeDetailInfos) { var adjustedDate = DateTime.Now.AddMonths(documentCodeDetail.InternalRetentionMonths * -1); var typeFilter = Builders.Filter.Eq(x => x.Type, documentCodeDetail.DocumentCode); var dateFilter = Builders.Filter.Lt(x => x.CreatedDate, adjustedDate); var groupFilter = Builders.Filter.And(typeFilter, dateFilter); filterConcat.Add(groupFilter); } var filter = Builders.Filter; var oneConcat = filter.Empty; oneConcat &= filter.Or(filterConcat); var nullFilter = Builders.Filter.Ne(x => x.DocumentBlobId, null); oneConcat &= filter.And(nullFilter); var pipelineFilter = new EmptyPipelineDefinition() .Match(oneConcat) .Limit(request.Limit); var documentsInformation = await _documents.AggregateAsync(pipelineFilter); return (await documentsInformation.ToListAsync()).Select(document => document.DocumentId).ToList(); } private PipelineDefinition FilterFactory(DocumentInformationFilter filter) { var builder = Builders.Filter; IList> stageOneFiltersAnd = new List>(); IList> stageOneFiltersOr = new List>(); IList> stageTwoFiltersAnd = new List>(); IList> stageTwoFiltersOr = new List>(); if (!string.IsNullOrWhiteSpace(filter.Name)) { stageOneFiltersAnd.Add(builder.Eq("Name", filter.Name)); } if (!string.IsNullOrWhiteSpace(filter.Type)) { stageOneFiltersAnd.Add(builder.Eq("Type", filter.Type)); } if (filter.TypeContainsAny != null && filter.TypeContainsAny.Any()) { stageOneFiltersAnd.Add(builder.In("Type", filter.TypeContainsAny)); } if (filter.ScanStatus != null) { stageOneFiltersAnd.Add(builder.Eq("ScanStatus", filter.ScanStatus)); } if (!string.IsNullOrWhiteSpace(filter.Source)) { stageOneFiltersAnd.Add(builder.Eq("Source", filter.Source)); } if (!string.IsNullOrWhiteSpace(filter.DataSource)) { stageOneFiltersAnd.Add(builder.Eq("DataSource", filter.DataSource)); } if (filter.TraceId != null) { stageOneFiltersAnd.Add(builder.Eq("TraceId", filter.TraceId)); } if (!string.IsNullOrWhiteSpace(filter.PersonNumbersEquals)) { stageOneFiltersAnd.Add(builder.Eq("PersonNumbers", filter.PersonNumbersEquals)); } if (filter.PersonNumbersContains != null && filter.PersonNumbersContains.Any()) { stageOneFiltersAnd.Add(builder.All("PersonNumbers", filter.PersonNumbersContains)); } if (filter.PersonNumbersContainsAny != null && filter.PersonNumbersContainsAny.Any()) { stageOneFiltersAnd.Add(builder.In("PersonNumbers", filter.PersonNumbersContainsAny)); } if (filter.OrganizationNumbersEquals != null && filter.OrganizationNumbersEquals.Any()) { stageOneFiltersAnd.Add(builder.Eq("OrganizationNumbers", filter.OrganizationNumbersEquals)); } if (filter.OrganizationNumbersContains != null && filter.OrganizationNumbersContains.Any()) { stageOneFiltersAnd.Add(builder.All("OrganizationNumbers", filter.OrganizationNumbersContains)); } if (filter.OrganizationNumbersContainsAny != null && filter.OrganizationNumbersContainsAny.Any()) { stageOneFiltersAnd.Add(builder.In("OrganizationNumbers", filter.OrganizationNumbersContainsAny)); } if (filter.AccountNumberEquals != null && filter.AccountNumberEquals.Any()) { stageOneFiltersAnd.Add(builder.Eq("AccountNumber", filter.AccountNumberEquals)); } if (filter.AccountNumberContainsAny != null && filter.AccountNumberContainsAny.Any()) { stageOneFiltersAnd.Add(builder.In("AccountNumber", filter.AccountNumberContainsAny)); } if (filter.AttributesEquals != null && filter.AttributesEquals.Any()) { foreach (var keyValuePair in filter.AttributesEquals) { stageOneFiltersAnd.Add(builder.Eq($"Attributes.{keyValuePair.Key.Trim()}", keyValuePair.Value.Trim())); } } if (filter.AttributesContains is not null) { foreach (var keyValuePair in filter.AttributesContains) { var queryExpr = new BsonRegularExpression( new Regex($"\\b({keyValuePair.Value.Trim()})\\b", RegexOptions.IgnoreCase)); stageTwoFiltersAnd.Add(builder.Regex($"Attributes.{keyValuePair.Key.Trim()}", queryExpr)); } } if (filter.AttributesContainsAny is not null) { foreach (var keyValuePair in filter.AttributesContainsAny) { foreach (var value in keyValuePair.Value.Split(',')) { var queryExpr = new BsonRegularExpression(new Regex($"^{value.Trim()}$", RegexOptions.IgnoreCase)); stageTwoFiltersOr.Add(builder.Regex($"Attributes.{keyValuePair.Key.Trim()}", queryExpr)); } } } if (filter.CreatedDateMinimum != null) { stageOneFiltersAnd.Add(builder.Gte("CreatedDate", filter.CreatedDateMinimum)); } if (filter.CreatedDateMaximum != null) { stageOneFiltersAnd.Add(builder.Lte("CreatedDate", filter.CreatedDateMaximum)); } if (filter.UploadDateMinimum != null) { stageOneFiltersAnd.Add(builder.Gte("UploadDate", filter.UploadDateMinimum)); } if (filter.UploadDateMaximum != null) { stageOneFiltersAnd.Add(builder.Lte("UploadDate", filter.UploadDateMaximum)); } if (filter.LastModifiedMinimum != null) { stageOneFiltersAnd.Add(builder.Gte("LastModifiedDate", filter.LastModifiedMinimum)); } if (filter.LastModifiedDateMaximum != null) { stageOneFiltersAnd.Add(builder.Lte("LastModifiedDate", filter.LastModifiedDateMaximum)); } var stageOneFilterConcat = builder.Empty; if (stageOneFiltersAnd.Any()) { stageOneFilterConcat &= builder.And(stageOneFiltersAnd); } if (stageOneFiltersOr.Any()) { stageOneFilterConcat &= builder.Or(stageOneFiltersOr); } var stageTwoFilterConcat = builder.Empty; if (stageTwoFiltersAnd.Any()) { stageOneFilterConcat &= builder.And(stageTwoFiltersAnd); } if (stageTwoFiltersOr.Any()) { stageTwoFilterConcat &= builder.Or(stageTwoFiltersOr); } return new EmptyPipelineDefinition() .Match(stageOneFilterConcat) .Match(stageTwoFilterConcat); } }