Skip to content

Commit c1ca475

Browse files
committed
perf(QueryParser): improve sort parsing perf
AscendingSort: 30.9% improvement DescendingSort: 20% improvement BEFORE Method | Mean | Error | StdDev| --------------- |---------:|----------:|----------:| AscendingSort | 4.558 us | 0.2451 us | 0.6832 us | DescendingSort | 4.218 us | 0.1780 us | 0.4990 us | AFTER Method | Mean | Error | StdDev | --------------- |---------:|----------:|----------:| AscendingSort | 3.146 us | 0.0326 us | 0.0709 us | DescendingSort | 3.372 us | 0.1228 us | 0.2618 us |
1 parent fbf7ecf commit c1ca475

File tree

1 file changed

+44
-58
lines changed

1 file changed

+44
-58
lines changed

src/JsonApiDotNetCore/Services/QueryParser.cs

Lines changed: 44 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -9,63 +9,52 @@
99
using JsonApiDotNetCore.Models;
1010
using Microsoft.AspNetCore.Http;
1111

12-
namespace JsonApiDotNetCore.Services
13-
{
14-
public interface IQueryParser
15-
{
12+
namespace JsonApiDotNetCore.Services {
13+
public interface IQueryParser {
1614
QuerySet Parse(IQueryCollection query);
1715
}
1816

19-
public class QueryParser : IQueryParser
20-
{
17+
public class QueryParser : IQueryParser {
2118
private readonly IControllerContext _controllerContext;
2219
private readonly JsonApiOptions _options;
2320

2421
public QueryParser(
2522
IControllerContext controllerContext,
26-
JsonApiOptions options)
27-
{
23+
JsonApiOptions options) {
2824
_controllerContext = controllerContext;
2925
_options = options;
3026
}
3127

32-
public virtual QuerySet Parse(IQueryCollection query)
33-
{
28+
public virtual QuerySet Parse(IQueryCollection query) {
3429
var querySet = new QuerySet();
35-
var disabledQueries = _controllerContext.GetControllerAttribute<DisableQueryAttribute>()?.QueryParams ?? QueryParams.None;
30+
var disabledQueries = _controllerContext.GetControllerAttribute<DisableQueryAttribute>() ? .QueryParams ?? QueryParams.None;
3631

37-
foreach (var pair in query)
38-
{
39-
if (pair.Key.StartsWith("filter"))
40-
{
32+
foreach (var pair in query) {
33+
if (pair.Key.StartsWith("filter")) {
4134
if (disabledQueries.HasFlag(QueryParams.Filter) == false)
4235
querySet.Filters.AddRange(ParseFilterQuery(pair.Key, pair.Value));
4336
continue;
4437
}
4538

46-
if (pair.Key.StartsWith("sort"))
47-
{
39+
if (pair.Key.StartsWith("sort")) {
4840
if (disabledQueries.HasFlag(QueryParams.Sort) == false)
4941
querySet.SortParameters = ParseSortParameters(pair.Value);
5042
continue;
5143
}
5244

53-
if (pair.Key.StartsWith("include"))
54-
{
45+
if (pair.Key.StartsWith("include")) {
5546
if (disabledQueries.HasFlag(QueryParams.Include) == false)
5647
querySet.IncludedRelationships = ParseIncludedRelationships(pair.Value);
5748
continue;
5849
}
5950

60-
if (pair.Key.StartsWith("page"))
61-
{
51+
if (pair.Key.StartsWith("page")) {
6252
if (disabledQueries.HasFlag(QueryParams.Page) == false)
6353
querySet.PageQuery = ParsePageQuery(querySet.PageQuery, pair.Key, pair.Value);
6454
continue;
6555
}
6656

67-
if (pair.Key.StartsWith("fields"))
68-
{
57+
if (pair.Key.StartsWith("fields")) {
6958
if (disabledQueries.HasFlag(QueryParams.Fields) == false)
7059
querySet.Fields = ParseFieldsQuery(pair.Key, pair.Value);
7160
continue;
@@ -78,26 +67,24 @@ public virtual QuerySet Parse(IQueryCollection query)
7867
return querySet;
7968
}
8069

81-
protected virtual List<FilterQuery> ParseFilterQuery(string key, string value)
82-
{
70+
protected virtual List<FilterQuery> ParseFilterQuery(string key, string value) {
8371
// expected input = filter[id]=1
8472
// expected input = filter[id]=eq:1
8573
var queries = new List<FilterQuery>();
8674

87-
var propertyName = key.Split('[', ']')[1].ToProperCase();
75+
var propertyName = key.Split('[', ']') [1].ToProperCase();
8876

8977
var values = value.Split(',');
90-
foreach (var val in values)
91-
{
92-
(var operation, var filterValue) = ParseFilterOperation(val);
78+
foreach (var val in values) {
79+
(var operation,
80+
var filterValue) = ParseFilterOperation(val);
9381
queries.Add(new FilterQuery(propertyName, filterValue, operation));
9482
}
9583

9684
return queries;
9785
}
9886

99-
protected virtual (string operation, string value) ParseFilterOperation(string value)
100-
{
87+
protected virtual(string operation, string value) ParseFilterOperation(string value) {
10188
if (value.Length < 3)
10289
return (string.Empty, value);
10390

@@ -116,13 +103,12 @@ protected virtual (string operation, string value) ParseFilterOperation(string v
116103
return (prefix, value);
117104
}
118105

119-
protected virtual PageQuery ParsePageQuery(PageQuery pageQuery, string key, string value)
120-
{
106+
protected virtual PageQuery ParsePageQuery(PageQuery pageQuery, string key, string value) {
121107
// expected input = page[size]=10
122108
// page[number]=1
123109
pageQuery = pageQuery ?? new PageQuery();
124110

125-
var propertyName = key.Split('[', ']')[1];
111+
var propertyName = key.Split('[', ']') [1];
126112

127113
if (propertyName == "size")
128114
pageQuery.PageSize = Convert.ToInt32(value);
@@ -134,28 +120,31 @@ protected virtual PageQuery ParsePageQuery(PageQuery pageQuery, string key, stri
134120

135121
// sort=id,name
136122
// sort=-id
137-
protected virtual List<SortQuery> ParseSortParameters(string value)
138-
{
123+
protected virtual List<SortQuery> ParseSortParameters(string value) {
124+
const char SORT_DELIMITER = ',';
125+
const char DESCENDING_SORT_OPERATOR = '-';
126+
139127
var sortParameters = new List<SortQuery>();
140-
value.Split(',').ToList().ForEach(p =>
141-
{
128+
var sortSegments = value.Split(SORT_DELIMITER);
129+
foreach (var sortSegment in sortSegments) {
130+
131+
var propertyName = sortSegment;
142132
var direction = SortDirection.Ascending;
143-
if (p[0] == '-')
144-
{
133+
134+
if (sortSegment[0] == DESCENDING_SORT_OPERATOR) {
145135
direction = SortDirection.Descending;
146-
p = p.Substring(1);
136+
propertyName = propertyName.Substring(1);
147137
}
148138

149-
var attribute = GetAttribute(p.ToProperCase());
139+
var attribute = GetAttribute(propertyName);
150140

151141
sortParameters.Add(new SortQuery(direction, attribute));
152-
});
142+
};
153143

154144
return sortParameters;
155145
}
156146

157-
protected virtual List<string> ParseIncludedRelationships(string value)
158-
{
147+
protected virtual List<string> ParseIncludedRelationships(string value) {
159148
if (value.Contains("."))
160149
throw new JsonApiException(400, "Deeply nested relationships are not supported");
161150

@@ -164,19 +153,17 @@ protected virtual List<string> ParseIncludedRelationships(string value)
164153
.ToList();
165154
}
166155

167-
protected virtual List<string> ParseFieldsQuery(string key, string value)
168-
{
156+
protected virtual List<string> ParseFieldsQuery(string key, string value) {
169157
// expected: fields[TYPE]=prop1,prop2
170-
var typeName = key.Split('[', ']')[1];
158+
var typeName = key.Split('[', ']') [1];
171159

172160
var includedFields = new List<string> { "Id" };
173161

174162
if (typeName != _controllerContext.RequestEntity.EntityName)
175163
return includedFields;
176164

177165
var fields = value.Split(',');
178-
foreach (var field in fields)
179-
{
166+
foreach (var field in fields) {
180167
var internalAttrName = _controllerContext.RequestEntity
181168
.Attributes
182169
.SingleOrDefault(attr => attr.PublicAttributeName == field)
@@ -187,12 +174,11 @@ protected virtual List<string> ParseFieldsQuery(string key, string value)
187174
return includedFields;
188175
}
189176

190-
protected virtual AttrAttribute GetAttribute(string propertyName)
191-
=> _controllerContext
192-
.RequestEntity
193-
.Attributes
194-
.FirstOrDefault(attr =>
195-
string.Equals(attr.InternalAttributeName, propertyName, StringComparison.OrdinalIgnoreCase)
196-
);
177+
protected virtual AttrAttribute GetAttribute(string propertyName) => _controllerContext
178+
.RequestEntity
179+
.Attributes
180+
.FirstOrDefault(attr =>
181+
string.Equals(attr.PublicAttributeName, propertyName, StringComparison.OrdinalIgnoreCase)
182+
);
197183
}
198-
}
184+
}

0 commit comments

Comments
 (0)