// ========================================================================== // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex UG (haftungsbeschraenkt) // All rights reserved. Licensed under the MIT license. // ========================================================================== using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using MongoDB.Driver; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Contents; using Squidex.Domain.Apps.Entities.MongoDb.Contents.Visitors; using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Infrastructure; using Squidex.Infrastructure.Json; using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.Queries; namespace Squidex.Domain.Apps.Entities.MongoDb.Contents { internal class MongoContentCollection : MongoRepositoryBase { private readonly IAppProvider appProvider; private readonly string collectionName; protected IJsonSerializer Serializer { get; } public MongoContentCollection(IMongoDatabase database, IJsonSerializer serializer, IAppProvider appProvider, string collectionName) : base(database) { this.collectionName = collectionName; this.appProvider = appProvider; Serializer = serializer; } protected override async Task SetupCollectionAsync(IMongoCollection collection, CancellationToken ct = default) { await collection.Indexes.CreateOneAsync( new CreateIndexModel(Index.Ascending(x => x.ReferencedIds)), cancellationToken: ct); } protected override string CollectionName() { return collectionName; } public async Task> QueryAsync(IAppEntity app, ISchemaEntity schema, Query query, Status[] status = null, bool useDraft = false) { try { query = query.AdjustToModel(schema.SchemaDef, useDraft); var filter = query.ToFilter(schema.Id, status); var contentCount = Collection.Find(filter).CountDocumentsAsync(); var contentItems = Collection.Find(filter) .ContentTake(query) .ContentSkip(query) .ContentSort(query) .Not(x => x.DataText) .ToListAsync(); await Task.WhenAll(contentItems, contentCount); foreach (var entity in contentItems.Result) { entity.ParseData(schema.SchemaDef, Serializer); } return ResultList.Create(contentCount.Result, contentItems.Result); } catch (MongoQueryException ex) { if (ex.Message.Contains("17406")) { throw new DomainException("Result set is too large to be retrieved. Use $top parameter to reduce the number of items."); } else { throw; } } } public async Task> QueryAsync(IAppEntity app, HashSet ids, Status[] status = null) { var find = status != null && status.Length > 0 ? Collection.Find(x => x.IndexedAppId == app.Id && ids.Contains(x.Id) && x.IsDeleted != true && status.Contains(x.Status)) : Collection.Find(x => x.IndexedAppId == app.Id && ids.Contains(x.Id)); var contentItems = await find.Not(x => x.DataText).ToListAsync(); var schemaIds = contentItems.Select(x => x.IndexedSchemaId).ToList(); var schemas = await Task.WhenAll(schemaIds.Select(x => appProvider.GetSchemaAsync(app.Id, x))); var result = new List<(IContentEntity Content, ISchemaEntity Schema)>(); foreach (var entity in contentItems) { var schema = schemas.FirstOrDefault(x => x.Id == entity.IndexedSchemaId); if (schema != null) { entity.ParseData(schema.SchemaDef, Serializer); result.Add((entity, schema)); } } return result; } public async Task> QueryAsync(IAppEntity app, ISchemaEntity schema, HashSet ids, Status[] status = null) { var find = status != null && status.Length > 0 ? Collection.Find(x => x.IndexedSchemaId == schema.Id && ids.Contains(x.Id) && x.IsDeleted != true && status.Contains(x.Status)) : Collection.Find(x => x.IndexedSchemaId == schema.Id && ids.Contains(x.Id)); var contentItems = find.Not(x => x.DataText).ToListAsync(); var contentCount = find.CountDocumentsAsync(); await Task.WhenAll(contentItems, contentCount); foreach (var entity in contentItems.Result) { entity.ParseData(schema.SchemaDef, Serializer); } return ResultList.Create(contentCount.Result, contentItems.Result); } public Task CleanupAsync(Guid id) { return Collection.UpdateManyAsync( Filter.And( Filter.AnyEq(x => x.ReferencedIds, id), Filter.AnyNe(x => x.ReferencedIdsDeleted, id)), Update.AddToSet(x => x.ReferencedIdsDeleted, id)); } public Task RemoveAsync(Guid id) { return Collection.DeleteOneAsync(x => x.Id == id); } } }