function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
akallioWileyakallioWiley 

Map Returns Null in keySet()

Hi All,

I'm having trouble debugging the following line, which is highlighted in code below: 

consCourseCodeMapBig.get(Con.Id).keySet()

for some reason the first value in the set is Null, and the second value is the value I'm expecting to receive. I have confirmed this by testing in UI with the system.assert that you see in the red highlighed section. That system assert returns the following:

Error:

courseRoleTrigger: execution of AfterInsert caused by: System.AssertException: Assertion Failed: Youre here {null, a0FZ0000001sjSzMAI} Class.contactToCourseCodeHandler.saveLogic: line 83, column 1 Class.contactToCourseCodeHandler.newCourseRoleHandler: line 10, column 1 Trigger.courseRoleTrigger: line 20, column 1

 

 

 

I didn't even think it was possible to have a null value in a keySet, and not sure where in my code it would be happening. The section highlighted in blue is where the map is being built. I have used system.asserts there,too and do not find null value there.

 

 

Thanks for any help!

 

 

 

public with sharing class contactToCourseCodeHandler {
    public static void newCourseRoleHandler(Set<Id> newCRs) {
        
        Map<Id,Map<Id,courseCodeWrapper>> contactToCourseCodeBig = createBigCCWMap(newCRS, 'cRole');
        Map<Id,courseCodeWrapper> contactToCourseCodeLittle = createLittleCCWMap(newCRS, 'cRole');
            
        if(!contactToCourseCodeBig.isEmpty()) {
            String reqType = 'Insert/Update';
            String source = 'Course';
            saveLogic(contactToCourseCodeBig,contactToCourseCodeLittle, reqType, source);
        }
    }
    
    public static void deletedCourseRoleHandler(Set<Id> deletedCRs) {
        
        map<Id,Map<Id,courseCodeWrapper>> contactToCourseCodeBig = createBigCCWMap(deletedCRs, 'cRole');        
        map<Id,courseCodeWrapper> contactToCourseCodeLittle = createLittleCCWMap(deletedCRs, 'cRole');
        
        if(!contactToCourseCodeBig.isEmpty()) {
            String reqType = 'Delete';
            String source = 'Course';
            saveLogic(contactToCourseCodeBig,contactToCourseCodeLittle, reqType, source);
        }
    }
    
    public static void updatedCourseRoleHandler() {
    }
    
    public static void evalItemHandler(Set<Id> evalItems) {
   
	    map<Id,Map<Id,courseCodeWrapper>> contactToCourseCodeBig = createBigCCWMap(evalItems, 'eval');
	    map<Id,courseCodeWrapper> contactToCourseCodeLittle = createLittleCCWMap(evalItems, 'eval');
	   
	    if(!contactToCourseCodeBig.isEmpty()) {
	    String reqType = 'Insert/Update';
	    String source = 'Eval';
	    saveLogic(contactToCourseCodeBig,contactToCourseCodeLittle, reqType, source);
	    }
    }
    
    public static void deletedEvalItemHandler(Set<Id> deletedEvalItems) {
   
	    map<Id,Map<Id,courseCodeWrapper>> contactToCourseCodeBig = createBigCCWMap(deletedEvalItems, 'eval');
	    map<Id,courseCodeWrapper> contactToCourseCodeLittle = createLittleCCWMap(deletedEvalItems, 'eval');
	   
	    if(!contactToCourseCodeBig.isEmpty()) {
		    String reqType = 'Delete';
		    String source = 'Eval';
		    saveLogic(contactToCourseCodeBig,contactToCourseCodeLittle, reqType, source);
	    }
   
    }
    
    public static void deletedEvalHandler(Set<Id> evals) {    
   
	    Set<Id> evalItems = new Set<Id>();
	   
	    for(Comp_Item__c ei : [select ID from Comp_Item__c where  Comp__c IN :evals]) {
	    	evalItems.add(ei.Id);
	    }	   
	   
	    map<Id,Map<Id,courseCodeWrapper>> contactToCourseCodeBig = createBigCCWMap(evalItems, 'eval');
	    map<Id,courseCodeWrapper> contactToCourseCodeLittle = createLittleCCWMap(evalItems, 'eval');
	   
	   
	    if(!contactToCourseCodeBig.isEmpty()) {
		    String reqType = 'Delete';
		    String source = 'Eval';
		    saveLogic(contactToCourseCodeBig,contactToCourseCodeLittle, reqType, source);
		}
   
    }    
    
    
    public static void saveLogic(Map<Id,Map<Id,courseCodeWrapper>> consCourseCodeMapBig, Map<Id,courseCodeWrapper> consCourseCodeMapLittle, String triggerType, String source) {
        
        List<Contact_Course_Code__c> ccdUpsert = new List<Contact_Course_Code__c>();
        List<Contact_Course_Code__c> ccdDelete = new List<Contact_Course_Code__c>();
        
        if(triggerType == 'Insert/Update') {
        	for(Contact Con : [Select Id, Course_Code_Count__c, (Select Id, Contact__c, Course_Code__c, CourseRoleCount__c, EvalCount__c from Contact_Course_Code__r) from Contact where Id IN :consCourseCodeMapBig.keySet()]) {
        		if(Con.Course_Code_Count__c == 0) {
        		   for(Id cccId : consCourseCodeMapBig.get(Con.Id).keySet()) { system.assert(false,'Youre here '+consCourseCodeMapBig.get(Con.Id).keySet());
	        			Contact_Course_Code__c newCCC = new Contact_Course_Code__c(
	        				Contact__c = Con.Id,
	        				Course_Code__c = cccId,
	        				CourseRoleCount__c = source == 'Course' ? consCourseCodeMapLittle.get(Con.Id).theCount : 0,
	        				EvalCount__c = source == 'Eval' ? consCourseCodeMapLittle.get(Con.Id).theCount : 0        				
	        			);
        			ccdUpsert.add(newCCC);
        			}
        		}else{
        			for(Contact_Course_Code__c existingCCC : Con.Contact_Course_Code__r) {
        				if(consCourseCodeMapBig.get(Con.Id).containsKey(existingCCC.Course_Code__c)) {
        					existingCCC.CourseRoleCount__c += source == 'Course' ? consCourseCodeMapBig.get(Con.Id).get(existingCCC.Course_Code__c).theCount : 0;
        					existingCCC.EvalCount__c += source == 'Eval' ? consCourseCodeMapBig.get(Con.Id).get(existingCCC.Course_Code__c).theCount : 0;
        					ccdUpsert.add(existingCCC);
        				}       				
        			}
        		}
        	}
        } if(null != ccdUpsert) { 
        	upsert ccdUpsert;
        }
            
       /*if(triggerType == 'Delete') {            
                        
            for(Contact con : [Select Id,Name, Course_Code_Count__c, (Select Id, Contact__c, Course_Code__c, CourseRoleCount__c, Current__c, Primary__c,Total_Association_Count__c,EvalCount__c from Contact_Course_Code__r) from Contact where Id IN: consCourseCodeMapBig.keySet()]) {                
                if(con.Course_Code_Count__c > 0) {
                    for(courseCodeWrapper cdWrap : consCourseCodeMap.get(con.Id) ) {                        
                        for(Contact_Course_Code__c existingCCC1 : Con.Contact_Course_Code__r) {
                            if(conCourseCodeMapBig.get(Con.Id).containsKey(existingCCC1.Course_Code__c)) {                                
                                if(existingCCC1.Total_Association_Count__c > 1) {
                                    existingCCC1.CourseRoleCount__c -= source == 'Course' ? conCourseCodeMapBig.get(Con.Id).get(existingCCC1.Course_Code__c).theCount : 0;  
                                    existingCCC1.EvalCount__c -= source == 'Eval' ? conCourseCodeMapBig.get(Con.Id).get(existingCCC1.Course_Code__c).the : 0;
                                    ccdUpsert.add(existingCCC1);
                                }else{
                                    ccdDelete.add(existingCCC1);
                                }
                            }
                        }
                    }
                    if(ccdUpdate.size() > 0) {
                    update conCourseCodesUpdate.values();
                    }
                    if(ccdDelete.size() > 0) {
                        delete conCourseCodesDel.values();
                    }
                }
            }
        } */   
    }
    
    //uses the triggered CourseRoles to map the Contact Id to CourseCodes...course codes in wrapper class.
    public static Map<Id,Map<Id,courseCodeWrapper>> createBigCCWMap(Set<Id> IDs, String source) {
        
        Map<Id,Map<Id,courseCodeWrapper>> bigMap = new Map<Id,Map<Id,courseCodeWrapper>>();
        Map<Id,courseCodeWrapper> littleMap = new Map<Id,courseCodeWrapper>();
        
        if(source == 'cRole') {                    
          
           for(aggregateResult cr1 : [select Contact__c, Course__r.ACE_Code_4__c,  Count(Id) from Course_Role__c where Id IN: IDs AND Course__r.ACE_Code_4__c != null AND Contact__c != null Group By Rollup (Contact__c,Course__r.ACE_Code_4__c)]) {                
               
               courseCodeWrapper wrappedCourse = new courseCodeWrapper(String.valueOf(cr1.get('Contact__c')),String.valueOf(cr1.get('ACE_Code_4__c')),Integer.valueOf(cr1.get('expr0')),source);
               littleMap.put(wrappedCourse.courseCodeId,wrappedCourse);
               bigMap.put(String.valueOf(cr1.get('Contact__c')),littleMap);
           }           
       
        }  else {
        	for(aggregateResult ei : [select Contact__c,Product__r.ACE_Code_4__c,  Count(Id) from Comp_Item__c where Id IN: Ids AND Product__r.ACE_Code_4__c != null and Contact__c != null Group By Rollup (Contact__c,Product__r.ACE_Code_4__c)]) {
        		
        		courseCodeWrapper wrappedEval = new courseCodeWrapper(String.valueOf(ei.get('Contact__c')), String.valueOf(ei.get('ACE_Code_4__c')), Integer.valueOf(ei.get('expr0')),source);
        		littleMap.put(wrappedEval.courseCodeId,wrappedEval);
        		bigMap.put(String.valueOf(ei.get('Contact__c')),littleMap);
        	} 
        }
		return bigMap; 
    }  
    
    public static Map<Id,courseCodeWrapper> createLittleCCWMap(Set<Id> IDs, String source) {
    	
    	Map<Id,courseCodeWrapper> littleMap = new Map<Id,courseCodeWrapper>();
        
        if(source == 'cRole') {                    
          
           for(aggregateResult cr1 : [select Contact__c, Course__r.ACE_Code_4__c,  Count(Id) from Course_Role__c where Id IN: IDs AND Course__r.ACE_Code_4__c != null AND Contact__c != null Group By Rollup (Contact__c,Course__r.ACE_Code_4__c)]) {                
               
               courseCodeWrapper wrappedCourse = new courseCodeWrapper(String.valueOf(cr1.get('Contact__c')),String.valueOf(cr1.get('ACE_Code_4__c')),Integer.valueOf(cr1.get('expr0')),source);
               littleMap.put(String.valueOf(cr1.get('Contact__c')),wrappedCourse);
           }           
       
        }  else {
        	for(aggregateResult ei : [select Contact__c,Product__r.ACE_Code_4__c,  Count(Id) from Comp_Item__c where Id IN: Ids AND Product__r.ACE_Code_4__c != null and Contact__c != null Group By Rollup (Contact__c,Product__r.ACE_Code_4__c)]) {
        		
        		courseCodeWrapper wrappedEval = new courseCodeWrapper(String.valueOf(ei.get('Contact__c')), String.valueOf(ei.get('ACE_Code_4__c')), Integer.valueOf(ei.get('expr0')),source);
        		littleMap.put(String.valueOf(ei.get('Contact__c')),wrappedEval);
        	} 
        }
		return littleMap; 
    }
    
    public class courseCodeWrapper {
        public Id contactId {get; set;}
        public Id courseCodeId {get; set;}        
        public Integer theCount {get; set;}
        public String source {get; set;}
        
        public courseCodeWrapper(Id conId, Id ccId, Integer elCount,  String wCDSource ) {
            contactId = conId; 
            courseCodeId = ccId;           
            theCount = elCount;
            source = wCDSource;
        }        
    } 
}

 

Best Answer chosen by Admin (Salesforce Developers) 
mcrosbymcrosby

I noticed you are using the "GROUP BY ROLLUP" clause.  I have not used this, but it may be giving you results that you didn't intend.

 

From  http://wiki.developerforce.com/page/A_Deeper_look_at_SOQL_and_Relationship_Queries_on_Force.com:

 

 GROUP BY ROLLUP Example

Let's look at a more advanced use of a grouping clause. There are several new constructs in SOQL in Spring '10 that allow you to return aggregated results. One example is a GROUP BY ROLLUP clause. This clause not only allows us to get aggregated results that are partitioned by the GROUP BY clause; it also provides a total rollup for all the groups that the evaluated by the query.

For example, the following query not only displays the total number of applications per Department but also provides a total number applications aggregated that are subject to query constraints.

SOQL:

SELECT Position__r.Department__c deptname, COUNT(id) total

FROM Job_Application__c

GROUP BY ROLLUP(Position__r.Department__c)

Results:

<AggregateResult>
null    |    2
Sales   |    2
null    |    4

Discussion: Although the query results appear to be similar to the GROUP BY example we used above, look at the last row of the results. The total number of all grouped applications is given in the result set as the values of the last row.

 I'm not sure of the best way to structure your SOQL to get the results you are looking for if you need to use the GROUP BY clause.  You mention that you're not familiar with aggregate queries--did you intend to use the ROLLUP condition?  If you remove "ROLLUP", does it give you the expected result?

All Answers

mcrosbymcrosby

From the documentation:

* A map key can hold the null value.

 

You should verify the SOQL query you're using to populate the map is returning the expected number of records.  It seems that one of the fields you are using for the key value is null.  You might want to add some System.debug lines and check the developer console to see what the values are for Contact__c and Course__r.ACE_Code_4__c in your AggregateResult query.  Even though it looks like the SOQL is set up to exclude records with these values set to null, you might want to verify that you are getting back the expected records/values.

akallioWileyakallioWiley

Hello

I finally got a chance to work on this code again. I took your advice and had a deeper look at the soql query, and I'm getting strange results, or at least they seem strange to me becuase I'm not familiar with aggregateResult queries.

 

So, here's the problem. The following query is returning 3 records when it should only return 1. 

 

List<aggregateResult> crtest = [select Contact__c, Course__r.ACE_Code_4__c,  Count(Id) from Course_Role__c where Id IN: IDs AND Course__r.ACE_Code_4__c != null AND Contact__c != null Group By Rollup (Contact__c,Course__r.ACE_Code_4__c)];
            system.assert(false, 'Results: '+crtest);

 When I feed that query one Course_Role__c Id it returns 3 records:

 

courseRoleTrigger: execution of AfterInsert caused by: System.AssertException: Assertion Failed: Results: (

AggregateResult:{expr0=1, ACE_Code_4__c=a0FZ0000001siMRMAY, Contact__c=003Z000000IVr6KIAT},
AggregateResult:{expr0=1, ACE_Code_4__c=null, Contact__c=003Z000000IVr6KIAT},
AggregateResult:{expr0=1, ACE_Code_4__c=null, Contact__c=null})

Class.contactToCourseCodeHandler.createBigCCWMap: line 139, column 1 Class.contactToCourseCodeHandler.newCourseRoleHandler: line 4, column 1 Trigger.courseRoleTrigger: line 20, column 1

 The first record in the list is exactly what I want. Then for some reason it gives me a second result with ACE_Code_4__c being null, and the Contact Id; and then a 3rd record with both ACE Code and Contact lookups null. 

 

Any ideas on why this happening?

 

 

mcrosbymcrosby

I noticed you are using the "GROUP BY ROLLUP" clause.  I have not used this, but it may be giving you results that you didn't intend.

 

From  http://wiki.developerforce.com/page/A_Deeper_look_at_SOQL_and_Relationship_Queries_on_Force.com:

 

 GROUP BY ROLLUP Example

Let's look at a more advanced use of a grouping clause. There are several new constructs in SOQL in Spring '10 that allow you to return aggregated results. One example is a GROUP BY ROLLUP clause. This clause not only allows us to get aggregated results that are partitioned by the GROUP BY clause; it also provides a total rollup for all the groups that the evaluated by the query.

For example, the following query not only displays the total number of applications per Department but also provides a total number applications aggregated that are subject to query constraints.

SOQL:

SELECT Position__r.Department__c deptname, COUNT(id) total

FROM Job_Application__c

GROUP BY ROLLUP(Position__r.Department__c)

Results:

<AggregateResult>
null    |    2
Sales   |    2
null    |    4

Discussion: Although the query results appear to be similar to the GROUP BY example we used above, look at the last row of the results. The total number of all grouped applications is given in the result set as the values of the last row.

 I'm not sure of the best way to structure your SOQL to get the results you are looking for if you need to use the GROUP BY clause.  You mention that you're not familiar with aggregate queries--did you intend to use the ROLLUP condition?  If you remove "ROLLUP", does it give you the expected result?

This was selected as the best answer
akallioWileyakallioWiley
Thanks for posting the article. I'm not sure what I read that made me think that Roll UP was necessary. I will experiment with just Group By, and will post results here.
akallioWileyakallioWiley

I think I've worked this out. Although I don't really get why the aggregateResults includes results for Null values, my solution was to simply exculde those with Null from the maps that I pass to other methods, and it seems to be working. Thanks for the help! Also, I quickly experimented with not using RollUp clause, but I think it's necessary because without it I can't groupby multiple fields...true?

mcrosbymcrosby

If you are simply grouping by the necessary fields, I don't think ROLL UP is required unless you need the counts for each field grouping.  Either way, congrats on getting it to work--excluding the null values in the map is a way to go.