mirror of https://github.com/Squidex/squidex.git
34 changed files with 556 additions and 137 deletions
@ -0,0 +1,32 @@ |
|||||
|
// ==========================================================================
|
||||
|
// ConstantVisitor.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using Microsoft.OData.Core.UriParser.Semantic; |
||||
|
using Microsoft.OData.Core.UriParser.Visitors; |
||||
|
|
||||
|
namespace Squidex.Store.MongoDb.Contents.Visitors |
||||
|
{ |
||||
|
public sealed class ConstantVisitor : QueryNodeVisitor<object> |
||||
|
{ |
||||
|
private static readonly ConstantVisitor Instance = new ConstantVisitor(); |
||||
|
|
||||
|
private ConstantVisitor() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public static object Visit(QueryNode node) |
||||
|
{ |
||||
|
return node.Accept(Instance); |
||||
|
} |
||||
|
|
||||
|
public override object Visit(ConstantNode nodeIn) |
||||
|
{ |
||||
|
return nodeIn.Value; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,39 @@ |
|||||
|
// ==========================================================================
|
||||
|
// FilterBuilder.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using Microsoft.OData.Core.UriParser; |
||||
|
using MongoDB.Driver; |
||||
|
using Squidex.Core.Schemas; |
||||
|
// ReSharper disable ConvertIfStatementToReturnStatement
|
||||
|
|
||||
|
namespace Squidex.Store.MongoDb.Contents.Visitors |
||||
|
{ |
||||
|
public static class FilterBuilder |
||||
|
{ |
||||
|
private static readonly FilterDefinitionBuilder<MongoContentEntity> Filter = Builders<MongoContentEntity>.Filter; |
||||
|
|
||||
|
public static FilterDefinition<MongoContentEntity> Build(ODataUriParser query, Schema schema) |
||||
|
{ |
||||
|
var search = query.ParseSearch(); |
||||
|
|
||||
|
if (search != null) |
||||
|
{ |
||||
|
return Filter.Text(ConstantVisitor.Visit(search.Expression).ToString()); |
||||
|
} |
||||
|
|
||||
|
var filter = query.ParseFilter(); |
||||
|
|
||||
|
if (filter != null) |
||||
|
{ |
||||
|
return FilterVisitor.Visit(filter.Expression, schema); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,95 @@ |
|||||
|
// ==========================================================================
|
||||
|
// FilterVisitor.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using Microsoft.OData.Core.UriParser.Semantic; |
||||
|
using Microsoft.OData.Core.UriParser.TreeNodeKinds; |
||||
|
using Microsoft.OData.Core.UriParser.Visitors; |
||||
|
using MongoDB.Driver; |
||||
|
using Squidex.Core.Schemas; |
||||
|
// ReSharper disable SwitchStatementMissingSomeCases
|
||||
|
// ReSharper disable ConvertIfStatementToSwitchStatement
|
||||
|
|
||||
|
namespace Squidex.Store.MongoDb.Contents.Visitors |
||||
|
{ |
||||
|
public class FilterVisitor : QueryNodeVisitor<FilterDefinition<MongoContentEntity>> |
||||
|
{ |
||||
|
private static readonly FilterDefinitionBuilder<MongoContentEntity> Filter = Builders<MongoContentEntity>.Filter; |
||||
|
private readonly Schema schema; |
||||
|
|
||||
|
private FilterVisitor(Schema schema) |
||||
|
{ |
||||
|
this.schema = schema; |
||||
|
} |
||||
|
|
||||
|
public static FilterDefinition<MongoContentEntity> Visit(QueryNode node, Schema schema) |
||||
|
{ |
||||
|
var visitor = new FilterVisitor(schema); |
||||
|
|
||||
|
return node.Accept(visitor); |
||||
|
} |
||||
|
|
||||
|
public override FilterDefinition<MongoContentEntity> Visit(UnaryOperatorNode nodeIn) |
||||
|
{ |
||||
|
if (nodeIn.OperatorKind == UnaryOperatorKind.Not) |
||||
|
{ |
||||
|
return Filter.Not(nodeIn.Operand.Accept(this)); |
||||
|
} |
||||
|
|
||||
|
throw new NotSupportedException(); |
||||
|
} |
||||
|
|
||||
|
public override FilterDefinition<MongoContentEntity> Visit(BinaryOperatorNode nodeIn) |
||||
|
{ |
||||
|
if (nodeIn.OperatorKind == BinaryOperatorKind.And) |
||||
|
{ |
||||
|
return Filter.And(nodeIn.Left.Accept(this), nodeIn.Right.Accept(this)); |
||||
|
} |
||||
|
if (nodeIn.OperatorKind == BinaryOperatorKind.Or) |
||||
|
{ |
||||
|
return Filter.Or(nodeIn.Left.Accept(this), nodeIn.Right.Accept(this)); |
||||
|
} |
||||
|
if (nodeIn.OperatorKind == BinaryOperatorKind.NotEqual) |
||||
|
{ |
||||
|
return Filter.Ne(BuildFieldDefinition(nodeIn.Left), BuildValue(nodeIn.Right)); |
||||
|
} |
||||
|
if (nodeIn.OperatorKind == BinaryOperatorKind.Equal) |
||||
|
{ |
||||
|
return Filter.Eq(BuildFieldDefinition(nodeIn.Left), BuildValue(nodeIn.Right)); |
||||
|
} |
||||
|
if (nodeIn.OperatorKind == BinaryOperatorKind.LessThan) |
||||
|
{ |
||||
|
return Filter.Lt(BuildFieldDefinition(nodeIn.Left), BuildValue(nodeIn.Right)); |
||||
|
} |
||||
|
if (nodeIn.OperatorKind == BinaryOperatorKind.LessThanOrEqual) |
||||
|
{ |
||||
|
return Filter.Lte(BuildFieldDefinition(nodeIn.Left), BuildValue(nodeIn.Right)); |
||||
|
} |
||||
|
if (nodeIn.OperatorKind == BinaryOperatorKind.GreaterThan) |
||||
|
{ |
||||
|
return Filter.Gt(BuildFieldDefinition(nodeIn.Left), BuildValue(nodeIn.Right)); |
||||
|
} |
||||
|
if (nodeIn.OperatorKind == BinaryOperatorKind.GreaterThanOrEqual) |
||||
|
{ |
||||
|
return Filter.Gte(BuildFieldDefinition(nodeIn.Left), BuildValue(nodeIn.Right)); |
||||
|
} |
||||
|
|
||||
|
throw new NotSupportedException(); |
||||
|
} |
||||
|
|
||||
|
private FieldDefinition<MongoContentEntity, object> BuildFieldDefinition(QueryNode nodeIn) |
||||
|
{ |
||||
|
return PropertyVisitor.Visit(nodeIn, schema); |
||||
|
} |
||||
|
|
||||
|
private static object BuildValue(QueryNode nodeIn) |
||||
|
{ |
||||
|
return ConstantVisitor.Visit(nodeIn); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,72 @@ |
|||||
|
// ==========================================================================
|
||||
|
// FindExtensions.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using Microsoft.OData.Core.UriParser; |
||||
|
using MongoDB.Driver; |
||||
|
using Squidex.Core.Schemas; |
||||
|
|
||||
|
namespace Squidex.Store.MongoDb.Contents.Visitors |
||||
|
{ |
||||
|
public static class FindExtensions |
||||
|
{ |
||||
|
private static readonly FilterDefinitionBuilder<MongoContentEntity> Filter = Builders<MongoContentEntity>.Filter; |
||||
|
|
||||
|
public static IFindFluent<MongoContentEntity, MongoContentEntity> Sort(this IFindFluent<MongoContentEntity, MongoContentEntity> cursor, ODataUriParser query, Schema schema) |
||||
|
{ |
||||
|
|
||||
|
return cursor.Sort(SortBuilder.BuildSort(query, schema)); |
||||
|
} |
||||
|
|
||||
|
public static IFindFluent<MongoContentEntity, MongoContentEntity> Take(this IFindFluent<MongoContentEntity, MongoContentEntity> cursor, ODataUriParser query) |
||||
|
{ |
||||
|
var top = query.ParseTop(); |
||||
|
|
||||
|
if (top.HasValue) |
||||
|
{ |
||||
|
cursor = cursor.Limit((int)top.Value); |
||||
|
} |
||||
|
|
||||
|
return cursor; |
||||
|
} |
||||
|
|
||||
|
public static IFindFluent<MongoContentEntity, MongoContentEntity> Skip(this IFindFluent<MongoContentEntity, MongoContentEntity> cursor, ODataUriParser query) |
||||
|
{ |
||||
|
var skip = query.ParseSkip(); |
||||
|
|
||||
|
if (skip.HasValue) |
||||
|
{ |
||||
|
cursor = cursor.Skip((int)skip.Value); |
||||
|
} |
||||
|
|
||||
|
return cursor; |
||||
|
} |
||||
|
|
||||
|
public static IFindFluent<MongoContentEntity, MongoContentEntity> Find(this IMongoCollection<MongoContentEntity> cursor, ODataUriParser query, Schema schema, bool nonPublished) |
||||
|
{ |
||||
|
var filters = new List<FilterDefinition<MongoContentEntity>> |
||||
|
{ |
||||
|
Filter.Eq(x => x.IsDeleted, false) |
||||
|
}; |
||||
|
|
||||
|
if (!nonPublished) |
||||
|
{ |
||||
|
filters.Add(Filter.Eq(x => x.IsPublished, false)); |
||||
|
} |
||||
|
|
||||
|
var filter = FilterBuilder.Build(query, schema); |
||||
|
|
||||
|
if (filter != null) |
||||
|
{ |
||||
|
filters.Add(filter); |
||||
|
} |
||||
|
|
||||
|
return cursor.Find(Filter.And(filters)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,63 @@ |
|||||
|
// ==========================================================================
|
||||
|
// PropertyVisitor.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Immutable; |
||||
|
using System.Linq; |
||||
|
using Microsoft.OData.Core.UriParser.Semantic; |
||||
|
using Microsoft.OData.Core.UriParser.Visitors; |
||||
|
using MongoDB.Driver; |
||||
|
using Squidex.Core.Schemas; |
||||
|
// ReSharper disable InvertIf
|
||||
|
// ReSharper disable RedundantIfElseBlock
|
||||
|
|
||||
|
namespace Squidex.Store.MongoDb.Contents.Visitors |
||||
|
{ |
||||
|
public sealed class PropertyVisitor : QueryNodeVisitor<ImmutableList<string>> |
||||
|
{ |
||||
|
private static readonly PropertyVisitor Instance = new PropertyVisitor(); |
||||
|
|
||||
|
public static StringFieldDefinition<MongoContentEntity, object> Visit(QueryNode node, Schema schema) |
||||
|
{ |
||||
|
var propertyNames = node.Accept(Instance).ToArray(); |
||||
|
|
||||
|
if (propertyNames.Length == 3) |
||||
|
{ |
||||
|
Field field; |
||||
|
|
||||
|
if (!schema.FieldsByName.TryGetValue(propertyNames[1], out field)) |
||||
|
{ |
||||
|
throw new NotSupportedException(); |
||||
|
} |
||||
|
|
||||
|
propertyNames[1] = field.Id.ToString(); |
||||
|
} |
||||
|
|
||||
|
var propertyName = string.Join(".", propertyNames); |
||||
|
|
||||
|
return new StringFieldDefinition<MongoContentEntity, object>(propertyName); |
||||
|
} |
||||
|
|
||||
|
public override ImmutableList<string> Visit(ConvertNode nodeIn) |
||||
|
{ |
||||
|
return nodeIn.Source.Accept(this); |
||||
|
} |
||||
|
|
||||
|
public override ImmutableList<string> Visit(SingleValuePropertyAccessNode nodeIn) |
||||
|
{ |
||||
|
if (nodeIn.Source is SingleValuePropertyAccessNode) |
||||
|
{ |
||||
|
return nodeIn.Source.Accept(this).Add(nodeIn.Property.Name); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return ImmutableList.Create(nodeIn.Property.Name); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,58 @@ |
|||||
|
// ==========================================================================
|
||||
|
// SchemaExtensions.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Microsoft.OData.Core.UriParser; |
||||
|
using Microsoft.OData.Edm; |
||||
|
using Microsoft.OData.Edm.Library; |
||||
|
using Squidex.Core.Schemas; |
||||
|
using Squidex.Infrastructure; |
||||
|
|
||||
|
namespace Squidex.Store.MongoDb.Contents.Visitors |
||||
|
{ |
||||
|
public static class SchemaExtensions |
||||
|
{ |
||||
|
public static EdmModel BuildEdmModel(this Schema schema, HashSet<Language> languages) |
||||
|
{ |
||||
|
var model = new EdmModel(); |
||||
|
var container = new EdmEntityContainer("Squidex", "Container"); |
||||
|
|
||||
|
var schemaType = schema.BuildEdmType(languages, x => |
||||
|
{ |
||||
|
model.AddElement(x); |
||||
|
|
||||
|
return x; |
||||
|
}); |
||||
|
|
||||
|
var entityType = new EdmEntityType("Squidex", schema.Name); |
||||
|
entityType.AddStructuralProperty("Data", new EdmComplexTypeReference(schemaType, false)); |
||||
|
entityType.AddStructuralProperty("Created", EdmPrimitiveTypeKind.Date); |
||||
|
entityType.AddStructuralProperty("CreatedBy", EdmPrimitiveTypeKind.String); |
||||
|
entityType.AddStructuralProperty("LastModified", EdmPrimitiveTypeKind.Date); |
||||
|
entityType.AddStructuralProperty("LastModifiedBy", EdmPrimitiveTypeKind.String); |
||||
|
|
||||
|
model.AddElement(container); |
||||
|
model.AddElement(schemaType); |
||||
|
model.AddElement(entityType); |
||||
|
|
||||
|
container.AddEntitySet($"{schema.Name}_Set", entityType); |
||||
|
|
||||
|
return model; |
||||
|
} |
||||
|
|
||||
|
public static ODataUriParser ParseQuery(this Schema schema, HashSet<Language> languages, string query) |
||||
|
{ |
||||
|
var model = schema.BuildEdmModel(languages); |
||||
|
|
||||
|
var parser = new ODataUriParser(model, new Uri($"{schema.Name}_Set?{query}", UriKind.Relative)); |
||||
|
|
||||
|
return parser; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,66 @@ |
|||||
|
// ==========================================================================
|
||||
|
// SortBuilder.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using Microsoft.OData.Core.UriParser; |
||||
|
using Microsoft.OData.Core.UriParser.Semantic; |
||||
|
using MongoDB.Driver; |
||||
|
using Squidex.Core.Schemas; |
||||
|
// ReSharper disable RedundantIfElseBlock
|
||||
|
// ReSharper disable ConvertIfStatementToReturnStatement
|
||||
|
// ReSharper disable ConvertIfStatementToConditionalTernaryExpression
|
||||
|
|
||||
|
namespace Squidex.Store.MongoDb.Contents.Visitors |
||||
|
{ |
||||
|
public static class SortBuilder |
||||
|
{ |
||||
|
private static readonly SortDefinitionBuilder<MongoContentEntity> Sort = Builders<MongoContentEntity>.Sort; |
||||
|
|
||||
|
public static SortDefinition<MongoContentEntity> BuildSort(ODataUriParser query, Schema schema) |
||||
|
{ |
||||
|
var orderBy = query.ParseOrderBy(); |
||||
|
|
||||
|
if (orderBy != null) |
||||
|
{ |
||||
|
var sorts = new List<SortDefinition<MongoContentEntity>>(); |
||||
|
|
||||
|
while (orderBy != null) |
||||
|
{ |
||||
|
sorts.Add(OrderBy(orderBy, schema)); |
||||
|
|
||||
|
orderBy = orderBy.ThenBy; |
||||
|
} |
||||
|
|
||||
|
if (sorts.Count > 1) |
||||
|
{ |
||||
|
return Sort.Combine(sorts); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return sorts[0]; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return Sort.Descending(x => x.LastModified); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static SortDefinition<MongoContentEntity> OrderBy(OrderByClause clause, Schema schema) |
||||
|
{ |
||||
|
if (clause.Direction == OrderByDirection.Ascending) |
||||
|
{ |
||||
|
return Sort.Ascending(PropertyVisitor.Visit(clause.Expression, schema)); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return Sort.Descending(PropertyVisitor.Visit(clause.Expression, schema)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue