Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release/2.2.2 [IN PROGRESS] #83

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 154 additions & 22 deletions force-app/main/default/classes/SOQL.cls
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ public virtual inherited sharing class SOQL implements Queryable {
Queryable count();
Queryable count(SObjectField field);
Queryable count(SObjectField field, String alias);
// GROUPING
Queryable grouping(SObjectField field, String alias);
// USING SCOPE
Queryable delegatedScope();
Queryable mineScope();
Expand All @@ -81,6 +83,7 @@ public virtual inherited sharing class SOQL implements Queryable {
// GROUP BY
Queryable groupBy(SObjectField field);
Queryable groupByRollup(SObjectField field);
Queryable groupByCube(SObjectField field);
// ORDER BY
Queryable orderBy(String field);
Queryable orderBy(String field, String direction);
Expand Down Expand Up @@ -123,6 +126,10 @@ public virtual inherited sharing class SOQL implements Queryable {
List<SObject> toList();
List<AggregateResult> toAggregated();
Map<Id, SObject> toMap();
Map<String, SObject> toMap(SObjectField keyField);
Map<String, String> toMap(SObjectField keyField, SObjectField valueField);
Map<String, List<SObject>> toAggregatedMap(SObjectField keyField);
Map<String, List<String>> toAggregatedMap(SObjectField keyField, SObjectField valueField);
Database.QueryLocator toQueryLocator();
}

Expand Down Expand Up @@ -243,6 +250,8 @@ public virtual inherited sharing class SOQL implements Queryable {
private QueryBuilder builder;
private Executor executor;

private String ofObject;

public static SOQL of(SObjectType ofObject) {
return new SOQL(ofObject);
}
Expand All @@ -256,6 +265,7 @@ public virtual inherited sharing class SOQL implements Queryable {
}

public SOQL(String ofObject) {
this.ofObject = ofObject;
binder = new Binder();
executor = new Executor();
builder = new QueryBuilder(ofObject);
Expand Down Expand Up @@ -346,6 +356,11 @@ public virtual inherited sharing class SOQL implements Queryable {
return this;
}

public SOQL grouping(SObjectField field, String alias) {
builder.fields.grouping(field, alias);
return this;
}

public SOQL delegatedScope() {
builder.scope.delegated();
return this;
Expand Down Expand Up @@ -403,11 +418,19 @@ public virtual inherited sharing class SOQL implements Queryable {

public SOQL groupBy(SObjectField field) {
builder.groupBy.with(field);
builder.fields.withAggregatedField(field);
return this;
}

public SOQL groupByRollup(SObjectField field) {
builder.groupBy.rollup(field);
builder.fields.withAggregatedField(field);
return this;
}

public SOQL groupByCube(SObjectField field) {
builder.groupBy.cube(field);
builder.fields.withAggregatedField(field);
return this;
}

Expand Down Expand Up @@ -526,11 +549,12 @@ public virtual inherited sharing class SOQL implements Queryable {

public Set<String> toValuesOf(SObjectField fieldToExtract) {
// https://salesforce.stackexchange.com/questions/393308/get-a-list-of-one-column-from-a-soql-result
builder.fields.clearAllFields(); // other fields not needed
return new Map<String, SObject>(with(fieldToExtract, 'Id').groupBy(fieldToExtract).toAggregated()).keySet();
}

public Integer toInteger() {
if (builder.fields.areCountsEmpty()) {
if (!builder.fields.hasCount()) {
count();
}
return executor.toInteger(builder.toString(), binder.getBindingMap());
Expand All @@ -549,7 +573,76 @@ public virtual inherited sharing class SOQL implements Queryable {
}

public Map<Id, SObject> toMap() {
return new Map<Id, SObject>(toList());
Map<Id, SObject> idToSObject = (Map<Id, SObject>) Type.forName('Map<Id, ' + ofObject + ' >').newInstance();
idToSObject.putAll(toList());
return idToSObject;
}

public Map<String, SObject> toMap(SObjectField keyField) {
with(keyField);

Map<String, SObject> cutomKeyToRecord = (Map<String, SObject>) Type.forName('Map<String, ' + ofObject + ' >').newInstance();

for (SObject record : toList()) {
cutomKeyToRecord.put(String.valueOf(record.get(keyField)), record);
}

return cutomKeyToRecord;
}

public Map<String, String> toMap(SObjectField keyField, SObjectField valueField) {
builder.fields.clearAllFields(); // other fields not needed

with(keyField, valueField);

Map<String, String> cutomKeyToCustomValue = new Map<String, String>();

for (SObject record : toList()) {
cutomKeyToCustomValue.put(
String.valueOf(record.get(keyField)),
String.valueOf(record.get(valueField))
);
}

return cutomKeyToCustomValue;
}

public Map<String, List<SObject>> toAggregatedMap(SObjectField keyField) {
with(keyField);

Map<String, List<SObject>> cutomKeyToRecords = (Map<String, List<SObject>>) Type.forName('Map<String, List<' + ofObject + ' >>').newInstance();

for (SObject record : toList()) {
String customKey = String.valueOf(record.get(keyField));

if (!cutomKeyToRecords.containsKey(customKey)) {
cutomKeyToRecords.put(customKey, new List<SObject>());
}

cutomKeyToRecords.get(customKey).add(record);
}

return cutomKeyToRecords;
}

public Map<String, List<String>> toAggregatedMap(SObjectField keyField, SObjectField valueField) {
builder.fields.clearAllFields(); // other fields not needed

with(keyField, valueField);

Map<String, List<String>> customKeyToValues = new Map<String, List<String>>();

for (SObject record : toList()) {
String customKey = String.valueOf(record.get(keyField));

if (!customKeyToValues.containsKey(customKey)) {
customKeyToValues.put(customKey, new List<String>());
}

customKeyToValues.get(customKey).add(String.valueOf(record.get(valueField)));
}

return customKeyToValues;
}

public Database.QueryLocator toQueryLocator() {
Expand Down Expand Up @@ -684,10 +777,10 @@ public virtual inherited sharing class SOQL implements Queryable {

private class QFields implements QueryClause {
private Set<String> fields = new Set<String>();
private Set<String> counts = new Set<String>();
private Set<String> aggregatedFields = new Set<String>();
private Boolean hasCount = false;

public void count() {
// COUNT() must be the only element in the SELECT list.
count('COUNT()');
}

Expand All @@ -697,20 +790,33 @@ public virtual inherited sharing class SOQL implements Queryable {

public void count(SObjectField field, String alias) {
count('COUNT(' + field + ') ' + alias);
fields.add('COUNT(' + field + ') ' + alias);
}

private void count(String count) {
this.hasCount = true;
withAggregatedField(count);
fields.add(count);
}

public void count(String countSoql) {
// Clear all default fields to avoid "Field must be grouped or aggregated"
clearAllFields();
counts.add(countSoql);
public void grouping(SObjectField field, String alias) {
withAggregatedField('GROUPING(' + field + ') ' + alias);
fields.add('GROUPING(' + field + ') ' + alias);
}

public void with(SObjectField field, String alias) {
// Only aggregate expressions use field aliasing. Clear all default fields to avoid "Field must be grouped or aggregated"
clearAllFields();
withAggregatedField(field + ' ' + alias);
fields.add(field + ' ' + alias);
}

public void withAggregatedField(SObjectField field) {
withAggregatedField(field.getDescribe().getName());
}

public void withAggregatedField(String field) {
aggregatedFields.add(field);
}

public void with(String stringFields) {
// To avoid field duplicates in query
fields.addAll(stringFields.deleteWhitespace().split(','));
Expand Down Expand Up @@ -740,21 +846,30 @@ public virtual inherited sharing class SOQL implements Queryable {
fields.clear();
}

public Boolean areCountsEmpty() {
return counts.isEmpty();
public Boolean hasCount() {
return hasCount;
}

public override String toString() {
if (fields.isEmpty() && counts.isEmpty()) {
removeNotAggregatedFieldsFromAggregateSoql();

if (fields.isEmpty()) {
return 'SELECT Id';
}

List<String> selectStatement = new List<String>();

selectStatement.addAll(counts);
selectStatement.addAll(fields);
return 'SELECT ' + String.join(fields, ', ');
}

return 'SELECT ' + String.join(selectStatement, ', ');
public void removeNotAggregatedFieldsFromAggregateSoql() {
if (aggregatedFields.isEmpty()) {
return;
}
// Clear not grouped or aggregated fields to avoid "Field must be grouped or aggregated" error
for (String field : fields) {
if (!aggregatedFields.contains(field)) {
fields.remove(field);
}
}
}
}

Expand Down Expand Up @@ -1237,10 +1352,10 @@ public virtual inherited sharing class SOQL implements Queryable {

public override String toString() {
if (skipBinding) {
return String.format(wrapper, new List<String> { field + ' ' + comperator + ' ' + value });
return String.format(wrapper, new List<String>{ field + ' ' + comperator + ' ' + value });
}

return String.format(wrapper, new List<String> { field + ' ' + comperator + ' :' + binder.bind(value) });
return String.format(wrapper, new List<String>{ field + ' ' + comperator + ' :' + binder.bind(value) });
}
}

Expand Down Expand Up @@ -1274,17 +1389,34 @@ public virtual inherited sharing class SOQL implements Queryable {

private class QGroupBy implements QueryClause {
private Set<String> groupByFields = new Set<String>();
private String groupByFunction = '';

public void with(SObjectField field) {
setGroupByFunction('{0}');
groupByFields.add(field.getDescribe().getName());
}

public void rollup(SObjectField field) {
groupByFields.add('ROLLUP(' + field + ')');
setGroupByFunction('ROLLUP({0})');
groupByFields.add(field.getDescribe().getName());
}

public void cube(SObjectField field) {
setGroupByFunction('CUBE({0})');
groupByFields.add(field.getDescribe().getName());
}

public void setGroupByFunction(String newGroupByFunction) {
if (String.isNotEmpty(groupByFunction) && groupByFunction != newGroupByFunction) {
QueryException e = new QueryException();
e.setMessage('You cant use GROUP BY, GROUP BY ROLLUP and GROUP BY CUBE in the same query.');
throw e;
}
this.groupByFunction = newGroupByFunction;
}

public override String toString() {
return 'GROUP BY ' + String.join(groupByFields, ', ');
return 'GROUP BY ' + String.format(groupByFunction, new List<String>{ String.join(groupByFields, ', ') });
}
}

Expand Down
Loading