Browse Source

Query by array field.

pull/353/head
Sebastian Stehle 7 years ago
parent
commit
5243842bc6
  1. 35
      src/Squidex.Domain.Apps.Core.Operations/GenerateEdmSchema/EdmSchemaExtensions.cs
  2. 27
      src/Squidex.Domain.Apps.Core.Operations/GenerateEdmSchema/EdmTypeVisitor.cs
  3. 12
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Visitors/FilterFactory.cs
  4. 2
      src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs
  5. 44
      src/Squidex.Domain.Apps.Entities/Contents/Edm/EdmModelBuilder.cs
  6. 8
      tests/Squidex.Domain.Apps.Core.Tests/Operations/GenerateEdmSchema/EdmTests.cs
  7. 2
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs
  8. 11
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/MongoDbQueryTests.cs

35
src/Squidex.Domain.Apps.Core.Operations/GenerateEdmSchema/EdmSchemaExtensions.cs

@ -5,14 +5,14 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Linq;
using Microsoft.OData.Edm;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.GenerateEdmSchema
{
public delegate (EdmComplexType Type, bool Created) EdmTypeFactory(string names);
public static class EdmSchemaExtensions
{
public static string EscapeEdmField(this string field)
@ -25,30 +25,39 @@ namespace Squidex.Domain.Apps.Core.GenerateEdmSchema
return field.Replace("_", "-");
}
public static EdmComplexType BuildEdmType(this Schema schema, PartitionResolver partitionResolver, Func<EdmComplexType, EdmComplexType> typeResolver)
public static EdmComplexType BuildEdmType(this Schema schema, bool withHidden, PartitionResolver partitionResolver, EdmTypeFactory typeFactory)
{
Guard.NotNull(typeResolver, nameof(typeResolver));
Guard.NotNull(typeFactory, nameof(typeFactory));
Guard.NotNull(partitionResolver, nameof(partitionResolver));
var schemaName = schema.Name.ToPascalCase();
var (edmType, _) = typeFactory("Data");
var edmType = new EdmComplexType("Squidex", schemaName);
var visitor = new EdmTypeVisitor(typeFactory);
foreach (var field in schema.FieldsByName.Values.Where(x => !x.IsHidden))
foreach (var field in schema.FieldsByName.Values)
{
var edmValueType = EdmTypeVisitor.CreateEdmType(field);
if (!withHidden && field.IsHidden)
{
continue;
}
var fieldEdmType = field.Accept(visitor);
if (edmValueType == null)
if (fieldEdmType == null)
{
continue;
}
var partitionType = typeResolver(new EdmComplexType("Squidex", $"{schemaName}{field.Name.ToPascalCase()}Property"));
var partition = partitionResolver(field.Partitioning);
var (partitionType, created) = typeFactory($"Data.{field.Name.ToPascalCase()}");
foreach (var partitionItem in partition)
if (created)
{
partitionType.AddStructuralProperty(partitionItem.Key.EscapeEdmField(), edmValueType);
var partition = partitionResolver(field.Partitioning);
foreach (var partitionItem in partition)
{
partitionType.AddStructuralProperty(partitionItem.Key.EscapeEdmField(), fieldEdmType);
}
}
edmType.AddStructuralProperty(field.Name.EscapeEdmField(), new EdmComplexTypeReference(partitionType, false));

27
src/Squidex.Domain.Apps.Core.Operations/GenerateEdmSchema/EdmTypeVisitor.cs

@ -7,25 +7,42 @@
using Microsoft.OData.Edm;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.GenerateEdmSchema
{
public sealed class EdmTypeVisitor : IFieldVisitor<IEdmTypeReference>
{
private static readonly EdmTypeVisitor Instance = new EdmTypeVisitor();
private readonly EdmTypeFactory typeFactory;
private EdmTypeVisitor()
internal EdmTypeVisitor(EdmTypeFactory typeFactory)
{
this.typeFactory = typeFactory;
}
public static IEdmTypeReference CreateEdmType(IField field)
public IEdmTypeReference CreateEdmType(IField field)
{
return field.Accept(Instance);
return field.Accept(this);
}
public IEdmTypeReference Visit(IArrayField field)
{
return null;
var (fieldEdmType, created) = typeFactory($"Data.{field.Name.ToPascalCase()}.Item");
if (created)
{
foreach (var nestedField in field.Fields)
{
var nestedEdmType = nestedField.Accept(this);
if (nestedEdmType != null)
{
fieldEdmType.AddStructuralProperty(nestedField.Name.EscapeEdmField(), nestedEdmType);
}
}
}
return new EdmComplexTypeReference(fieldEdmType, false);
}
public IEdmTypeReference Visit(IField<AssetsFieldProperties> field)

12
src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Visitors/FilterFactory.cs

@ -65,6 +65,18 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Visitors
}
result[1] = field.Id.ToString();
if (field is IArrayField arrayField && result.Count > 3)
{
var nestedEdmName = result[3].UnescapeEdmField();
if (!arrayField.FieldsByName.TryGetValue(nestedEdmName, out var nestedField))
{
throw new NotSupportedException();
}
result[3] = nestedField.Id.ToString();
}
}
if (result.Count > 2)

2
src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs

@ -262,7 +262,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
try
{
var model = modelBuilder.BuildEdmModel(schema, context.App);
var model = modelBuilder.BuildEdmModel(context.App, schema, context.IsFrontendClient);
var result = model.ParseQuery(query).ToQuery();

44
src/Squidex.Domain.Apps.Entities/Contents/Edm/EdmModelBuilder.cs

@ -6,6 +6,7 @@
// ==========================================================================
using System;
using System.Linq;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.OData.Edm;
using Squidex.Domain.Apps.Core;
@ -26,34 +27,56 @@ namespace Squidex.Domain.Apps.Entities.Contents.Edm
{
}
public virtual IEdmModel BuildEdmModel(ISchemaEntity schema, IAppEntity app)
public virtual IEdmModel BuildEdmModel(IAppEntity app, ISchemaEntity schema, bool withHidden)
{
Guard.NotNull(schema, nameof(schema));
var cacheKey = $"{schema.Id}_{schema.Version}_{app.Id}_{app.Version}";
var cacheKey = BuildCacheKey(app, schema, withHidden);
var result = Cache.GetOrCreate<IEdmModel>(cacheKey, entry =>
{
entry.AbsoluteExpirationRelativeToNow = CacheTime;
return BuildEdmModel(schema.SchemaDef, app.PartitionResolver());
return BuildEdmModel(schema.SchemaDef, app, withHidden);
});
return result;
}
private static EdmModel BuildEdmModel(Schema schema, PartitionResolver partitionResolver)
private static EdmModel BuildEdmModel(Schema schema, IAppEntity app, bool withHidden)
{
var model = new EdmModel();
var schemaType = schema.BuildEdmType(partitionResolver, x =>
var pascalAppName = app.Name.ToPascalCase();
var pascalSchemaName = schema.Name.ToPascalCase();
var typeFactory = new EdmTypeFactory(name =>
{
model.AddElement(x);
var finalName = pascalSchemaName;
if (!string.IsNullOrWhiteSpace(name))
{
finalName += ".";
finalName += name;
}
var result = model.SchemaElements.OfType<EdmComplexType>().FirstOrDefault(x => x.Name == finalName);
if (result != null)
{
return (result, false);
}
result = new EdmComplexType(pascalAppName, finalName);
return x;
model.AddElement(result);
return (result, true);
});
var entityType = new EdmEntityType("Squidex", schema.Name);
var schemaType = schema.BuildEdmType(withHidden, app.PartitionResolver(), typeFactory);
var entityType = new EdmEntityType(app.Name.ToPascalCase(), schema.Name);
entityType.AddStructuralProperty(nameof(IContentEntity.Id).ToCamelCase(), EdmPrimitiveTypeKind.String);
entityType.AddStructuralProperty(nameof(IContentEntity.Created).ToCamelCase(), EdmPrimitiveTypeKind.DateTimeOffset);
entityType.AddStructuralProperty(nameof(IContentEntity.CreatedBy).ToCamelCase(), EdmPrimitiveTypeKind.String);
@ -73,5 +96,10 @@ namespace Squidex.Domain.Apps.Entities.Contents.Edm
return model;
}
private static string BuildCacheKey(IAppEntity app, ISchemaEntity schema, bool withHidden)
{
return string.Join("_", schema.Id, schema.Version, app.Id, app.Version, withHidden);
}
}
}

8
tests/Squidex.Domain.Apps.Core.Tests/Operations/GenerateEdmSchema/EdmTests.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Microsoft.OData.Edm;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Core.GenerateEdmSchema;
using Squidex.Infrastructure;
@ -31,7 +32,12 @@ namespace Squidex.Domain.Apps.Core.Operations.GenerateEdmSchema
{
var languagesConfig = LanguagesConfig.Build(Language.DE, Language.EN);
var edmModel = TestUtils.MixedSchema().BuildEdmType(languagesConfig.ToResolver(), x => x);
var typeFactory = new EdmTypeFactory(names =>
{
return (new EdmComplexType("Squidex", string.Join(".", names)), true);
});
var edmModel = TestUtils.MixedSchema().BuildEdmType(true, languagesConfig.ToResolver(), typeFactory);
Assert.NotNull(edmModel);
}

2
tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs

@ -310,7 +310,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
SetupClaims();
SetupSchema();
A.CallTo(() => modelBuilder.BuildEdmModel(schema, app))
A.CallTo(() => modelBuilder.BuildEdmModel(app, schema, A<bool>.Ignored))
.Throws(new ODataException());
return Assert.ThrowsAsync<ValidationException>(() => sut.QueryAsync(context, schemaId.Name, Q.Empty.WithODataQuery("query")));

11
tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/MongoDbQueryTests.cs

@ -60,6 +60,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
new ReferencesFieldProperties())
.AddString(8, "dashed-field", Partitioning.Invariant,
new StringFieldProperties())
.AddArray(9, "hobbies", Partitioning.Invariant, a => a
.AddString(91, "name"))
.Update(new SchemaProperties());
var schema = A.Dummy<ISchemaEntity>();
@ -178,6 +180,15 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
Assert.Equal(o, i);
}
[Fact]
public void Should_make_query_with_array_field()
{
var i = F(FilterBuilder.Eq("data/hobbies/iv/name", "PC"));
var o = C("{ 'do.9.iv.91' : 'PC' }");
Assert.Equal(o, i);
}
[Fact]
public void Should_make_query_with_assets_equals()
{

Loading…
Cancel
Save