Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose Content Fields Indexing via GraphQL for content field filtering #16092

Merged
merged 63 commits into from
Jun 15, 2024
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
cd1c0dc
Expose ContentFields as input fields, add more content definition eve…
May 17, 2024
5dbce74
Merge branch 'OrchardCMS:main' into graph-dynamic-fields-filter
mdameer May 17, 2024
9d132e8
Exclude empty input parts
May 17, 2024
2a34d2f
Merge branch 'OrchardCMS:main' into graph-dynamic-fields-filter
mdameer May 17, 2024
44a202f
Remove unused usings, fix injecting for nullable dependency "fieldsIn…
May 18, 2024
83da283
Merge branch 'OrchardCMS:main' into graph-dynamic-fields-filter
mdameer May 18, 2024
0d0d0a9
Merge branch 'OrchardCMS:main' into graph-dynamic-fields-filter
mdameer May 19, 2024
3500572
Merge branch 'main' into graph-dynamic-fields-filter
hishamco May 20, 2024
26d07fc
Merge branch 'OrchardCMS:main' into graph-dynamic-fields-filter
mdameer May 20, 2024
d8fa1cc
Fix multi tenant issue with dynamic index alias provider
May 20, 2024
387ea80
Fix duplicate aliases
May 23, 2024
acad591
Merge branch 'OrchardCMS:main' into graph-dynamic-fields-filter
mdameer May 23, 2024
63427b2
Merge branch 'OrchardCMS:main' into graph-dynamic-fields-filter
mdameer May 24, 2024
36afb33
Merge branch 'OrchardCMS:main' into graph-dynamic-fields-filter
mdameer May 25, 2024
3c0d85c
Merge branch 'OrchardCMS:main' into graph-dynamic-fields-filter
mdameer May 25, 2024
692b40a
Merge branch 'OrchardCMS:main' into graph-dynamic-fields-filter
mdameer May 27, 2024
73135d4
Merge branch 'main' into graph-dynamic-fields-filter
hishamco May 27, 2024
6d88f14
Differentiate query and input builders for the dynamic content item
May 27, 2024
3250a53
Merge branch 'OrchardCMS:main' into graph-dynamic-fields-filter
mdameer May 28, 2024
06680e5
Return "FieldTypeIndexDescriptor" instead of tuple from GetFieldIndex
May 28, 2024
2f13f45
Check if field index is null
mdameer May 28, 2024
1cd80e0
DynamicContentFieldsIndexAliasProvider.cs formatting
May 28, 2024
b250836
Merge branch 'OrchardCMS:main' into graph-dynamic-fields-filter
mdameer May 28, 2024
341f404
Merge branch 'OrchardCMS:main' into graph-dynamic-fields-filter
mdameer May 30, 2024
884e646
Merge remote-tracking branch 'OrchardCore/main' into graph-dynamic-fi…
May 31, 2024
cff031d
Fix merge conflicts
May 31, 2024
926b12a
Differentiate query and input dynamic builders, dynamic content type …
Jun 4, 2024
b6b7f06
Merge branch 'OrchardCMS:main' into graph-dynamic-fields-filter
mdameer Jun 4, 2024
95fb3bf
Remove multi value filters for boolean graph type
Jun 4, 2024
381e6b6
Fix dynamic content type test recipe name
Jun 4, 2024
68093fb
Add query multiple content fields test
Jun 4, 2024
73f4f9d
Merge branch 'OrchardCMS:main' into graph-dynamic-fields-filter
mdameer Jun 4, 2024
6f12374
Merge branch 'main' into graph-dynamic-fields-filter
hishamco Jun 5, 2024
23c164f
Merge branch 'main' into graph-dynamic-fields-filter
mdameer Jun 5, 2024
473666a
Add documentation for dynamic content fields usage in GraphQL
Jun 5, 2024
b18b5d0
Merge branch 'main' into graph-dynamic-fields-filter
mdameer Jun 5, 2024
b54b0e1
Merge branch 'main' into graph-dynamic-fields-filter
hishamco Jun 6, 2024
a4fe18c
Merge branch 'main' into graph-dynamic-fields-filter
mdameer Jun 6, 2024
85a3dea
Add a blank line at the end of the file
mdameer Jun 6, 2024
7cd0804
Add a blank line at the end of the file
mdameer Jun 6, 2024
1f07cb3
Use var instead of string
mdameer Jun 6, 2024
a46bb75
Update src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/…
mdameer Jun 6, 2024
a90e4e5
Remove unused content query parameter in "DynamicContentTypeBuilder",…
Jun 6, 2024
73eb17b
Merge branch 'main' into graph-dynamic-fields-filter
mdameer Jun 6, 2024
3c53c26
Add dynamic content field query release note for 2.0.0
Jun 6, 2024
dd7106f
Merge branch 'main' into graph-dynamic-fields-filter
mdameer Jun 6, 2024
837eb66
Make clearing and populating the tenantAliases thread-safe
Jun 6, 2024
60e1f83
Fix clearing and populating the tenantAliases to be thread-safe
Jun 7, 2024
7945f06
Implement IFilterInputObjectGraphType to unify filter object graph types
Jun 8, 2024
a3bb0b2
Merge 'main' into graph-dynamic-fields-filter
Jun 8, 2024
9ccfd9f
Update comment for clarity
Jun 8, 2024
bcbf918
Merge branch 'OrchardCMS:main' into graph-dynamic-fields-filter
mdameer Jun 9, 2024
306d85a
Merge branch 'OrchardCMS:main' into graph-dynamic-fields-filter
mdameer Jun 10, 2024
2781ca6
DynamicContentTypeBuilder variables name refactoring
mdameer Jun 10, 2024
da07f14
Mahe DynamicContentTypeBuilder ctor protected
Jun 10, 2024
491a97f
Merge branch 'main' into graph-dynamic-fields-filter
mdameer Jun 10, 2024
8676203
Merge branch 'main' into graph-dynamic-fields-filter
mdameer Jun 11, 2024
60168e4
Make IIndexAliasProvider.GetAliases async and rename it to GetAliases…
Jun 11, 2024
b2ae3ec
Fix tests to renamed GetAliasesAsync
Jun 11, 2024
a58e57b
Merge branch 'main' into graph-dynamic-fields-filter
mdameer Jun 12, 2024
9981450
Merge branch 'main' into graph-dynamic-fields-filter
mdameer Jun 13, 2024
9a88c3d
Merge branch 'main' into graph-dynamic-fields-filter
mdameer Jun 13, 2024
46d0eb0
Merge branch 'main' into graph-dynamic-fields-filter
mdameer Jun 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
using System.Collections.Generic;
using GraphQL.Resolvers;
using GraphQL.Types;
using OrchardCore.Apis.GraphQL.Queries.Types;
using OrchardCore.ContentFields.Fields;
using OrchardCore.ContentFields.GraphQL.Types;
using OrchardCore.ContentFields.Indexing.SQL;
using OrchardCore.ContentManagement;
using OrchardCore.ContentManagement.GraphQL.Queries.Types;
using OrchardCore.ContentManagement.Metadata.Models;
Expand All @@ -23,6 +24,8 @@ public class ContentFieldsProvider : IContentFieldProvider
FieldType = typeof(BooleanGraphType),
UnderlyingType = typeof(BooleanField),
FieldAccessor = field => ((BooleanField)field).Value,
IndexType = typeof(BooleanFieldIndex),
Index = nameof(BooleanFieldIndex.Boolean)
}
},
{
Expand All @@ -33,6 +36,8 @@ public class ContentFieldsProvider : IContentFieldProvider
FieldType = typeof(DateGraphType),
UnderlyingType = typeof(DateField),
FieldAccessor = field => ((DateField)field).Value,
IndexType = typeof(DateFieldIndex),
Index = nameof(DateFieldIndex.Date)
}
},
{
Expand All @@ -43,6 +48,8 @@ public class ContentFieldsProvider : IContentFieldProvider
FieldType = typeof(DateTimeGraphType),
UnderlyingType = typeof(DateTimeField),
FieldAccessor = field => ((DateTimeField)field).Value,
IndexType = typeof(DateTimeFieldIndex),
Index = nameof(DateTimeFieldIndex.DateTime)
}
},
{
Expand All @@ -53,6 +60,8 @@ public class ContentFieldsProvider : IContentFieldProvider
FieldType = typeof(DecimalGraphType),
UnderlyingType = typeof(NumericField),
FieldAccessor = field => ((NumericField)field).Value,
IndexType = typeof(NumericFieldIndex),
Index = nameof(NumericFieldIndex.Numeric)
}
},
{
Expand All @@ -63,6 +72,8 @@ public class ContentFieldsProvider : IContentFieldProvider
FieldType = typeof(StringGraphType),
UnderlyingType = typeof(TextField),
FieldAccessor = field => ((TextField)field).Text,
IndexType = typeof(TextFieldIndex),
Index = nameof(TextFieldIndex.Text)
}
},
{
Expand All @@ -73,6 +84,8 @@ public class ContentFieldsProvider : IContentFieldProvider
FieldType = typeof(TimeSpanGraphType),
UnderlyingType = typeof(TimeField),
FieldAccessor = field => ((TimeField)field).Value,
IndexType = typeof(TimeFieldIndex),
Index = nameof(TimeFieldIndex.Time)
}
},
{
Expand Down Expand Up @@ -118,12 +131,35 @@ public FieldType GetField(ISchema schema, ContentPartFieldDefinition field, stri

public bool HasField(ISchema schema, ContentPartFieldDefinition field) => _contentFieldTypeMappings.ContainsKey(field.FieldDefinition.Name);

public FieldTypeIndexDescriptor GetFieldIndex(ContentPartFieldDefinition field)
{
if (!HasFieldIndex(field))
mdameer marked this conversation as resolved.
Show resolved Hide resolved
{
return null;
}

var fieldDescriptor = _contentFieldTypeMappings[field.FieldDefinition.Name];

return new FieldTypeIndexDescriptor
{
Index = fieldDescriptor.Index,
IndexType = fieldDescriptor.IndexType
};
}

public bool HasFieldIndex(ContentPartFieldDefinition field) =>
_contentFieldTypeMappings.TryGetValue(field.FieldDefinition.Name, out var fieldTypeDescriptor) &&
fieldTypeDescriptor.IndexType != null &&
!string.IsNullOrWhiteSpace(fieldTypeDescriptor.Index);

private sealed class FieldTypeDescriptor
{
public string Description { get; set; }
public Type FieldType { get; set; }
public Type UnderlyingType { get; set; }
public Func<ContentElement, object> FieldAccessor { get; set; }
public string Index { get; set; }
public Type IndexType { get; set; }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ public FieldType GetField(ISchema schema, ContentPartFieldDefinition field, stri
ResolvedType = queryGraphType,
Resolver = new FuncFieldResolver<ContentElement, ContentElement>(context =>
{
var typeToResolve = context.FieldDefinition.ResolvedType.GetType().BaseType.GetGenericArguments().First();
var typeToResolve = context.FieldDefinition.ResolvedType.GetType().BaseType
.GetGenericArguments().First();
mdameer marked this conversation as resolved.
Show resolved Hide resolved

// Check if part has been collapsed by trying to get the parent part.
ContentElement contentPart = context.Source.Get<ContentPart>(field.PartDefinition.Name);
Expand All @@ -44,8 +45,15 @@ public FieldType GetField(ISchema schema, ContentPartFieldDefinition field, stri
return null;
}

public FieldTypeIndexDescriptor GetFieldIndex(ContentPartFieldDefinition field)
{
return null;
}

public bool HasField(ISchema schema, ContentPartFieldDefinition field) => GetObjectGraphType(schema, field) != null;

public bool HasFieldIndex(ContentPartFieldDefinition field) => false;

private static IObjectGraphType GetObjectGraphType(ISchema schema, ContentPartFieldDefinition field) =>
schema.AdditionalTypeInstances
.FirstOrDefault(x => x is IObjectGraphType && x.GetType().BaseType.GetGenericArguments().First().Name == field.FieldDefinition.Name) as IObjectGraphType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using OrchardCore.ContentFields.Fields;
using OrchardCore.ContentFields.GraphQL.Fields;
using OrchardCore.ContentFields.GraphQL.Types;
using OrchardCore.ContentManagement.GraphQL;
using OrchardCore.ContentManagement.GraphQL.Queries.Types;
using OrchardCore.Modules;

Expand All @@ -22,4 +23,13 @@ public override void ConfigureServices(IServiceCollection services)
services.AddObjectGraphType<UserPickerField, UserPickerFieldQueryObjectType>();
}
}

[RequireFeatures("OrchardCore.Apis.GraphQL", "OrchardCore.ContentFields.Indexing.SQL")]
public class IndexStartup : StartupBase
mdameer marked this conversation as resolved.
Show resolved Hide resolved
{
public override void ConfigureServices(IServiceCollection services)
{
services.AddContentFieldsInputGraphQL();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,12 @@ await _contentDefinitionManager.AlterPartDefinitionAsync(partViewModel.Name, par
fieldBuilder.WithDisplayMode(fieldViewModel.DisplayMode);
});
});

_contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentPartFieldUpdated(context), new ContentPartFieldUpdatedContext
{
ContentPartName = partViewModel.Name,
ContentFieldName = fieldViewModel.Name
}, _logger);
}

public async Task AlterTypePartAsync(EditTypePartViewModel typePartViewModel)
Expand All @@ -385,10 +391,17 @@ await _contentDefinitionManager.AlterTypeDefinitionAsync(typeDefinition.Name, ty
part.WithDisplayMode(typePartViewModel.DisplayMode);
});
});

_contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentTypePartUpdated(context), new ContentTypePartUpdatedContext
{
ContentTypeName = typeDefinition.Name,
ContentPartName = typePartViewModel.Name
}, _logger);
}

public Task AlterTypePartsOrderAsync(ContentTypeDefinition typeDefinition, string[] partNames)
=> _contentDefinitionManager.AlterTypeDefinitionAsync(typeDefinition.Name, type =>
public async Task AlterTypePartsOrderAsync(ContentTypeDefinition typeDefinition, string[] partNames)
{
await _contentDefinitionManager.AlterTypeDefinitionAsync(typeDefinition.Name, type =>
{
if (partNames is null)
{
Expand All @@ -411,8 +424,15 @@ public Task AlterTypePartsOrderAsync(ContentTypeDefinition typeDefinition, strin
}
});

public Task AlterPartFieldsOrderAsync(ContentPartDefinition partDefinition, string[] fieldNames)
=> _contentDefinitionManager.AlterPartDefinitionAsync(partDefinition.Name, type =>
_contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentTypeUpdated(context), new ContentTypeUpdatedContext
{
ContentTypeDefinition = typeDefinition
}, _logger);
}

public async Task AlterPartFieldsOrderAsync(ContentPartDefinition partDefinition, string[] fieldNames)
{
await _contentDefinitionManager.AlterPartDefinitionAsync(partDefinition.Name, type =>
{
if (fieldNames is null)
{
Expand All @@ -429,6 +449,12 @@ public Task AlterPartFieldsOrderAsync(ContentPartDefinition partDefinition, stri
}
});

_contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentPartUpdated(context), new ContentPartUpdatedContext
{
ContentPartDefinition = partDefinition
}, _logger);
}

public async Task<string> GenerateContentTypeNameFromDisplayNameAsync(string displayName)
{
displayName = displayName.ToSafeName();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using GraphQL.Types;
using GraphQLParser.AST;

namespace OrchardCore.ContentFields.GraphQL.Types
namespace OrchardCore.Apis.GraphQL.Queries.Types
{
public class TimeSpanGraphType : ScalarGraphType
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
using System;
using System.Collections.Generic;
using GraphQL.Types;
using OrchardCore.Apis.GraphQL.Queries.Types;

namespace OrchardCore.Apis.GraphQL.Queries
{
public class WhereInputObjectGraphType : WhereInputObjectGraphType<object>
public interface IFilterInputObjectGraphType : IInputObjectGraphType
{
void AddScalarFilterFields<TGraphType>(string fieldName, string description);

void AddScalarFilterFields(Type graphType, string fieldName, string description);
}

public class WhereInputObjectGraphType : WhereInputObjectGraphType<object>, IFilterInputObjectGraphType
{
}

public class WhereInputObjectGraphType<TSourceType> : InputObjectGraphType<TSourceType>
public class WhereInputObjectGraphType<TSourceType> : InputObjectGraphType<TSourceType>, IFilterInputObjectGraphType
{
// arguments of typed input graph types return typed object, without additional input fields (_in, _contains,..)
// so we return dictionary as it was before.
Expand Down Expand Up @@ -51,22 +59,37 @@ public override object ParseDictionary(IDictionary<string, object> value)
{"_not_ends_with", "does not end with the string"},
};

public void AddScalarFilterFields<TGraphType>(string fieldName, string description)
public virtual void AddScalarFilterFields<TGraphType>(string fieldName, string description)
{
AddScalarFilterFields(typeof(TGraphType), fieldName, description);
}

public void AddScalarFilterFields(Type graphType, string fieldName, string description)
public virtual void AddScalarFilterFields(Type graphType, string fieldName, string description)
{
if (!typeof(ScalarGraphType).IsAssignableFrom(graphType) &&
!typeof(IInputObjectGraphType).IsAssignableFrom(graphType))
{
return;
}

AddEqualityFilters(graphType, fieldName, description);
AddMultiValueFilters(graphType, fieldName, description);

if (graphType == typeof(StringGraphType))
{
AddMultiValueFilters(graphType, fieldName, description);
AddStringFilters(graphType, fieldName, description);
}
else if (graphType == typeof(DateTimeGraphType))
else if (graphType == typeof(DateTimeGraphType) ||
graphType == typeof(DateGraphType) ||
graphType == typeof(DateOnlyGraphType) ||
graphType == typeof(TimeSpanGraphType) ||
graphType == typeof(DecimalGraphType) ||
graphType == typeof(IntGraphType) ||
graphType == typeof(LongGraphType) ||
graphType == typeof(FloatGraphType) ||
graphType == typeof(BigIntGraphType))
{
AddMultiValueFilters(graphType, fieldName, description);
AddNonStringFilters(graphType, fieldName, description);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ internal static string GetFieldName(ContentTypePartDefinition definition, string
return partName.ToFieldName() + fieldName.ToPascalCase();
}

return fieldName;
return fieldName.ToCamelCase();
mdameer marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<ItemGroup>
<ProjectReference Include="..\..\OrchardCore\OrchardCore.Apis.GraphQL.Abstractions\OrchardCore.Apis.GraphQL.Abstractions.csproj" />
<ProjectReference Include="..\OrchardCore.ContentManagement.Abstractions\OrchardCore.ContentManagement.Abstractions.csproj" />
<ProjectReference Include="..\OrchardCore.ContentTypes.Abstractions\OrchardCore.ContentTypes.Abstractions.csproj" />
<ProjectReference Include="..\OrchardCore.ContentManagement.Display\OrchardCore.ContentManagement.Display.csproj" />
<ProjectReference Include="..\OrchardCore.ContentManagement\OrchardCore.ContentManagement.csproj" />
<ProjectReference Include="..\OrchardCore.Contents.Core\OrchardCore.Contents.Core.csproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,10 @@ private IQuery<ContentItem> FilterWhereArguments(
foreach (var alias in aliasProvider.GetAliases())
{
predicateQuery.CreateAlias(alias.Alias, alias.Index);
indexAliases.Add(alias.Alias, alias.Alias);
indexes.TryAdd(alias.Index, alias);
if (indexAliases.TryAdd(alias.Alias, alias.Alias))
{
indexes.TryAdd(alias.Index, alias);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using GraphQL.Types;
using Microsoft.Extensions.Options;
using OrchardCore.Apis.GraphQL;
Expand All @@ -21,26 +22,26 @@ public ContentItemWhereInput(string contentItemName, IOptions<GraphQLContentOpti

Description = $"the {contentItemName} content item filters";

AddFilterField<IdGraphType>("contentItemId", "content item id");
AddFilterField<IdGraphType>("contentItemVersionId", "the content item version id");
AddFilterField<StringGraphType>("displayText", "the display text of the content item");
AddFilterField<DateTimeGraphType>("createdUtc", "the date and time of creation");
AddFilterField<DateTimeGraphType>("modifiedUtc", "the date and time of modification");
AddFilterField<DateTimeGraphType>("publishedUtc", "the date and time of publication");
AddFilterField<StringGraphType>("owner", "the owner of the content item");
AddFilterField<StringGraphType>("author", "the author of the content item");
AddScalarFilterFields<IdGraphType>("contentItemId", "content item id");
AddScalarFilterFields<IdGraphType>("contentItemVersionId", "the content item version id");
AddScalarFilterFields<StringGraphType>("displayText", "the display text of the content item");
AddScalarFilterFields<DateTimeGraphType>("createdUtc", "the date and time of creation");
AddScalarFilterFields<DateTimeGraphType>("modifiedUtc", "the date and time of modification");
AddScalarFilterFields<DateTimeGraphType>("publishedUtc", "the date and time of publication");
AddScalarFilterFields<StringGraphType>("owner", "the owner of the content item");
AddScalarFilterFields<StringGraphType>("author", "the author of the content item");

var whereInputType = new ListGraphType(this);
Field<ListGraphType<ContentItemWhereInput>>("Or").Description("OR logical operation").Type(whereInputType);
Field<ListGraphType<ContentItemWhereInput>>("And").Description("AND logical operation").Type(whereInputType);
Field<ListGraphType<ContentItemWhereInput>>("Not").Description("NOT logical operation").Type(whereInputType);
}

private void AddFilterField<T>(string name, string description)
public override void AddScalarFilterFields(Type graphType, string fieldName, string description)
{
if (!_optionsAccessor.Value.ShouldSkip(typeof(ContentItemType), name))
if (!_optionsAccessor.Value.ShouldSkip(typeof(ContentItemType), fieldName))
{
AddScalarFilterFields<T>(name, description);
base.AddScalarFilterFields(graphType, fieldName, description);
}
}
}
Expand Down
Loading