@@ -73,6 +73,8 @@ public class fflib_QueryFactory { //No explicit sharing declaration - inherit fr
7373 **/
7474 private Boolean enforceFLS ;
7575
76+ private Boolean sortSelectFields = true ;
77+
7678 /**
7779 * The relationship and subselectQueryMap variables are used to support subselect queries. Subselects can be added to
7880 * a query, as long as it isn't a subselect query itself. You may have many subselects inside
@@ -173,6 +175,17 @@ public class fflib_QueryFactory { //No explicit sharing declaration - inherit fr
173175 return this ;
174176 }
175177
178+ /**
179+ * Sets a flag to indicate that this query should have ordered
180+ * query fields in the select statement (this at a small cost to performance).
181+ * If you are processing large query sets, you should switch this off.
182+ * @param whether or not select fields should be sorted in the soql statement.
183+ **/
184+ public fflib_QueryFactory setSortSelectFields (Boolean doSort ){
185+ this .sortSelectFields = doSort ;
186+ return this ;
187+ }
188+
176189 /**
177190 * Selects a single field from the SObject specified in {@link #table}.
178191 * Selecting fields is idempotent, if this field is already selected calling this method will have no additional impact.
@@ -408,6 +421,10 @@ public class fflib_QueryFactory { //No explicit sharing declaration - inherit fr
408421 }
409422
410423 fflib_QueryFactory subselectQuery = new fflib_QueryFactory (relationship );
424+
425+ // The child queryFactory should be configured in the same way as the parent by default - can override after if required
426+ subSelectQuery .setSortSelectFields (sortSelectFields );
427+
411428 if (assertIsAccessible ){
412429 subSelectQuery .assertIsAccessible ();
413430 }
@@ -537,13 +554,17 @@ public class fflib_QueryFactory { //No explicit sharing declaration - inherit fr
537554 if (fields .size () == 0 ){
538555 if (enforceFLS ) fflib_SecurityUtils .checkFieldIsReadable (table , ' Id' );
539556 result += ' Id ' ;
540- }else {
557+ }else if ( sortSelectFields ) {
541558 List <QueryField > fieldsToQuery = new List <QueryField >(fields );
542559 fieldsToQuery .sort (); // delegates to QueryFilter's comparable implementation
543560 for (QueryField field : fieldsToQuery ){
544561 result += field + ' , ' ;
545562 }
546- }
563+ }else {
564+ for (QueryField field : fields )
565+ result += field + ' , ' ;
566+ }
567+
547568 if (subselectQueryMap != null && ! subselectQueryMap .isEmpty ()){
548569 for (fflib_QueryFactory childRow : subselectQueryMap .values ()){
549570 result += ' (' + childRow .toSOQL () + ' ), ' ;
@@ -675,57 +696,70 @@ public class fflib_QueryFactory { //No explicit sharing declaration - inherit fr
675696 }
676697 public override String toString (){
677698 String result = ' ' ;
678- Iterator <Schema .sObjectField > i = fields .iterator ();
679- while (i .hasNext ()){
680- String fieldName = i .next ().getDescribe ().getName ();
681- if (fieldName .endsWithIgnoreCase (' Id' ) && i .hasNext ())
682- fieldName = fieldName .removeEndIgnoreCase (' Id' );
683- if (fieldName .endsWithIgnoreCase (' __c' ) && i .hasNext ())
684- fieldName = fieldName .removeEndIgnoreCase (' __c' )+ ' __r' ;
685- result += fieldName + (i .hasNext () ? ' .' : ' ' );
699+ Integer size = fields .size ();
700+ for (Integer i = 0 ; i < size ; i ++ )
701+ {
702+ if (i > 0 )
703+ {
704+ if (result .endsWithIgnoreCase (' Id' ))
705+ result = result .removeEndIgnoreCase (' Id' );
706+ else if (result .endsWithIgnoreCase (' __c' )) {
707+ result = result .removeEndIgnoreCase (' __c' ) + ' __r' ;
708+ }
709+ result += ' .' ;
710+ }
711+ result += fields [i ].getDescribe ().getName ();
686712 }
687713 return result ;
688714 }
689715 public integer hashCode (){
690716 return String .valueOf (this .fields ).hashCode ();
691717 }
692718 public boolean equals (Object obj ){
693- if (! (obj instanceof QueryField ))
719+ // Easy checks first
720+ if (obj == null || ! (obj instanceof QueryField ))
694721 return false ;
695- if ( String .valueOf (((QueryField ) obj ).fields ) != String .valueOf (this .fields ))
722+
723+ if (this == = obj )
724+ return true ;
725+
726+ // Detailed checks
727+ QueryField other = (QueryField )obj ;
728+ Integer size = fields .size ();
729+ if (size != other .fields .size ())
696730 return false ;
697- Set <Schema .SObjectField > objFields = new Set <Schema .SObjectField >();
698- objFields .addAll ( ((QueryField )obj ).fields );
699- objFields .retainAll (this .fields );
700- objFields .removeAll (this .fields );
701- return objFields .size () == 0 ;
731+
732+ for (Integer i = 0 ; i < size ; i ++ )
733+ if (fields [i ] != (other .fields [i ]))
734+ return false ;
735+
736+ return true ;
702737 }
703738 /**
704- * Allows sorting QueryField instances, which means we'll get deterministic field ordering by just sorting the parent
705- * QueryFactory's array when toSOQL'ing.
739+ * Allows sorting QueryField instances.
706740 *
707741 * Returns:
708742 * - Objects that are not QueryField instances as -2, which functions as -1 but with more flair
709- * - QueryField instances with less joins in their path as -1
710- * - QueryField instances with an equal number of joins and alphabetically first as an undefined negative integer
711- * - equals as 0
712- * - anything else an undefined positive integer (usually, but not always 1)
743+ * - Equivalent QueryFields return 0.
744+ * - QueryFields with more joins give +1, while fewer joins give -1
745+ * - For anything else, compare the toStrings of this and the supplied object.
713746 **/
714747 public Integer compareTo (Object o ){
715- if (! (o instanceof QueryField ))
748+ if (o == null || ! (o instanceof QueryField ))
716749 return - 2 ; // We can't possibly do a sane comparison against an unknwon type, go athead and let it "win"
717- QueryField that = (QueryField ) o ;
718- if (this .fields .size () < that .fields .size ()){
750+
751+ if (this == = o )
752+ return 0 ;
753+
754+ QueryField other = (QueryField )o ;
755+ Integer size = fields .size ();
756+ Integer sizeOther = other .fields .size ();
757+ if (size < sizeOther )
719758 return - 1 ;
720- }else if ( this .fields .size () == that .fields .size () ){
721- if (this .equals (that )){
722- return 0 ;
723- }else {
724- return this .toString ().compareTo (that .toString ());
725- }
726- }else {
759+ if (size > sizeOther )
727760 return 1 ;
728- }
761+
762+ return this .toString ().compareTo (other .toString ());
729763 }
730764 }
731765
0 commit comments