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
salesforcecrapsalesforcecrap 

Retrieving all contacts within Account heirarchy

Hi fellow hackers, I'm wondering if any of you SOQL jocks can help steer me in the right direction.

 

I have a function that basically:

  • creates a list of all Contracts
  • for each Contract's Account, create a list of Contacts from that Account and all child Accounts.

Given that I have an Account object, let's call it root,  I basically execute some simple recursion to obtain a List of all Account objects in the heirarchy below root.

 

This works great. But then I run into an issue when trying to grab all the Contacts for all of those Accounts. I thought that I would iterate over each account and query for its Contacts, but that exceeds my SOQL query limit. Then I realized that instead of a SOQL query for the Contact objects, I could just access each Account object's Contacts (eg, account.Contacts). However, I then get a different Exception:

 

System.SObjectException: SObject row was retrieved via SOQL without querying the requested field: Account.Contacts

 

Which I understand. My question is, can I create a SOQL query that allows me to access all the Contacts of the Account associated with Contracts?

 

Here is my code. I'll point out some highlights below.

 

/**
   Find all enterprise contracts
   For each contract, instantiate the account
   Find the account's child accounts
   Create a list of all contacts from the main and child accounts
   Set each contact's "Subscription" attribute to the contract "product" attribute, if the contract is active (current date is within contract start/end dates) and the contact's email domain matches the contract "domains" attribute.
 */
private void updateEnterpriseContactSubscriptions() {
  List<Contract> enterpriseContracts = [SELECT Id,startDate,endDate,product__c,subscription_type__c,enterprise_email_extensions__c, Account.Id, Account.parentId FROM Contract];
  List<Contact> allSubscribersToUpdate = new List<Contact>();
  for (Contract contract : enterpriseContracts) {
    if (contract.startDate <= Date.today() && contract.endDate >= Date.today()) {
      
      List<Account> accounts = accountListFromHeirarchy(contract.Account);
      List<Contact> subscribers = new List<Contact>();
      for (Account account : accounts) {
        // The following line is what I want, but exceeds my SOQL limit.
// List<Contact> contacts = [SELECT Id,email,subscriptions__c FROM Contact WHERE accountID=:account.Id]; List<Contact> contacts = account.Contacts; List<Contact> contactsWithValidEmail = new List<Contact>(); for (Contact c : contacts) { if (contractEmailDomainsIncludeContactEmail(contract.enterprise_email_extensions__c, c.email)) { contactsWithValidEmail.add(c); } } subscribers.addAll(contactsWithValidEmail); } for (Contact subscriber : subscribers) { subscriber.subscriptions__c = appendEditionName(subscriber.subscriptions__c, contract.product__c); } allSubscribersToUpdate.addAll(subscribers); } } update allSubscribersToUpdate; }

 
And here is my recursive accountListFromHeirarchy function:

 

public List<Account> accountListFromHeirarchy(Account root) {
  List<Account> accounts = new List<Account>();
  accounts.add(root);
  List<Account> children = [SELECT Id FROM Account WHERE parentId = :root.Id];
  for (Account child : children) {
    accounts.addAll(accountListFromHeirarchy(child));
  }
  return accounts;
}

 

In my updateEnterpriseContactSubscriptions function, the line

 

List<Contact> contacts = account.Contacts;

 

is what raises the exception I mentioned above. And just above that line, I've commented out what I inteded to do originally, but it's that loop and query that busts my SOQL limit.


Is there a way to modify the query that generates enterpriseContracts (the first line of the first function shown above) such that I would be able to access each Account object's Contacts?

 

Thanks for your time.

salesforcecrapsalesforcecrap

Despite my approach to the problem above, I'm trying to obtain the Contacts for a specific Account plus the Contacts for that Account's child Accounts, and the Contacts for each of those child Account's children, and so on.

 

Given that Accounts and their children are a tree, I'm trying to get a list of all the Contacts for every Account in the tree.

 

 

salesforcecrapsalesforcecrap

I kind of want a query like this:

 

SELECT Id,startDate,endDate,product__c,subscription_type__c,enterprise_email_extensions__c, Account.Id, Account.parentId, (SELECT Contact.Id FROM Contract.Account.Contacts ) FROM Contract

 

Which is invalid.

 

Starz26Starz26

You cannot use SOQL to transver from child->Parent->child. You are also limited to traversing 5 levels child -> Parent1 -> Parent2->etc

 

in order to do this you would have to query all the additional child object records from the parent matching the parent ID, then itenerate through the list.

salesforcecrapsalesforcecrap

I just wanted to follow up. I revised my recursive function to execute queries using IN and that  helped get my query counts down. In addition, the function returns a list of Ids, which helps make the caller, updateEnterpriseContactSubscription(),  more efficient too.

 

My approach doesn't scale, as the current working implementation generates 90 queries. My long term solution is to implement this thing as a Batch Apex thing, so I've got to educate myself about that.