Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -74,6 +74,11 @@ public static Expression<Func<TValue, TValue>> AsSelector<TValue>(
return GetOrCreateExpression<TValue>(selection, builder);
}

if ((flags & FieldFlags.NodesField) == FieldFlags.NodesField)
{
return GetOrCreateNodeExpression<TValue>(selection);
}

return GetOrCreateExpression<TValue>(selection);
}

Expand All @@ -93,6 +98,13 @@ private static Expression<Func<TValue, TValue>> GetOrCreateExpression<TValue>(
static (_, ctx) => ctx.builder.TryCompile<TValue>()!,
(builder, selection));

private static Expression<Func<TValue, TValue>> GetOrCreateNodeExpression<TValue>(
ISelection selection)
=> selection.DeclaringOperation.GetOrAddState(
CreateNodeExpressionKey<TValue>(selection.Id),
static (_, ctx) => ctx._builder.BuildNodeExpression<TValue>(ctx.selection),
(_builder, selection));

private static bool TryGetExpression<TValue>(
ISelection selection,
[NotNullWhen(true)] out Expression<Func<TValue, TValue>>? expression)
Expand Down Expand Up @@ -170,6 +182,30 @@ private static string CreateExpressionKey(int key)
return Encoding.UTF8.GetString(span.Slice(0, written + keyPrefix.Length));
}

private static string CreateNodeExpressionKey<TValue>(int key)
{
var typeName = typeof(TValue).FullName!;
var typeNameLength = Encoding.UTF8.GetMaxByteCount(typeName.Length);
var keyPrefix = GetKeyPrefix();
var requiredBufferSize = EstimateIntLength(key) + keyPrefix.Length + typeNameLength;
byte[]? rented = null;
var span = requiredBufferSize <= 256
? stackalloc byte[requiredBufferSize]
: (rented = ArrayPool<byte>.Shared.Rent(requiredBufferSize));

keyPrefix.CopyTo(span);
Utf8Formatter.TryFormat(key, span.Slice(keyPrefix.Length), out var written, 'D');
var typeNameWritten = Encoding.UTF8.GetBytes(typeName, span.Slice(written + keyPrefix.Length));
var keyString = Encoding.UTF8.GetString(span.Slice(0, written + keyPrefix.Length + typeNameWritten));

if (rented is not null)
{
ArrayPool<byte>.Shared.Return(rented);
}

return keyString;
}

private static ReadOnlySpan<byte> GetKeyPrefix()
=> "hc-dataloader-expr-"u8;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,44 @@ public Expression<Func<TRoot, TRoot>> BuildExpression<TRoot>(ISelection selectio
return Expression.Lambda<Func<TRoot, TRoot>>(selectionSetExpression, parameter);
}

public Expression<Func<TRoot, TRoot>> BuildNodeExpression<TRoot>(ISelection selection)
{
var rootType = typeof(TRoot);
var parameter = Expression.Parameter(rootType, "root");
var requirements = selection.DeclaringOperation.Schema.Features.GetRequired<FieldRequirementsMetadata>();
var context = new Context(parameter, rootType, requirements);
var root = new TypeContainer();

var entityType = selection.DeclaringOperation.GetPossibleTypes(selection).FirstOrDefault(t => t.RuntimeType == typeof(TRoot));

if (entityType is null)
{
throw new InvalidOperationException(
$"Unable to resolve the entity type from `{typeof(TRoot).FullName}`.");
}

var typeNode = new TypeNode(entityType.RuntimeType);
var selectionSet = selection.DeclaringOperation.GetSelectionSet(selection, entityType);
CollectSelections(context, selectionSet, typeNode);
root.TryAddNode(typeNode);

if (typeNode.Nodes.Count == 0)
{
TryAddAnyLeafField(selection, typeNode);
}

CollectTypes(context, selection, root);

var selectionSetExpression = BuildTypeSwitchExpression(context, root);

if (selectionSetExpression is null)
{
throw new InvalidOperationException("The selection set is empty.");
}

return Expression.Lambda<Func<TRoot, TRoot>>(selectionSetExpression, parameter);
}

private void CollectTypes(Context context, ISelection selection, TypeContainer parent)
{
var namedType = selection.Type.NamedType();
Expand All @@ -49,11 +87,12 @@ private void CollectTypes(Context context, ISelection selection, TypeContainer p
CollectSelections(context, possibleSelectionSet, possibleTypeNode);
parent.TryAddNode(possibleTypeNode);

if(possibleTypeNode.Nodes.Count == 0)
if (possibleTypeNode.Nodes.Count == 0)
{
TryAddAnyLeafField(selection, possibleTypeNode);
}
}

return;
}

Expand All @@ -63,7 +102,7 @@ private void CollectTypes(Context context, ISelection selection, TypeContainer p
CollectSelections(context, selectionSet, typeNode);
parent.TryAddNode(typeNode);

if(typeNode.Nodes.Count == 0)
if (typeNode.Nodes.Count == 0)
{
TryAddAnyLeafField(selection, typeNode);
}
Expand All @@ -80,7 +119,7 @@ private void CollectTypes(Context context, ISelection selection, TypeContainer p
foreach (var typeNode in parent.Nodes)
{
var newParent = Expression.Convert(context.Parent, typeNode.Type);
var newContext = context with { Parent = newParent, ParentType = typeNode.Type };
var newContext = context with { Parent = newParent, ParentType = typeNode.Type };
var typeCondition = Expression.TypeIs(context.Parent, typeNode.Type);
var selectionSet = BuildSelectionSetExpression(newContext, typeNode);

Expand Down Expand Up @@ -225,7 +264,7 @@ private void CollectSelections(
return Expression.Bind(node.Property, propertyAccessor);
}

if(node.IsArrayOrCollection)
if (node.IsArrayOrCollection)
{
throw new NotSupportedException("List projections are not supported.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@
using HotChocolate.Execution.Processing;
using HotChocolate.Execution.TestContext;
using HotChocolate.Types;
<<<<<<< Updated upstream
=======
using HotChocolate.Types.Relay;
>>>>>>> Stashed changes
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Squadron;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Brand_With_Name_Over_Node

## SQL

```text
-- @__keys_0={ '1' } (DbType = Object)
SELECT b."Id", b."Name"
FROM "Brands" AS b
WHERE b."Id" = ANY (@__keys_0)
```

## Result

```json
{
"data": {
"node": {
"id": "QnJhbmQ6MQ==",
"name": "Brand0"
}
}
}
```