Skip to content

Commit 866c8b1

Browse files
author
Bart Koelman
committed
Changed AnyExpression.Constants type from IReadOnlyCollection to IImmutableSet
1 parent 159b99f commit 866c8b1

File tree

5 files changed

+49
-20
lines changed

5 files changed

+49
-20
lines changed

src/JsonApiDotNetCore/Queries/Expressions/AnyExpression.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
using System;
2-
using System.Collections.Generic;
2+
using System.Collections.Immutable;
33
using System.Linq;
44
using System.Text;
55
using JetBrains.Annotations;
@@ -14,9 +14,9 @@ namespace JsonApiDotNetCore.Queries.Expressions
1414
public class AnyExpression : FilterExpression
1515
{
1616
public ResourceFieldChainExpression TargetAttribute { get; }
17-
public IReadOnlyCollection<LiteralConstantExpression> Constants { get; }
17+
public IImmutableSet<LiteralConstantExpression> Constants { get; }
1818

19-
public AnyExpression(ResourceFieldChainExpression targetAttribute, IReadOnlyCollection<LiteralConstantExpression> constants)
19+
public AnyExpression(ResourceFieldChainExpression targetAttribute, IImmutableSet<LiteralConstantExpression> constants)
2020
{
2121
ArgumentGuard.NotNull(targetAttribute, nameof(targetAttribute));
2222
ArgumentGuard.NotNull(constants, nameof(constants));
@@ -43,7 +43,7 @@ public override string ToString()
4343
builder.Append('(');
4444
builder.Append(TargetAttribute);
4545
builder.Append(',');
46-
builder.Append(string.Join(",", Constants.Select(constant => constant.ToString())));
46+
builder.Append(string.Join(",", Constants.Select(constant => constant.ToString()).OrderBy(value => value)));
4747
builder.Append(')');
4848

4949
return builder.ToString();
@@ -63,7 +63,7 @@ public override bool Equals(object obj)
6363

6464
var other = (AnyExpression)obj;
6565

66-
return TargetAttribute.Equals(other.TargetAttribute) && Constants.SequenceEqual(other.Constants);
66+
return TargetAttribute.Equals(other.TargetAttribute) && Constants.SetEquals(other.Constants);
6767
}
6868

6969
public override int GetHashCode()

src/JsonApiDotNetCore/Queries/Expressions/QueryExpressionRewriter.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ public override QueryExpression VisitAny(AnyExpression expression, TArgument arg
186186
if (expression != null)
187187
{
188188
var newTargetAttribute = Visit(expression.TargetAttribute, argument) as ResourceFieldChainExpression;
189-
IReadOnlyCollection<LiteralConstantExpression> newConstants = VisitSequence(expression.Constants, argument);
189+
IImmutableSet<LiteralConstantExpression> newConstants = VisitSet(expression.Constants, argument);
190190

191191
var newExpression = new AnyExpression(newTargetAttribute, newConstants);
192192
return newExpression.Equals(expression) ? expression : newExpression;
@@ -333,5 +333,21 @@ protected virtual IImmutableList<TExpression> VisitList<TExpression>(IImmutableL
333333

334334
return arrayBuilder.ToImmutable();
335335
}
336+
337+
protected virtual IImmutableSet<TExpression> VisitSet<TExpression>(IImmutableSet<TExpression> elements, TArgument argument)
338+
where TExpression : QueryExpression
339+
{
340+
ImmutableHashSet<TExpression>.Builder setBuilder = ImmutableHashSet.CreateBuilder<TExpression>();
341+
342+
foreach (TExpression element in elements)
343+
{
344+
if (Visit(element, argument) is TExpression newElement)
345+
{
346+
setBuilder.Add(newElement);
347+
}
348+
}
349+
350+
return setBuilder.ToImmutable();
351+
}
336352
}
337353
}

src/JsonApiDotNetCore/Queries/Internal/Parsing/FilterParser.cs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -196,39 +196,52 @@ protected AnyExpression ParseAny()
196196

197197
EatSingleCharacterToken(TokenKind.Comma);
198198

199-
var constants = new List<LiteralConstantExpression>();
199+
ImmutableHashSet<LiteralConstantExpression>.Builder constantsBuilder = ImmutableHashSet.CreateBuilder<LiteralConstantExpression>();
200200

201201
LiteralConstantExpression constant = ParseConstant();
202-
constants.Add(constant);
202+
constantsBuilder.Add(constant);
203203

204204
EatSingleCharacterToken(TokenKind.Comma);
205205

206206
constant = ParseConstant();
207-
constants.Add(constant);
207+
constantsBuilder.Add(constant);
208208

209209
while (TokenStack.TryPeek(out Token nextToken) && nextToken.Kind == TokenKind.Comma)
210210
{
211211
EatSingleCharacterToken(TokenKind.Comma);
212212

213213
constant = ParseConstant();
214-
constants.Add(constant);
214+
constantsBuilder.Add(constant);
215215
}
216216

217217
EatSingleCharacterToken(TokenKind.CloseParen);
218218

219+
IImmutableSet<LiteralConstantExpression> constantSet = constantsBuilder.ToImmutable();
220+
219221
PropertyInfo targetAttributeProperty = targetAttribute.Fields[^1].Property;
220222

221223
if (targetAttributeProperty.Name == nameof(Identifiable.Id))
222224
{
223-
for (int index = 0; index < constants.Count; index++)
224-
{
225-
string stringId = constants[index].Value;
226-
string id = DeObfuscateStringId(targetAttributeProperty.ReflectedType, stringId);
227-
constants[index] = new LiteralConstantExpression(id);
228-
}
225+
constantSet = DeObfuscateIdConstants(constantSet, targetAttributeProperty);
226+
}
227+
228+
return new AnyExpression(targetAttribute, constantSet);
229+
}
230+
231+
private IImmutableSet<LiteralConstantExpression> DeObfuscateIdConstants(IImmutableSet<LiteralConstantExpression> constantSet,
232+
PropertyInfo targetAttributeProperty)
233+
{
234+
ImmutableHashSet<LiteralConstantExpression>.Builder idConstantsBuilder = ImmutableHashSet.CreateBuilder<LiteralConstantExpression>();
235+
236+
foreach (LiteralConstantExpression idConstant in constantSet)
237+
{
238+
string stringId = idConstant.Value;
239+
string id = DeObfuscateStringId(targetAttributeProperty.ReflectedType, stringId);
240+
241+
idConstantsBuilder.Add(new LiteralConstantExpression(id));
229242
}
230243

231-
return new AnyExpression(targetAttribute, constants);
244+
return idConstantsBuilder.ToImmutable();
232245
}
233246

234247
protected HasExpression ParseHas()

src/JsonApiDotNetCore/Queries/Internal/QueryLayerComposer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ private IncludeExpression RewriteIncludeForSecondaryEndpoint(IncludeExpression r
297297
return new IncludeExpression(parentElement.AsArray());
298298
}
299299

300-
private FilterExpression CreateFilterByIds<TId>(ICollection<TId> ids, AttrAttribute idAttribute, FilterExpression existingFilter)
300+
private FilterExpression CreateFilterByIds<TId>(IReadOnlyCollection<TId> ids, AttrAttribute idAttribute, FilterExpression existingFilter)
301301
{
302302
var idChain = new ResourceFieldChainExpression(idAttribute);
303303

@@ -310,7 +310,7 @@ private FilterExpression CreateFilterByIds<TId>(ICollection<TId> ids, AttrAttrib
310310
}
311311
else if (ids.Count > 1)
312312
{
313-
List<LiteralConstantExpression> constants = ids.Select(id => new LiteralConstantExpression(id.ToString())).ToList();
313+
ImmutableHashSet<LiteralConstantExpression> constants = ids.Select(id => new LiteralConstantExpression(id.ToString())).ToImmutableHashSet();
314314
filter = new AnyExpression(idChain, constants);
315315
}
316316

test/JsonApiDotNetCoreExampleTests/UnitTests/QueryStringParameters/FilterParseTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ public void Reader_Read_Fails(string parameterName, string parameterValue, strin
131131
[InlineData("filter", "contains(title,'this')", null, "contains(title,'this')")]
132132
[InlineData("filter", "startsWith(title,'this')", null, "startsWith(title,'this')")]
133133
[InlineData("filter", "endsWith(title,'this')", null, "endsWith(title,'this')")]
134-
[InlineData("filter", "any(title,'this','that','there')", null, "any(title,'this','that','there')")]
134+
[InlineData("filter", "any(title,'this','that','there')", null, "any(title,'that','there','this')")]
135135
[InlineData("filter", "and(contains(title,'sales'),contains(title,'marketing'),contains(title,'advertising'))", null,
136136
"and(contains(title,'sales'),contains(title,'marketing'),contains(title,'advertising'))")]
137137
[InlineData("filter[posts]", "or(and(not(equals(author.userName,null)),not(equals(author.displayName,null))),not(has(comments,startsWith(text,'A'))))",

0 commit comments

Comments
 (0)