Skip to content

Commit a9562da

Browse files
committed
Improve selection lookup on value completion
1 parent e693a7f commit a9562da

4 files changed

Lines changed: 139 additions & 2 deletions

File tree

src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Results/ValueCompletion.cs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,19 @@ public bool BuildResult(
7878
InitializeTargetObject(source, target);
7979
}
8080

81+
var hasObjectScope = target.TryGetObjectScope(out var objectScope);
82+
8183
foreach (var property in source.EnumerateObject())
8284
{
83-
if (!target.TryGetProperty(property.NameSpan, out var resultField))
85+
CompositeResultElement resultField;
86+
if (hasObjectScope)
87+
{
88+
if (!objectScope.TryGetProperty(property.NameSpan, out resultField))
89+
{
90+
continue;
91+
}
92+
}
93+
else if (!target.TryGetProperty(property.NameSpan, out resultField))
8494
{
8595
continue;
8696
}
@@ -812,9 +822,19 @@ private bool TryCompleteObjectValue(
812822
target.SetObjectValue(objectSelectionSet);
813823
}
814824

825+
var hasObjectScope = target.TryGetObjectScope(out var objectScope);
826+
815827
foreach (var property in source.EnumerateObject())
816828
{
817-
if (!target.TryGetProperty(property.NameSpan, out var targetProperty))
829+
CompositeResultElement targetProperty;
830+
if (hasObjectScope)
831+
{
832+
if (!objectScope.TryGetProperty(property.NameSpan, out targetProperty))
833+
{
834+
continue;
835+
}
836+
}
837+
else if (!target.TryGetProperty(property.NameSpan, out targetProperty))
818838
{
819839
continue;
820840
}

src/HotChocolate/Fusion/src/Fusion.Execution/Text/Json/CompositeResultDocument.TryGetProperty.cs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System.Buffers;
22
using System.Diagnostics;
3+
using System.Runtime.CompilerServices;
4+
using HotChocolate.Fusion.Execution.Nodes;
35
using HotChocolate.Text.Json;
46

57
namespace HotChocolate.Fusion.Text.Json;
@@ -162,6 +164,28 @@ internal bool TryGetNamedPropertyValue(
162164
out value);
163165
}
164166

167+
internal bool TryGetObjectScope(
168+
Cursor startCursor,
169+
out CompositeResultObjectScope scope)
170+
{
171+
ObjectDisposedException.ThrowIf(_disposed != 0, this);
172+
173+
var row = _metaDb.GetValue(ref startCursor);
174+
CheckExpectedType(ElementTokenType.StartObject, row.TokenType);
175+
176+
if (row.OperationReferenceType is OperationReferenceType.SelectionSet)
177+
{
178+
var selectionSetId = row.OperationReferenceId;
179+
var selectionSet = _operation.GetSelectionSetById(selectionSetId);
180+
181+
scope = new CompositeResultObjectScope(this, startCursor, selectionSet, selectionSetId);
182+
return true;
183+
}
184+
185+
scope = default;
186+
return false;
187+
}
188+
165189
private bool TryGetNamedPropertyValue(
166190
Cursor startCursor,
167191
Cursor endCursor,
@@ -274,3 +298,40 @@ internal Cursor GetEndCursor(Cursor cursor)
274298
return cursor + _metaDb.GetNumberOfRows(cursor);
275299
}
276300
}
301+
302+
internal readonly ref struct CompositeResultObjectScope
303+
{
304+
private readonly CompositeResultDocument _document;
305+
private readonly CompositeResultDocument.Cursor _startObject;
306+
private readonly SelectionSet _selectionSet;
307+
private readonly int _selectionSetId;
308+
309+
internal CompositeResultObjectScope(
310+
CompositeResultDocument document,
311+
CompositeResultDocument.Cursor startObject,
312+
SelectionSet selectionSet,
313+
int selectionSetId)
314+
{
315+
_document = document;
316+
_startObject = startObject;
317+
_selectionSet = selectionSet;
318+
_selectionSetId = selectionSetId;
319+
}
320+
321+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
322+
public bool TryGetProperty(
323+
ReadOnlySpan<byte> name,
324+
out CompositeResultElement value)
325+
{
326+
if (_selectionSet.TryGetSelection(name, out var selection))
327+
{
328+
var propertyIndex = selection.Id - _selectionSetId - 1;
329+
var cursor = _startObject + (propertyIndex * 2) + 2;
330+
value = new CompositeResultElement(_document, cursor);
331+
return true;
332+
}
333+
334+
value = default;
335+
return false;
336+
}
337+
}

src/HotChocolate/Fusion/src/Fusion.Execution/Text/Json/CompositeResultElement.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,13 @@ public bool TryGetProperty(ReadOnlySpan<byte> utf8PropertyName, out CompositeRes
432432
return _parent.TryGetNamedPropertyValue(_cursor, utf8PropertyName, out value);
433433
}
434434

435+
internal bool TryGetObjectScope(out CompositeResultObjectScope scope)
436+
{
437+
CheckValidInstance();
438+
439+
return _parent.TryGetObjectScope(_cursor, out scope);
440+
}
441+
435442
internal CompositeResultElement GetPropertyBySelectionId(int selectionId)
436443
{
437444
CheckValidInstance();

src/HotChocolate/Fusion/test/Fusion.Execution.Tests/Text/Json/CompositeResultDocumentTests.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,55 @@ fragment Product on Product {
8080
Assert.Equal(JsonValueKind.Object, productBySlug.ValueKind);
8181
}
8282

83+
[Fact]
84+
public void TryGetObjectScope_Should_ReturnFalseAndFallback_When_ObjectHasNoSelectionSet()
85+
{
86+
// arrange
87+
var schema = CreateCompositeSchema();
88+
89+
var plan = PlanOperation(
90+
schema,
91+
"""
92+
{
93+
productBySlug(slug: "1") {
94+
... Product
95+
}
96+
}
97+
98+
fragment Product on Product {
99+
id
100+
name
101+
}
102+
""");
103+
104+
var compositeResult = new CompositeResultDocument(CommonTestExtensions.CreateArena(), plan.Operation, 0);
105+
var operation = compositeResult.Data.Operation;
106+
107+
var productBySlug = compositeResult.Data.GetProperty("productBySlug");
108+
var productBySlugSelection = productBySlug.AssertSelection();
109+
var selectionSet = operation.GetSelectionSet(productBySlugSelection);
110+
productBySlug.SetObjectValue(selectionSet);
111+
112+
var objectStart = compositeResult.GetStartCursor(productBySlug.Cursor);
113+
var row = compositeResult._metaDb.Get(objectStart);
114+
compositeResult._metaDb.Replace(
115+
objectStart,
116+
ElementTokenType.StartObject,
117+
sizeOrLength: row.SizeOrLength,
118+
parentRow: row.Parent,
119+
numberOfRows: row.NumberOfRows,
120+
flags: row.Flags);
121+
122+
// act
123+
var hasScope = productBySlug.TryGetObjectScope(out _);
124+
var hasProperty = productBySlug.TryGetProperty("id"u8, out var id);
125+
126+
// assert
127+
Assert.False(hasScope);
128+
Assert.True(hasProperty);
129+
Assert.Equal("id", id.GetPropertyName());
130+
}
131+
83132
[Fact]
84133
public void Add_SourceResult_Leaf_Value()
85134
{

0 commit comments

Comments
 (0)