mirror of https://github.com/Squidex/squidex.git
Browse Source
* References endpoints. * Tests fixed. * Mini fix. * Some fallback changes.pull/607/head
committed by
GitHub
47 changed files with 965 additions and 944 deletions
@ -1,40 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using System; |
|
||||
using MongoDB.Bson.Serialization; |
|
||||
|
|
||||
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents |
|
||||
{ |
|
||||
internal static class Fields |
|
||||
{ |
|
||||
private static readonly Lazy<string> IdField = new Lazy<string>(GetIdField); |
|
||||
private static readonly Lazy<string> SchemaIdField = new Lazy<string>(GetSchemaIdField); |
|
||||
private static readonly Lazy<string> StatusField = new Lazy<string>(GetStatusField); |
|
||||
|
|
||||
public static string Id => IdField.Value; |
|
||||
|
|
||||
public static string SchemaId => SchemaIdField.Value; |
|
||||
|
|
||||
public static string Status => StatusField.Value; |
|
||||
|
|
||||
private static string GetIdField() |
|
||||
{ |
|
||||
return BsonClassMap.LookupClassMap(typeof(MongoContentEntity)).GetMemberMap(nameof(MongoContentEntity.Id)).ElementName; |
|
||||
} |
|
||||
|
|
||||
private static string GetSchemaIdField() |
|
||||
{ |
|
||||
return BsonClassMap.LookupClassMap(typeof(MongoContentEntity)).GetMemberMap(nameof(MongoContentEntity.IndexedSchemaId)).ElementName; |
|
||||
} |
|
||||
|
|
||||
private static string GetStatusField() |
|
||||
{ |
|
||||
return BsonClassMap.LookupClassMap(typeof(MongoContentEntity)).GetMemberMap(nameof(MongoContentEntity.Status)).ElementName; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,138 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|
||||
// 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.Contents.Text; |
|
||||
using Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations; |
|
||||
using Squidex.Domain.Apps.Entities.Schemas; |
|
||||
using Squidex.Infrastructure; |
|
||||
using Squidex.Infrastructure.MongoDb; |
|
||||
using Squidex.Infrastructure.Queries; |
|
||||
using Squidex.Log; |
|
||||
|
|
||||
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents |
|
||||
{ |
|
||||
public sealed class MongoContentCollectionPublished : MongoRepositoryBase<MongoContentEntity> |
|
||||
{ |
|
||||
private readonly QueryContent queryContentAsync; |
|
||||
private readonly QueryContentsByIds queryContentsById; |
|
||||
private readonly QueryContentsByQuery queryContentsByQuery; |
|
||||
private readonly QueryIdsAsync queryIdsAsync; |
|
||||
private readonly QueryReferrersAsync queryReferrersAsync; |
|
||||
|
|
||||
public MongoContentCollectionPublished(IMongoDatabase database, IAppProvider appProvider, ITextIndex indexer, DataConverter converter) |
|
||||
: base(database) |
|
||||
{ |
|
||||
queryContentAsync = new QueryContent(converter); |
|
||||
queryContentsById = new QueryContentsByIds(converter, appProvider); |
|
||||
queryContentsByQuery = new QueryContentsByQuery(converter, indexer, appProvider); |
|
||||
queryReferrersAsync = new QueryReferrersAsync(); |
|
||||
queryIdsAsync = new QueryIdsAsync(appProvider); |
|
||||
} |
|
||||
|
|
||||
public IMongoCollection<MongoContentEntity> GetInternalCollection() |
|
||||
{ |
|
||||
return Collection; |
|
||||
} |
|
||||
|
|
||||
protected override MongoCollectionSettings CollectionSettings() |
|
||||
{ |
|
||||
return new MongoCollectionSettings |
|
||||
{ |
|
||||
ReadPreference = ReadPreference.SecondaryPreferred.With(TimeSpan.FromMinutes(2)) |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
protected override string CollectionName() |
|
||||
{ |
|
||||
return "States_Contents_Published2"; |
|
||||
} |
|
||||
|
|
||||
protected override async Task SetupCollectionAsync(IMongoCollection<MongoContentEntity> collection, CancellationToken ct = default) |
|
||||
{ |
|
||||
await queryContentAsync.PrepareAsync(collection, ct); |
|
||||
await queryContentsById.PrepareAsync(collection, ct); |
|
||||
await queryContentsByQuery.PrepareAsync(collection, ct); |
|
||||
await queryReferrersAsync.PrepareAsync(collection, ct); |
|
||||
await queryIdsAsync.PrepareAsync(collection, ct); |
|
||||
} |
|
||||
|
|
||||
public async Task<IResultList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, ClrQuery query, DomainId? referenced) |
|
||||
{ |
|
||||
using (Profiler.TraceMethod<MongoContentRepository>("QueryAsyncByQuery")) |
|
||||
{ |
|
||||
return await queryContentsByQuery.DoAsync(app, schema, query, referenced, SearchScope.Published); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public async Task<IResultList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, HashSet<DomainId> ids) |
|
||||
{ |
|
||||
Guard.NotNull(app, nameof(app)); |
|
||||
|
|
||||
using (Profiler.TraceMethod<MongoContentRepository>("QueryAsyncByIds")) |
|
||||
{ |
|
||||
var result = await queryContentsById.DoAsync(app.Id, schema, ids, true); |
|
||||
|
|
||||
return ResultList.Create(result.Count, result.Select(x => x.Content)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public async Task<List<(IContentEntity Content, ISchemaEntity Schema)>> QueryAsync(IAppEntity app, HashSet<DomainId> ids) |
|
||||
{ |
|
||||
Guard.NotNull(app, nameof(app)); |
|
||||
|
|
||||
using (Profiler.TraceMethod<MongoContentRepository>("QueryAsyncByIdsWithoutSchema")) |
|
||||
{ |
|
||||
var result = await queryContentsById.DoAsync(app.Id, null, ids, true); |
|
||||
|
|
||||
return result; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public async Task<IContentEntity?> FindContentAsync(ISchemaEntity schema, DomainId id) |
|
||||
{ |
|
||||
using (Profiler.TraceMethod<MongoContentRepository>()) |
|
||||
{ |
|
||||
return await queryContentAsync.DoAsync(schema, id); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public async Task<IReadOnlyList<(DomainId SchemaId, DomainId Id, Status Status)>> QueryIdsAsync(DomainId appId, HashSet<DomainId> ids) |
|
||||
{ |
|
||||
using (Profiler.TraceMethod<MongoContentRepository>()) |
|
||||
{ |
|
||||
return await queryIdsAsync.DoAsync(appId, ids); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public async Task<bool> HasReferrersAsync(DomainId appId, DomainId contentId) |
|
||||
{ |
|
||||
using (Profiler.TraceMethod<MongoContentRepository>()) |
|
||||
{ |
|
||||
return await queryReferrersAsync.DoAsync(appId, contentId); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public Task UpsertVersionedAsync(DomainId documentId, long oldVersion, MongoContentEntity entity) |
|
||||
{ |
|
||||
return Collection.UpsertVersionedAsync(documentId, oldVersion, entity.Version, entity); |
|
||||
} |
|
||||
|
|
||||
public Task RemoveAsync(DomainId documentId) |
|
||||
{ |
|
||||
return Collection.DeleteOneAsync(x => x.DocumentId == documentId); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,50 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using MongoDB.Bson.Serialization.Attributes; |
||||
|
using MongoDB.Driver; |
||||
|
using Squidex.Domain.Apps.Core.Contents; |
||||
|
using Squidex.Infrastructure; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations |
||||
|
{ |
||||
|
public static class Extensions |
||||
|
{ |
||||
|
public sealed class StatusModel |
||||
|
{ |
||||
|
[BsonId] |
||||
|
[BsonElement("_id")] |
||||
|
public DomainId DocumentId { get; set; } |
||||
|
|
||||
|
[BsonRequired] |
||||
|
[BsonElement("id")] |
||||
|
public DomainId Id { get; set; } |
||||
|
|
||||
|
[BsonRequired] |
||||
|
[BsonElement("_si")] |
||||
|
public DomainId IndexedSchemaId { get; set; } |
||||
|
|
||||
|
[BsonRequired] |
||||
|
[BsonElement("ss")] |
||||
|
public Status Status { get; set; } |
||||
|
} |
||||
|
|
||||
|
public static Task<List<StatusModel>> FindStatusAsync(this IMongoCollection<MongoContentEntity> collection, FilterDefinition<MongoContentEntity> filter) |
||||
|
{ |
||||
|
var projections = Builders<MongoContentEntity>.Projection; |
||||
|
|
||||
|
return collection.Find(filter) |
||||
|
.Project<StatusModel>(projections |
||||
|
.Include(x => x.Id) |
||||
|
.Include(x => x.IndexedSchemaId) |
||||
|
.Include(x => x.Status)) |
||||
|
.ToListAsync(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,64 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using MongoDB.Driver; |
||||
|
using Squidex.Domain.Apps.Entities.Contents; |
||||
|
using Squidex.Infrastructure; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations |
||||
|
{ |
||||
|
public sealed class QueryAsStream : OperationBase |
||||
|
{ |
||||
|
private readonly IAppProvider appProvider; |
||||
|
|
||||
|
public QueryAsStream(DataConverter converter, IAppProvider appProvider) |
||||
|
: base(converter) |
||||
|
{ |
||||
|
this.appProvider = appProvider; |
||||
|
} |
||||
|
|
||||
|
protected override async Task PrepareAsync(CancellationToken ct = default) |
||||
|
{ |
||||
|
var indexBySchema = |
||||
|
new CreateIndexModel<MongoContentEntity>(Index |
||||
|
.Ascending(x => x.IndexedAppId) |
||||
|
.Ascending(x => x.IsDeleted) |
||||
|
.Ascending(x => x.IndexedSchemaId)); |
||||
|
|
||||
|
await Collection.Indexes.CreateOneAsync(indexBySchema, cancellationToken: ct); |
||||
|
} |
||||
|
|
||||
|
public async IAsyncEnumerable<IContentEntity> StreamAll(DomainId appId, HashSet<DomainId>? schemaIds) |
||||
|
{ |
||||
|
var find = |
||||
|
schemaIds != null ? |
||||
|
Collection.Find(x => x.IndexedAppId == appId && !x.IsDeleted && schemaIds.Contains(x.IndexedSchemaId)) : |
||||
|
Collection.Find(x => x.IndexedAppId == appId && !x.IsDeleted); |
||||
|
|
||||
|
using (var cursor = await find.ToCursorAsync()) |
||||
|
{ |
||||
|
while (await cursor.MoveNextAsync()) |
||||
|
{ |
||||
|
foreach (var entity in cursor.Current) |
||||
|
{ |
||||
|
var schema = await appProvider.GetSchemaAsync(appId, entity.SchemaId.Id, false); |
||||
|
|
||||
|
if (schema != null) |
||||
|
{ |
||||
|
entity.ParseData(schema.SchemaDef, DataConverter); |
||||
|
|
||||
|
yield return entity; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,112 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using MongoDB.Driver; |
||||
|
using Squidex.Domain.Apps.Core.Contents; |
||||
|
using Squidex.Domain.Apps.Entities.Contents; |
||||
|
using Squidex.Domain.Apps.Entities.Schemas; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.MongoDb.Queries; |
||||
|
using Squidex.Infrastructure.Queries; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations |
||||
|
{ |
||||
|
internal sealed class QueryByIds : OperationBase |
||||
|
{ |
||||
|
public QueryByIds(DataConverter dataConverter) |
||||
|
: base(dataConverter) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public async Task<IReadOnlyList<(DomainId SchemaId, DomainId Id, Status Status)>> QueryIdsAsync(DomainId appId, HashSet<DomainId> ids) |
||||
|
{ |
||||
|
if (ids == null || ids.Count == 0) |
||||
|
{ |
||||
|
return new List<(DomainId SchemaId, DomainId Id, Status Status)>(); |
||||
|
} |
||||
|
|
||||
|
var filter = CreateFilter(appId, null, ids); |
||||
|
|
||||
|
var contentItems = await Collection.FindStatusAsync(filter); |
||||
|
|
||||
|
return contentItems.Select(x => (x.IndexedSchemaId, x.Id, x.Status)).ToList(); |
||||
|
} |
||||
|
|
||||
|
public async Task<IResultList<IContentEntity>> QueryAsync(DomainId appId, List<ISchemaEntity> schemas, Q q) |
||||
|
{ |
||||
|
Guard.NotNull(q, nameof(q)); |
||||
|
|
||||
|
if (q.Ids == null || q.Ids.Count == 0) |
||||
|
{ |
||||
|
return ResultList.CreateFrom<IContentEntity>(0); |
||||
|
} |
||||
|
|
||||
|
var filter = CreateFilter(appId, schemas.Select(x => x.Id), q.Ids.ToHashSet()); |
||||
|
|
||||
|
var items = await FindContentsAsync(q.Query, filter); |
||||
|
|
||||
|
if (items.Count > 0) |
||||
|
{ |
||||
|
var contentSchemas = schemas.ToDictionary(x => x.Id); |
||||
|
|
||||
|
foreach (var content in items) |
||||
|
{ |
||||
|
var schema = contentSchemas[content.SchemaId.Id]; |
||||
|
|
||||
|
content.ParseData(schema.SchemaDef, DataConverter); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return ResultList.Create(items.Count, items); |
||||
|
} |
||||
|
|
||||
|
private async Task<List<MongoContentEntity>> FindContentsAsync(ClrQuery query, FilterDefinition<MongoContentEntity> filter) |
||||
|
{ |
||||
|
var result = |
||||
|
Collection.Find(filter) |
||||
|
.QueryLimit(query) |
||||
|
.QuerySkip(query) |
||||
|
.ToListAsync(); |
||||
|
|
||||
|
return await result; |
||||
|
} |
||||
|
|
||||
|
private static FilterDefinition<MongoContentEntity> CreateFilter(DomainId appId, IEnumerable<DomainId>? schemaIds, HashSet<DomainId> ids) |
||||
|
{ |
||||
|
var filters = new List<FilterDefinition<MongoContentEntity>>(); |
||||
|
|
||||
|
var documentIds = ids.Select(x => DomainId.Combine(appId, x)).ToList(); |
||||
|
|
||||
|
if (documentIds.Count > 1) |
||||
|
{ |
||||
|
filters.Add( |
||||
|
Filter.Or( |
||||
|
Filter.In(x => x.DocumentId, documentIds))); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
var first = documentIds.First(); |
||||
|
|
||||
|
filters.Add( |
||||
|
Filter.Or( |
||||
|
Filter.Eq(x => x.DocumentId, first))); |
||||
|
} |
||||
|
|
||||
|
if (schemaIds != null) |
||||
|
{ |
||||
|
filters.Add(Filter.In(x => x.IndexedSchemaId, schemaIds)); |
||||
|
} |
||||
|
|
||||
|
filters.Add(Filter.Ne(x => x.IsDeleted, true)); |
||||
|
|
||||
|
return Filter.And(filters); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,111 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Threading.Tasks; |
|
||||
using MongoDB.Driver; |
|
||||
using Squidex.Domain.Apps.Entities.Contents; |
|
||||
using Squidex.Domain.Apps.Entities.Schemas; |
|
||||
using Squidex.Infrastructure; |
|
||||
|
|
||||
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations |
|
||||
{ |
|
||||
internal sealed class QueryContentsByIds : OperationBase |
|
||||
{ |
|
||||
private readonly DataConverter converter; |
|
||||
private readonly IAppProvider appProvider; |
|
||||
|
|
||||
public QueryContentsByIds(DataConverter converter, IAppProvider appProvider) |
|
||||
{ |
|
||||
this.converter = converter; |
|
||||
|
|
||||
this.appProvider = appProvider; |
|
||||
} |
|
||||
|
|
||||
public async Task<List<(IContentEntity Content, ISchemaEntity Schema)>> DoAsync(DomainId appId, ISchemaEntity? schema, HashSet<DomainId> ids, bool canCache) |
|
||||
{ |
|
||||
Guard.NotNull(ids, nameof(ids)); |
|
||||
|
|
||||
var find = Collection.Find(CreateFilter(appId, ids)); |
|
||||
|
|
||||
var contentItems = await find.ToListAsync(); |
|
||||
var contentSchemas = await GetSchemasAsync(appId, schema, contentItems, canCache); |
|
||||
|
|
||||
var result = new List<(IContentEntity Content, ISchemaEntity Schema)>(); |
|
||||
|
|
||||
foreach (var contentEntity in contentItems) |
|
||||
{ |
|
||||
if (contentSchemas.TryGetValue(contentEntity.IndexedSchemaId, out var contentSchema)) |
|
||||
{ |
|
||||
contentEntity.ParseData(contentSchema.SchemaDef, converter); |
|
||||
|
|
||||
result.Add((contentEntity, contentSchema)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
private async Task<IDictionary<DomainId, ISchemaEntity>> GetSchemasAsync(DomainId appId, ISchemaEntity? schema, List<MongoContentEntity> contentItems, bool canCache) |
|
||||
{ |
|
||||
var schemas = new Dictionary<DomainId, ISchemaEntity>(); |
|
||||
|
|
||||
if (schema != null) |
|
||||
{ |
|
||||
schemas[schema.Id] = schema; |
|
||||
} |
|
||||
|
|
||||
var schemaIds = contentItems.Select(x => x.IndexedSchemaId).Distinct(); |
|
||||
|
|
||||
foreach (var schemaId in schemaIds) |
|
||||
{ |
|
||||
if (!schemas.ContainsKey(schemaId)) |
|
||||
{ |
|
||||
var found = await appProvider.GetSchemaAsync(appId, schemaId, false, canCache); |
|
||||
|
|
||||
if (found != null) |
|
||||
{ |
|
||||
schemas[schemaId] = found; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return schemas; |
|
||||
} |
|
||||
|
|
||||
private static FilterDefinition<MongoContentEntity> CreateFilter(DomainId appId, ICollection<DomainId> ids) |
|
||||
{ |
|
||||
var filters = new List<FilterDefinition<MongoContentEntity>> |
|
||||
{ |
|
||||
Filter.Ne(x => x.IsDeleted, true) |
|
||||
}; |
|
||||
|
|
||||
if (ids != null && ids.Count > 0) |
|
||||
{ |
|
||||
var documentIds = ids.Select(x => DomainId.Combine(appId, x)).ToList(); |
|
||||
|
|
||||
if (ids.Count > 1) |
|
||||
{ |
|
||||
filters.Add( |
|
||||
Filter.Or( |
|
||||
Filter.In(x => x.DocumentId, documentIds))); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
var first = documentIds.First(); |
|
||||
|
|
||||
filters.Add( |
|
||||
Filter.Or( |
|
||||
Filter.Eq(x => x.DocumentId, first))); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return Filter.And(filters); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,98 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
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.Infrastructure; |
|
||||
using Squidex.Infrastructure.MongoDb; |
|
||||
using Squidex.Infrastructure.MongoDb.Queries; |
|
||||
using Squidex.Infrastructure.Queries; |
|
||||
|
|
||||
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations |
|
||||
{ |
|
||||
internal sealed class QueryIdsAsync : OperationBase |
|
||||
{ |
|
||||
private static readonly List<(DomainId SchemaId, DomainId Id, Status Status)> EmptyIds = new List<(DomainId SchemaId, DomainId Id, Status Status)>(); |
|
||||
private readonly IAppProvider appProvider; |
|
||||
|
|
||||
public QueryIdsAsync(IAppProvider appProvider) |
|
||||
{ |
|
||||
this.appProvider = appProvider; |
|
||||
} |
|
||||
|
|
||||
protected override Task PrepareAsync(CancellationToken ct = default) |
|
||||
{ |
|
||||
var index = |
|
||||
new CreateIndexModel<MongoContentEntity>(Index |
|
||||
.Ascending(x => x.IndexedAppId) |
|
||||
.Ascending(x => x.IndexedSchemaId) |
|
||||
.Ascending(x => x.IsDeleted)); |
|
||||
|
|
||||
return Collection.Indexes.CreateOneAsync(index, cancellationToken: ct); |
|
||||
} |
|
||||
|
|
||||
public async Task<IReadOnlyList<(DomainId SchemaId, DomainId Id, Status Status)>> DoAsync(DomainId appId, HashSet<DomainId> ids) |
|
||||
{ |
|
||||
var documentIds = ids.Select(x => DomainId.Combine(appId, x)); |
|
||||
|
|
||||
var filter = |
|
||||
Filter.And( |
|
||||
Filter.In(x => x.DocumentId, documentIds), |
|
||||
Filter.Ne(x => x.IsDeleted, true)); |
|
||||
|
|
||||
return await SearchAsync(filter); |
|
||||
} |
|
||||
|
|
||||
public async Task<IReadOnlyList<(DomainId SchemaId, DomainId Id, Status Status)>> DoAsync(DomainId appId, DomainId schemaId, FilterNode<ClrValue> filterNode) |
|
||||
{ |
|
||||
var schema = await appProvider.GetSchemaAsync(appId, schemaId, false); |
|
||||
|
|
||||
if (schema == null) |
|
||||
{ |
|
||||
return EmptyIds; |
|
||||
} |
|
||||
|
|
||||
var filter = BuildFilter(filterNode.AdjustToModel(schema.SchemaDef), appId, schemaId); |
|
||||
|
|
||||
return await SearchAsync(filter); |
|
||||
} |
|
||||
|
|
||||
private async Task<IReadOnlyList<(DomainId SchemaId, DomainId Id, Status Status)>> SearchAsync(FilterDefinition<MongoContentEntity> filter) |
|
||||
{ |
|
||||
var contentEntities = |
|
||||
await Collection.Find(filter).Only(x => x.Id, x => x.IndexedSchemaId, x => x.Status) |
|
||||
.ToListAsync(); |
|
||||
|
|
||||
return contentEntities.Select(x => ( |
|
||||
DomainId.Create(x[Fields.SchemaId].AsString), |
|
||||
DomainId.Create(x[Fields.Id].AsString), |
|
||||
new Status(x[Fields.Status].AsString) |
|
||||
)).ToList(); |
|
||||
} |
|
||||
|
|
||||
private static FilterDefinition<MongoContentEntity> BuildFilter(FilterNode<ClrValue>? filterNode, DomainId appId, DomainId schemaId) |
|
||||
{ |
|
||||
var filters = new List<FilterDefinition<MongoContentEntity>> |
|
||||
{ |
|
||||
Filter.Eq(x => x.IndexedAppId, appId), |
|
||||
Filter.Eq(x => x.IndexedSchemaId, schemaId), |
|
||||
Filter.Ne(x => x.IsDeleted, true) |
|
||||
}; |
|
||||
|
|
||||
if (filterNode != null) |
|
||||
{ |
|
||||
filters.Add(filterNode.BuildFilter<MongoContentEntity>()); |
|
||||
} |
|
||||
|
|
||||
return Filter.And(filters); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,66 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using MongoDB.Bson.Serialization.Attributes; |
||||
|
using MongoDB.Driver; |
||||
|
using Squidex.Domain.Apps.Entities.Contents; |
||||
|
using Squidex.Domain.Apps.Entities.Schemas; |
||||
|
using Squidex.Infrastructure; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations |
||||
|
{ |
||||
|
internal class QueryReferences : OperationBase |
||||
|
{ |
||||
|
private static readonly IResultList<IContentEntity> EmptyIds = ResultList.CreateFrom<IContentEntity>(0); |
||||
|
private readonly QueryByIds queryByIds; |
||||
|
|
||||
|
public sealed class ReferencedIdsOnly |
||||
|
{ |
||||
|
[BsonId] |
||||
|
[BsonElement("_id")] |
||||
|
public DomainId DocumentId { get; set; } |
||||
|
|
||||
|
[BsonRequired] |
||||
|
[BsonElement("rf")] |
||||
|
public HashSet<DomainId>? ReferencedIds { get; set; } |
||||
|
} |
||||
|
|
||||
|
public QueryReferences(DataConverter dataConverter, QueryByIds queryByIds) |
||||
|
: base(dataConverter) |
||||
|
{ |
||||
|
this.queryByIds = queryByIds; |
||||
|
} |
||||
|
|
||||
|
public async Task<IResultList<IContentEntity>> QueryAsync(DomainId appId, List<ISchemaEntity> schemas, Q q) |
||||
|
{ |
||||
|
var documentId = DomainId.Combine(appId, q.Referencing); |
||||
|
|
||||
|
var find = |
||||
|
Collection |
||||
|
.Find(x => x.DocumentId == documentId) |
||||
|
.Project<ReferencedIdsOnly>(Projection.Include(x => x.ReferencedIds)); |
||||
|
|
||||
|
var contentEntity = await find.FirstOrDefaultAsync(); |
||||
|
|
||||
|
if (contentEntity == null) |
||||
|
{ |
||||
|
throw new DomainObjectNotFoundException(q.Referencing.ToString()); |
||||
|
} |
||||
|
|
||||
|
if (contentEntity.ReferencedIds == null || contentEntity.ReferencedIds.Count == 0) |
||||
|
{ |
||||
|
return EmptyIds; |
||||
|
} |
||||
|
|
||||
|
q = q.WithReferencing(default).WithIds(contentEntity.ReferencedIds!); |
||||
|
|
||||
|
return await queryByIds.QueryAsync(appId, schemas, q); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue