Are you an LLM? You can read better optimized documentation at /after-insert/add-ons/related-query.md for this page in Markdown format
AfterInsert.RelatedQuery
Queries other records once per chunk, such as the other contacts of the same account, without SOQL in your loop. Read the rows per record with record.getRelated('<provider name>').
Interface
apex
public class AfterInsert {
public interface RelatedQuery {
Map<String, AfterInsert.RecordsProvider> queryRelatedOnAfterInsert();
}
}queryRelatedOnAfterInsert(): called once per chunk, at this handler’s turn, before its first predicate. Returns provider name →RecordsProvider.
RecordsProvider
apex
public class AfterInsert {
public interface RecordsProvider {
List<SObject> query(TriggerHandler.InsertRecords records);
String keyOf(SObject record);
}
}query(records): called once per provider, at the handler’s turn, with every record in the chunk. Returns the rows to index by key;nullcounts as no rows.keyOf(record): called once per row thatqueryreturned. Returns the key thatgetFirstWhereKeyEqualsandgetAllWhereKeyEqualsmatch;nullleaves the row out of the index.
Example
apex
public with sharing class ContactWriter implements AfterInsert.Writer, AfterInsert.RelatedQuery {
public Map<String, AfterInsert.RecordsProvider> queryRelatedOnAfterInsert() {
return new Map<String, AfterInsert.RecordsProvider>{ 'accountContacts' => new AccountContactsProvider() };
}
public Boolean writeOnAfterInsertWhen(TriggerHandler.InsertRecord record) {
return record.isNotNull(Contact.AccountId);
}
public void writeOnAfterInsert(TriggerHandler.InsertRecord record, TriggerHandler.UnitOfWork unitOfWork) {
Id accountId = ((Contact) record.getNewSObject()).AccountId;
Integer contactCount = record.getRelated('accountContacts').getAllWhereKeyEquals(accountId).size();
unitOfWork.toInsert(new Task(WhatId = accountId, Subject = 'Contacts on the account: ' + contactCount));
}
private with sharing class AccountContactsProvider implements AfterInsert.RecordsProvider {
public List<SObject> query(TriggerHandler.InsertRecords records) {
return [SELECT Id, AccountId FROM Contact WHERE AccountId IN :records.getIdsOf(Contact.AccountId)];
}
public String keyOf(SObject record) {
return ((Contact) record).AccountId;
}
}
}Good to Know
- SOQL sees the new records. They are saved but not committed. Add
Id NOT IN :records.getIds()to leave them out. - Providers run before any predicate. They query even when no record qualifies. Return an empty list from
querywhen no record can qualify. - Keys are case-sensitive. Normalize text keys the same way in
keyOfand when you read. - Unknown names throw.
getRelatedwith a name the handler did not return throwsTriggerHandler.TriggerHandlerException, even with ContinueOnError. - Set sharing on the provider. Its query runs under its own class's sharing keyword. An inner class does not take its outer class's keyword.
