Skip to content

Commit

Permalink
Issue SFDO-Community#239 - support backwards compat on orderby for si…
Browse files Browse the repository at this point in the history
…ngle field ctx

See
SFDO-Community#239 (comment)
  • Loading branch information
jondavis9898 committed Aug 30, 2015
1 parent e24f7a1 commit 7db44b1
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 4 deletions.
4 changes: 3 additions & 1 deletion rolluptool/src/classes/LREngine.cls
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,9 @@ public class LREngine {

// #4 Order by clause fields
// i.e. Amount ASC NULLS FIRST, Name DESC NULL LAST
String orderByClause = ctx.lookupField.getName() + (String.isBlank(ctx.detailOrderByClause) ? '' : (',' + ctx.detailOrderByClause));
// in order to maintain backwards compat, if there is no orderby specified and if the context contains only one rollupsummaryfield
// add the detail field to the orderby. See https://github.com/afawcett/declarative-lookup-rollup-summaries/issues/239#issuecomment-136122959.
String orderByClause = ctx.lookupField.getName() + (String.isBlank(ctx.detailOrderByClause) ? (ctx.fieldsToRoll.size() == 1 ? (',' + ctx.fieldsToRoll[0].detail.getName()) : '') : (',' + ctx.detailOrderByClause));

// build approprite soql for this rollup context
String soql =
Expand Down
88 changes: 85 additions & 3 deletions rolluptool/src/classes/RollupServiceTest.cls
Original file line number Diff line number Diff line change
Expand Up @@ -1103,9 +1103,15 @@ private with sharing class RollupServiceTest
}

/**
* Current default behavior of LREngine is to order by relationship field then by the orderby
* clause specified in the Context. If no order by is specified in the context, the only field
* that will be specified in the order by projection is the RelationshipField__c field.
* Current default behavior of LREngine is to build the order by clause based on the following
* 1) RelationshipField__c (context.lookupfield in LRE) then by
* 2) If FieldToOrderBy__c (context.detailOrderByClause in LRE) is blank:
* If Context RollupSummaryFields.size() == 1 (context.fieldsToRoll) add FieldToAggregate__c (context.fieldsToRoll[0].detail in LRE)
* If Context RollupSummaryFields.size() > 1 (context.fieldsToRoll) then do not add any additional fields to orderby
* 3) If FieldToOrderBy__c (context.detailOrderByClause in LRE) is not blank, add FieldToOrderBy__c to orderby
*
* This results in all queries having an order by of at least RelationshipField__c even if no orderby
* is specified in FieldToOrderBy__c.
*
* Current default behavior of DLRS is to build the context with all rollupsummaries
* retrieving them ordered by ParentObject__c (Account) and then by RelationshipField__c (e.g. AccountId)
Expand Down Expand Up @@ -1243,6 +1249,82 @@ private with sharing class RollupServiceTest
System.assertEquals(expectedResultB, accountResult.Description);
}

/**
* Test default behavior with no order by making sure order is based on fieldtoaggregate
*
* See comment above setupMultiRollupDifferentTypes for information on how orderby is established
*
* Note that there is no reliable method for testing the situation where multiple fields that do not have
* orderby specified and would share a context. The reason for this is that the order is
* non-deterministic and there is no way currently (without adding one) to evaluate the SOQL generated.
* Using debug statements, it's been verified that only RelationshipField__c is included in
* order by in this scenario.
*/
private testmethod static void testSingleQueryRollupNoOrderByVerifyDetailFieldIncludedInOrderBy()
{
// Test supported?
if(!TestContext.isSupported())
return;

Schema.SObjectType parentType = LookupParent__c.sObjectType;
Schema.SObjectType childType = LookupChild__c.sObjectType;
String parentObjectName = parentType.getDescribe().getName();
String childObjectName = childType.getDescribe().getName();
String relationshipField = LookupChild__c.LookupParent__c.getDescribe().getName();
String aggregateField = LookupChild__c.Color__c.getDescribe().getName();
String aggregateResultField = LookupParent__c.Colours__c.getDescribe().getName();

// Create a picklist rollup
LookupRollupSummary__c rollupSummary = new LookupRollupSummary__c();
rollupSummary.Name = 'Test Rollup';
rollupSummary.ParentObject__c = parentObjectName;
rollupSummary.ChildObject__c = childObjectName;
rollupSummary.RelationShipField__c = relationshipField;
rollupSummary.FieldToAggregate__c = aggregateField;
rollupSummary.FieldToOrderBy__c = null;
rollupSummary.AggregateOperation__c = RollupSummaries.AggregateOperation.Concatenate.name();
rollupSummary.AggregateResultField__c = aggregateResultField;
rollupSummary.ConcatenateDelimiter__c = ';';
rollupSummary.Active__c = true;
rollupSummary.CalculationMode__c = 'Realtime';
insert rollupSummary;

// Insert parents
SObject parentA = parentType.newSObject();
parentA.put('Name', 'ParentA');
SObject parentB = parentType.newSObject();
parentB.put('Name', 'ParentB');
SObject parentC = parentType.newSObject();
parentC.put('Name', 'ParentC');
List<SObject> parents = new List<SObject> { parentA, parentB, parentC };
insert parents;

// Insert children
List<SObject> children = new List<SObject>();
for(SObject parent : parents)
{
SObject child1 = childType.newSObject();
child1.put(relationshipField, parent.Id);
child1.put(aggregateField, 'silver');
children.add(child1);
SObject child2 = childType.newSObject();
child2.put(relationshipField, parent.Id);
child2.put(aggregateField, 'orange');
children.add(child2);
SObject child3 = childType.newSObject();
child3.put(relationshipField, parent.Id);
child3.put(aggregateField, 'purple');
children.add(child3);
}
insert children;

// Assert rollups
Map<Id, SObject> assertParents = new Map<Id, SObject>(Database.query(String.format('select id, {0} from {1}', new List<String>{ aggregateResultField, parentObjectName })));
System.assertEquals('orange;purple;silver', (String) assertParents.get(parentA.id).get(aggregateResultField));
System.assertEquals('orange;purple;silver', (String) assertParents.get(parentB.id).get(aggregateResultField));
System.assertEquals('orange;purple;silver', (String) assertParents.get(parentC.id).get(aggregateResultField));
}

private testmethod static void testPicklistRollup()
{
// Test supported?
Expand Down
98 changes: 98 additions & 0 deletions rolluptool/src/classes/TestLREngine.cls
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,104 @@ private class TestLREngine {
'Won,Won,Lost');
}

/**
* Current default behavior of LREngine is to build the order by clause based on the following
* 1) LookupField then by
* 2) If detailOrderByClause is blank:
* If context.fieldsToRoll.size() == 1 add context.fieldsToRoll[0].detail
* If context.fieldsToRoll.size() > 1 then do not add any additional fields to orderby
* 3) If detailOrderByClause is not blank, add detailOrderByClause to orderby
*
* This results in all queries having an order by of at least lookupfield even if no orderby
* is specified on the context.
*
* The context.fieldsToRoll.size() == 1 scenario is included strictly to maintain as much backwards compatibility
* as possible from previous release. See https://github.com/afawcett/declarative-lookup-rollup-summaries/issues/239#issuecomment-136122959
*
* Note that there is no reliable method for testing that when multiple fields are included in context
* and no order by is specified that the only field included in the orderby is the lookupfield. To do
* this the SOQL would have to be exposed from LREngine.rollup and then parsed/evaluated and this seems
* kind of klunky. Using debug statements, it's been verified that only lookupfield is included in
* order by in this scenario.
*
* Test that when no orderby is specified and only a single field is included in the context
* that the data is ordered by the fieldtoaggregate (StageName)
*/
static testMethod void testSingleRollupSummaryFieldWithNoOrderByConcatVerifyDetailFieldIncludedInOrderBy() {
// create seed data
prepareData2();

LREngine.Context ctx = new LREngine.Context(Account.SobjectType,
Opportunity.SobjectType,
Schema.SObjectType.Opportunity.fields.AccountId);

LREngine.RollupSummaryField rollupField =
new LREngine.RollupSummaryField(
Schema.SObjectType.Account.fields.Description,
Schema.SObjectType.Opportunity.fields.StageName,
LREngine.RollupOperation.Concatenate, ','
);
ctx.add(rollupField);

SObject[] masters = LREngine.rollUp(ctx, detailRecords2);

Map<Id, SObject> mastersById = new Map<Id, SObject>(masters);
Account reloadedAcc3 = (Account)mastersById.get(acc3.Id);
Account reloadedAcc4 = (Account)mastersById.get(acc4.Id);
System.assertEquals(2, masters.size());
System.assertEquals('blue,red,yellow', reloadedAcc3.get(rollupField.master.getName()));
System.assertEquals('green,orange,purple', reloadedAcc4.get(rollupField.master.getName()));
}

/**
* Current default behavior of LREngine is to build the order by clause based on the following
* 1) LookupField then by
* 2) If detailOrderByClause is blank:
* If context.fieldsToRoll.size() == 1 add context.fieldsToRoll[0].detail
* If context.fieldsToRoll.size() > 1 then do not add any additional fields to orderby
* 3) If detailOrderByClause is not blank, add detailOrderByClause to orderby
*
* This results in all queries having an order by of at least lookupfield even if no orderby
* is specified on the context.
*
* The context.fieldsToRoll.size() == 1 scenario is included strictly to maintain as much backwards compatibility
* as possible from previous release. See https://github.com/afawcett/declarative-lookup-rollup-summaries/issues/239#issuecomment-136122959
*
* Note that there is no reliable method for testing that when multiple fields are included in context
* and no order by is specified that the only field included in the orderby is the lookupfield. To do
* this the SOQL would have to be exposed from LREngine.rollup and then parsed/evaluated and this seems
* kind of klunky. Using debug statements, it's been verified that only lookupfield is included in
* order by in this scenario.
*
* Test that when no orderby is specified and only a single field is included in the context
* that the data is ordered by the fieldtoaggregate (Name)
*/
static testMethod void testSingleRollupSummaryFieldWithNoOrderByLastVerifyDetailFieldIncludedInOrderBy() {
// create seed data
prepareData2();

LREngine.Context ctx = new LREngine.Context(Account.SobjectType,
Opportunity.SobjectType,
Schema.SObjectType.Opportunity.fields.AccountId);

LREngine.RollupSummaryField rollupField =
new LREngine.RollupSummaryField(
Schema.SObjectType.Account.fields.Description,
Schema.SObjectType.Opportunity.fields.Name,
LREngine.RollupOperation.Last
);
ctx.add(rollupField);

SObject[] masters = LREngine.rollUp(ctx, detailRecords2);

Map<Id, SObject> mastersById = new Map<Id, SObject>(masters);
Account reloadedAcc3 = (Account)mastersById.get(acc3.Id);
Account reloadedAcc4 = (Account)mastersById.get(acc4.Id);
System.assertEquals(2, masters.size());
System.assertEquals('o3Acc3', reloadedAcc3.get(rollupField.master.getName()));
System.assertEquals('o3Acc4', reloadedAcc4.get(rollupField.master.getName()));
}

static testMethod void testRollupConcatenateOrderByMultipleAscendingNullsFirst() {
testRollup2(
'CloseDate, Type, Amount, Name',
Expand Down

0 comments on commit 7db44b1

Please sign in to comment.