Skip to content

Commit ff687a5

Browse files
authoredJul 16, 2021
IsProjected on multiple fields (#3916)
1 parent 94c331e commit ff687a5

5 files changed

+100
-32
lines changed
 

‎src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionConvention.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ private static IReadOnlyList<IProjectionProviderExtension> CollectExtensions(
9292
IServiceProvider serviceProvider,
9393
ProjectionConventionDefinition definition)
9494
{
95-
List<IProjectionProviderExtension> extensions = new List<IProjectionProviderExtension>();
95+
List<IProjectionProviderExtension> extensions = new();
9696
extensions.AddRange(definition.ProviderExtensions);
9797
foreach (var extensionType in definition.ProviderExtensionsTypes)
9898
{

‎src/HotChocolate/Data/src/Data/Projections/Optimizers/IsProjectedProjectionOptimizer.cs

+42-30
Original file line numberDiff line numberDiff line change
@@ -17,40 +17,52 @@ public Selection RewriteSelection(
1717
SelectionOptimizerContext context,
1818
Selection selection)
1919
{
20-
if (context.Type is ObjectType type &&
20+
if (!(context.Type is ObjectType type &&
2121
type.ContextData.TryGetValue(AlwaysProjectedFieldsKey, out object? fieldsObj) &&
22-
fieldsObj is string[] fields)
22+
fieldsObj is string[] fields))
2323
{
24-
int aliasCount = 0;
25-
for (var i = 0; i < fields.Length; i++)
24+
return selection;
25+
}
26+
27+
for (var i = 0; i < fields.Length; i++)
28+
{
29+
var alias = "__projection_alias_" + i;
30+
31+
// if the field is already in the selection set we do not need to project it
32+
if (context.Fields.TryGetValue(fields[i], out var field) &&
33+
field.Field.Name == fields[i])
2634
{
27-
if (!context.Fields.TryGetValue(fields[i], out var field) ||
28-
field.Field.Name != fields[i])
29-
{
30-
IObjectField nodesField = type.Fields[fields[i]];
31-
var alias = "__projection_alias_" + aliasCount++;
32-
var nodesFieldNode = new FieldNode(
33-
null,
34-
new NameNode(fields[i]),
35-
new NameNode(alias),
36-
Array.Empty<DirectiveNode>(),
37-
Array.Empty<ArgumentNode>(),
38-
null);
39-
40-
FieldDelegate nodesPipeline =
41-
context.CompileResolverPipeline(nodesField, nodesFieldNode);
42-
43-
var compiledSelection = new Selection(
44-
context.Type,
45-
nodesField,
46-
nodesFieldNode,
47-
nodesPipeline,
48-
arguments: selection.Arguments,
49-
internalSelection: true);
50-
51-
context.Fields[alias] = compiledSelection;
52-
}
35+
continue;
5336
}
37+
38+
// if the field is already added as an alias we do not need to add it
39+
if (context.Fields.TryGetValue(alias, out field) &&
40+
field.Field.Name == fields[i])
41+
{
42+
continue;
43+
}
44+
45+
IObjectField nodesField = type.Fields[fields[i]];
46+
var nodesFieldNode = new FieldNode(
47+
null,
48+
new NameNode(fields[i]),
49+
new NameNode(alias),
50+
Array.Empty<DirectiveNode>(),
51+
Array.Empty<ArgumentNode>(),
52+
null);
53+
54+
FieldDelegate nodesPipeline =
55+
context.CompileResolverPipeline(nodesField, nodesFieldNode);
56+
57+
var compiledSelection = new Selection(
58+
context.Type,
59+
nodesField,
60+
nodesFieldNode,
61+
nodesPipeline,
62+
arguments: selection.Arguments,
63+
internalSelection: true);
64+
65+
context.Fields[alias] = compiledSelection;
5466
}
5567

5668
return selection;

‎src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/QueryableProjectionVisitorIsProjectedTests.cs

+43-1
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,16 @@ public class QueryableProjectionVisitorIsProjectedTests
1212
new Foo { IsProjectedTrue = true, IsProjectedFalse = false }
1313
};
1414

15+
private static readonly MultipleFoo[] _fooMultipleEntities =
16+
{
17+
new MultipleFoo{ IsProjectedTrue1 = true, IsProjectedFalse = false },
18+
new MultipleFoo{ IsProjectedTrue1 = true, IsProjectedFalse = false }
19+
};
20+
1521
private static readonly Bar[] _barEntities =
1622
{
17-
new Bar { IsProjectedFalse = false }, new Bar { IsProjectedFalse = false }
23+
new Bar { IsProjectedFalse = false },
24+
new Bar { IsProjectedFalse = false }
1825
};
1926

2027
private readonly SchemaCache _cache = new SchemaCache();
@@ -67,6 +74,22 @@ public async Task IsProjected_Should_AlwaysBeProjectedWhenSelected_When_True()
6774
res1.MatchSqlSnapshot();
6875
}
6976

77+
[Fact]
78+
public async Task IsProjected_Should_AlwaysBeProjectedWhenSelected_When_TrueAndMultiple()
79+
{
80+
// arrange
81+
IRequestExecutor tester = _cache.CreateSchema(_fooMultipleEntities);
82+
83+
// act
84+
// assert
85+
IExecutionResult res1 = await tester.ExecuteAsync(
86+
QueryRequestBuilder.New()
87+
.SetQuery("{ root { isProjectedFalse }}")
88+
.Create());
89+
90+
res1.MatchSqlSnapshot();
91+
}
92+
7093
[Fact]
7194
public async Task IsProjected_Should_NotFailWhenSelectionSetSkippedCompletely()
7295
{
@@ -105,5 +128,24 @@ public class Bar
105128

106129
public bool? ShouldNeverBeProjected { get; set; }
107130
}
131+
132+
public class MultipleFoo
133+
{
134+
public int Id { get; set; }
135+
136+
[IsProjected(true)]
137+
public bool? IsProjectedTrue1 { get; set; }
138+
139+
[IsProjected(true)]
140+
public bool? IsProjectedTrue2 { get; set; }
141+
142+
[IsProjected(true)]
143+
public bool? IsProjectedTrue3 { get; set; }
144+
145+
[IsProjected(false)]
146+
public bool? IsProjectedFalse { get; set; }
147+
148+
public bool? ShouldNeverBeProjected { get; set; }
149+
}
108150
}
109151
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"data": {
3+
"root": [
4+
{
5+
"isProjectedFalse": null
6+
},
7+
{
8+
"isProjectedFalse": null
9+
}
10+
]
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
SELECT "d"."IsProjectedTrue1", "d"."IsProjectedTrue2", "d"."IsProjectedTrue3"
2+
FROM "Data" AS "d"

0 commit comments

Comments
 (0)
Please sign in to comment.