Skip to content

Commit 4093b6c

Browse files
authored
Dynamic Query Improvements: aggregate function exceptions, Filter Group bulkification, SubQuery dynamic order (#153)
1 parent 53522d4 commit 4093b6c

File tree

2 files changed

+119
-1
lines changed

2 files changed

+119
-1
lines changed

force-app/main/default/classes/main/standard-soql/SOQL.cls

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ public virtual inherited sharing class SOQL implements Queryable {
163163
SubQuery with(SObjectField field1, SObjectField field2, SObjectField field3, SObjectField field4);
164164
SubQuery with(SObjectField field1, SObjectField field2, SObjectField field3, SObjectField field4, SObjectField field5);
165165
SubQuery with(Iterable<SObjectField> fields);
166+
SubQuery with(String fields);
166167
SubQuery with(String relationshipName, Iterable<SObjectField> fields);
167168
SubQuery with(SubQuery subQuery);
168169
// WHERE
@@ -171,6 +172,7 @@ public virtual inherited sharing class SOQL implements Queryable {
171172
//ORDER BY
172173
SubQuery orderBy(SObjectField field);
173174
SubQuery orderBy(String relationshipName, SObjectField field);
175+
SubQuery orderBy(String field, String direction);
174176
SubQuery sortDesc();
175177
SubQuery nullsLast();
176178
// LIMIT
@@ -186,7 +188,9 @@ public virtual inherited sharing class SOQL implements Queryable {
186188
// ADD CONDITION
187189
FilterGroup add(FilterGroup filterGroup);
188190
FilterGroup add(Filter filter);
191+
FilterGroup addAll(List<Filter> filters);
189192
FilterGroup add(String dynamicCondition);
193+
FilterGroup addAll(List<String> dynamicConditions);
190194
// ORDER
191195
FilterGroup anyConditionMatching();
192196
FilterGroup conditionLogic(String order);
@@ -1090,7 +1094,12 @@ public virtual inherited sharing class SOQL implements Queryable {
10901094

10911095
private Boolean isAggregateFunction(String field) {
10921096
// AVG(), COUNT(), MIN(), MAX(), SUM() or Field aliasing
1093-
return field.contains('(') && field.contains(')') || field.contains(' ');
1097+
// Exclude other SOQL Funcions ex. toLabel(), convertCurrency(), FORMAT()
1098+
Set<String> exceptionFunctions = new Set<String>{ 'toLabel', 'convertCurrency', 'FORMAT' };
1099+
Pattern exceptionFunctionsRegexPattern = Pattern.compile('(?i)\\b(' + String.join(new List<String>(exceptionFunctions), '|') + ')\\s*\\(');
1100+
Matcher exceptionFunctionsMatcher = exceptionFunctionsRegexPattern.matcher(field);
1101+
1102+
return field.contains('(') && field.contains(')') && !exceptionFunctionsMatcher.find() || field.contains(' ');
10941103
}
10951104

10961105
public void with(Iterable<SObjectField> fields) {
@@ -1204,6 +1213,11 @@ public virtual inherited sharing class SOQL implements Queryable {
12041213
return this;
12051214
}
12061215

1216+
public SubQuery with(String fields) {
1217+
builder.fields.with(fields);
1218+
return this;
1219+
}
1220+
12071221
public SubQuery with(String relationshipName, Iterable<SObjectField> fields) {
12081222
builder.fields.with(relationshipName, fields);
12091223
return this;
@@ -1234,6 +1248,11 @@ public virtual inherited sharing class SOQL implements Queryable {
12341248
return this;
12351249
}
12361250

1251+
public SubQuery orderBy(String field, String direction) {
1252+
builder.orderBys.newOrderBy().with(field).sortingOrder(direction);
1253+
return this;
1254+
}
1255+
12371256
public SubQuery sortDesc() {
12381257
builder.latestOrderBy.sortDesc();
12391258
return this;
@@ -1349,10 +1368,24 @@ public virtual inherited sharing class SOQL implements Queryable {
13491368
return add(new FilterAdapter(filter));
13501369
}
13511370

1371+
public FilterGroup addAll(List<Filter> filters) {
1372+
for (Filter filter: filters) {
1373+
add(filter);
1374+
}
1375+
return this;
1376+
}
1377+
13521378
public FilterGroup add(String dynamicCondition) {
13531379
return add(new StringConditionAdapter(dynamicCondition));
13541380
}
13551381

1382+
public FilterGroup addAll(List<String> dynamicConditions) {
1383+
for (String dynamicCondition: dynamicConditions) {
1384+
add(dynamicCondition);
1385+
}
1386+
return this;
1387+
}
1388+
13561389
public FilterGroup add(FilterClause condition) {
13571390
if (condition.hasValue()) {
13581391
queryConditions.add(condition);

force-app/main/default/classes/main/standard-soql/SOQL_Test.cls

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,17 @@ private class SOQL_Test {
558558
Assert.areEqual('SELECT CampaignId campaign, AVG(Amount) amount FROM Opportunity GROUP BY CampaignId', soql, 'The generated SOQL should match the expected one.');
559559
}
560560

561+
@IsTest
562+
static void withStringFieldsAndToLabelFunction() {
563+
// Test
564+
String soql = SOQL.of(Case.SObjectType)
565+
.with('Id, toLabel(Status), Subject')
566+
.toString();
567+
568+
// Verify
569+
Assert.areEqual('SELECT Id, toLabel(Status), Subject FROM Case', soql);
570+
}
571+
561572
@IsTest
562573
static void withFieldAlias() {
563574
// Test
@@ -830,6 +841,21 @@ private class SOQL_Test {
830841
Assert.areEqual('SELECT Name , (SELECT Id, Name FROM Contacts ORDER BY Name DESC NULLS LAST) FROM Account', soql, 'The generated SOQL should match the expected one.');
831842
}
832843

844+
@IsTest
845+
static void subQueryOrderByDynamic() {
846+
// Test
847+
String soql = SOQL.of(Account.SObjectType)
848+
.with(Account.Name)
849+
.with(SOQL.SubQuery.of('Contacts')
850+
.with(Contact.Id, Contact.Name)
851+
.orderBy('Name', 'ASC')
852+
.nullsLast()
853+
).toString();
854+
855+
// Verify
856+
Assert.areEqual('SELECT Name , (SELECT Id, Name FROM Contacts ORDER BY Name ASC NULLS LAST) FROM Account', soql);
857+
}
858+
833859
@IsTest
834860
static void subQueryOrderByRelated() {
835861
// Test
@@ -1540,6 +1566,65 @@ private class SOQL_Test {
15401566
Assert.areEqual('Krakow', binding.get('v2'), 'The binding variable should match the expected value.');
15411567
}
15421568

1569+
@IsTest
1570+
static void dynamicFiltersListGroup() {
1571+
// Setup
1572+
SOQL.FilterGroup filterGroup = SOQL.FilterGroup;
1573+
1574+
filterGroup.addAll(new List<SOQL.Filter> {
1575+
SOQL.Filter.with(Account.Name).equal('Test'),
1576+
SOQL.Filter.with(Account.BillingCity).equal('Krakow')
1577+
});
1578+
1579+
// Test
1580+
SOQL builder = SOQL.of(Account.SObjectType)
1581+
.whereAre(filterGroup);
1582+
1583+
// Verify
1584+
String soql = builder.toString();
1585+
Assert.areEqual('SELECT Id FROM Account WHERE (Name = :v1 AND BillingCity = :v2)', soql);
1586+
1587+
Map<String, Object> binding = builder.binding();
1588+
Assert.areEqual('Test', binding.get('v1'));
1589+
Assert.areEqual('Krakow', binding.get('v2'));
1590+
}
1591+
1592+
@IsTest
1593+
static void dynamicStringFiltersGroup() {
1594+
// Setup
1595+
SOQL.FilterGroup filterGroup = SOQL.FilterGroup;
1596+
1597+
filterGroup.add('Name = \'Test\'');
1598+
filterGroup.add('BillingCity = \'Krakow\'');
1599+
1600+
// Test
1601+
SOQL builder = SOQL.of(Account.SObjectType)
1602+
.whereAre(filterGroup);
1603+
1604+
// Verify
1605+
String soql = builder.toString();
1606+
Assert.areEqual('SELECT Id FROM Account WHERE (Name = \'Test\' AND BillingCity = \'Krakow\')', soql);
1607+
}
1608+
1609+
@IsTest
1610+
static void dynamicStringFiltersListGroup() {
1611+
// Setup
1612+
SOQL.FilterGroup filterGroup = SOQL.FilterGroup;
1613+
1614+
filterGroup.addAll(new List<String> {
1615+
'Name = \'Test\'',
1616+
'BillingCity = \'Krakow\''
1617+
});
1618+
1619+
// Test
1620+
SOQL builder = SOQL.of(Account.SObjectType)
1621+
.whereAre(filterGroup);
1622+
1623+
// Verify
1624+
String soql = builder.toString();
1625+
Assert.areEqual('SELECT Id FROM Account WHERE (Name = \'Test\' AND BillingCity = \'Krakow\')', soql);
1626+
}
1627+
15431628
@IsTest
15441629
static void dynamicFiltersGroupOnSoqlInstance() {
15451630
// Test

0 commit comments

Comments
 (0)