Apex Basics & Database
These are technical notes I compiled while studying using Trailhead, Salesforce's free self-learning portal.
Get Started with Apex
Describe the key features of the Apex programming language. Save an Apex class and call methods with Anonymous.Apex. Use the Developer Console to inspect debug logs.- Resources from Trailhead:
- Get Started with Apex
- Apex enables developers to add business logic to system events, like button clicks, updates of related records, and Visualforce pages.
- Apex is Java-like and acts like database stored procedures. Other characteristics:
- Hosted: saved, compiled, executed on Lightning Platform servers
- Object oriented: supports classes, interfaces, and inheritance
- Strongly typed: validates references to objects at compile time
- Multitenant aware: guards closely against runaway code by enforcing limits, preventing code from monopolizing shared resources
- Integrated with the database: straightforward to access and manipulate records. Provides direct access to records and fields, and gives statements and query languages to manipulate those records.
- Data focused: provides transactional access to the database, which allows you to roll back operations
- Easy to use: based on familiar Java idioms
- Easy to test: provides build-in support for unit test creation, execution, and code coverage. Ensures that all custom Apex code works as expected by executing all unit tests before platform upgrades.
- Versioned: custom Apex code can be saved against different versions of the API
- Case-insensitive
- Similar to other object-oriented programming languages by supporting:
- Classes, interfaces, properties, collections (arrays)
- Object and array notation
- Expressions, variables, constants
- If-then-else (conditional statements)
- For loops, while loops (control flow statements)
- Different from other object-oriented programming languages by supporting:
- Stored, compiled, executed in the cloud (Cloud development)
- Triggers, similar to triggers in database systems
- Database statements that allow you to make direct database calls
- Transactions and rollbacks
global
access modifier is more permissive thanpublic
, allowing access across namespaces/applications- Versioning of custom code
- Apex supports several data types:
- Primitives: Integer, Double, Long, Date, Datetime, String, ID, Boolean
- sObjects: both generic and specific, such as Account, Contact, or MyCustomObject__c
- Specific to Salesforce
- Collections:
- List (or array) of primitives, sObjects, user defined objects, objects created from Apex classes, collections
- Set of primitives
- Map from a primitive to a primitive, sObject, or collection
- Enum: typed list of values
- Apex Classes:
- User-Defined
- System-Supplied
- Apex Collections: List (Apex also supports Set and Map collections. Reference the collections of Apex Developer Guide.)
- Lists are synonymous with arrays and can be used interchangeably
- Array:
List<String> colors = new List<String>();
- List:
String[] colors = new List<String>();
- Its easier to create a list than an array most of the time, because lists don’t require specifying the length ahead of time.
- Array:
- Add elements to a list during creation
List<String> colors = new List<String> { 'red', 'green', 'blue' };
- Or: Add elements afterwards by calling the
add()
methodList<String> moreColors = new List<String>();
moreColors.add('orange');
- List elements can be read by specifying an index between square brackets, just like with array elements:
String color2 = moreColors[0];
- Can also use
get()
to read a list element:String color1 = moreColors.get(0);
- Example of iterating over array elements:
- Lists are synonymous with arrays and can be used interchangeably
for(Integer i=0;i<colors.size();i++) {
// Write value to the debug log
System.debug(colors[i]);
}
- Apex Classes
- Benefit of Apex classes is code reuse. Class methods can be called by triggers and other classes.
- Save an Apex Class to your org via: Setup > Developer Console > File | New | Apex Class
- Example class from Trailhead, below
- Class below makes use of object-oriented programming. It encapsulates the methods related to managing email.
public class EmailManager {
// Public method
public void sendMail(String address, String subject, String body) {
// Create an email message object
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
String[] toAddresses = new String[] {address};
mail.setToAddresses(toAddresses);
mail.setSubject(subject);
mail.setPlainTextBody(body);
// Pass this email message to the built-in sendEmail method
// of the Messaging class
Messaging.SendEmailResult[] results = Messaging.sendEmail(
new Messaging.SingleEmailMessage[] { mail });
// Call a helper method to inspect the returned results
inspectResults(results);
}
// Helper method
private static Boolean inspectResults(Messaging.SendEmailResult[] results) {
Boolean sendResult = true;
// sendEmail returns an array of result objects.
// Iterate through the list to inspect results.
// In this class, the methods send only one email,
// so we should have only one result.
for (Messaging.SendEmailResult res : results) {
if (res.isSuccess()) {
System.debug('Email sent successfully');
}
else {
sendResult = false;
System.debug('The following errors occurred: ' + res.getErrors());
}
}
return sendResult;
}
}
- A perfect example of OOP would also contain member variables (attributes) and accessor methods to access them - for simplicity these aren’t included above.
Call a Method
- Anonymous Apex allows you to run lines of code on the fly - handy way to invoke Apex and test functionality.
- Can also invoke Apex through triggers, for example.
- Execute anonymous apex via: Developer Console > Debug | Open Execute Anonymous Window
EmailManager em = new EmailManager();
em.sendMail('Your email address', 'Trailhead Tutorial', '123 body');
Inspect Debug Logs
- Debug logs are useful for debugging your code. When Apex methods execute, the calls are logged in the debug log.
- The
inspectResults()
helper method in the script above is called bysendMail()
to write messages to the log using theSystem.debug()
method to indicate whether the email send operation was successful. - Select the “Debug Only” checkbox to filter the log such that only log lines for
System.debug()
statements are shown.- A line with
DEBUG|Email sent successfully
- A line with
- The
Call a Static Method
- Since the
sendMail()
method in our class doesn’t access class member variables, it can be a static (not an instance) method. - Convert to a static method by adding the word
static
to the method declaration. - Then, change the method call from the following:
EmailManager em = new EmailManager();
em.sendMail('Your email address', 'Trailhead Tutorial', '123 body');
- To the following:
EmailManager.sendMail('Your email address', 'Trailhead Tutorial', '123 body');
Example Apex Class
- Example Apex class with a method that returns an array (or list) of strings:
public class StringArrayTest {
public static List<String> generateStringArray (Integer numStrings) {
List<String> stringList = new List<String>();
for (Integer i=0; i<numStrings; i++) {
stringList.add('Test ' + i);
}
return stringList;
}
}
- Execute Anonymous code to call the class above
StringArrayTest.generateStringArray(3);
Use sObjects
Describe the relationship between sObjects and Salesforce records. Create and use specific sObject variables. Cast a generic sObject to a specific sObject.- Apex is tightly integrated with the database, so you can access Salesforce records and fields directly from Apex.
- Each record in Salesforce is represented as an sObject in Salesforce.
- Account sObject is an abstraction of the Acme account example record. It holds the account field info in memory as an object.
- Each record is represented as an sObject before it is inserted into/retrieved from Salesforce.
- To create an sObject, you need to declare a variable and assign it to an sObject instance.
- Names of sObjects correspond to API names of corresponding standard or custom objects.
- Names for sObject fields correspond to the API names of corresponding fields.
- Custom objects and fields have API names that end with
__c
suffix. - Custom relationship fields have API names that end with
__r
suffix.
- Custom objects and fields have API names that end with
- Before you can insert a Salesforce record, you must create it in memory first as an sObject. These are created with the
new
operator.- Populate fields by passing them in through the construtor or assigning them after the fact using dot notation:
Account acct = new Account();
Account acct = new Account(Name='Acme', Phone='(415)555-1212', NumberOfEmployees=100);
Account acct = new Account();
acct.Name = 'Acme';
acct.Phone = '(415)555-1212';
acct.NumberOfEmployees = 100;
Working with the Generic sObject Data Type
- If you don’t know the specific sObject data type, such as Account, you can use the generic sObject data type.
- Variables that are declared with the generic sObject data type can reference any Salesforce record.
sObject sobj1 = new Account(Name='Trailhead');
sObject sobj2 = new Book__c(Name='Workbook 1');
- sObject is a parent type for all specific sObjects.
- So, you can “cast” your sObject variable to a specific sObject type.
- Benefits of doing this is being able to access fields using dot notation, which isn’t available on the generic sObject.
// Cast a generic sObject to an Account
Account acct = (Account)myGenericSObject;
// Now, you can use the dot notation to access fields on Account
String name = acct.Name;
String phone = acct.Phone;
- Generic sObjects can be created only through the
newSOjbect()
method. - Fields of a generic sObject can only be access through
put()
andget()
methods. - Creating an sObject doesn’t persist it as a record in the database.
- To save an sObject as a record, we use Data Manipulation Language (DML)
- To retrieve a record, use Salesforce Object Query Language (SOQL)
- Relationship between sObjects and Salesforce records:
- Every record in Salesforce is natively represented as an sObject in Apex.
- You can obtain an instance of an sObject by:
- Either creating the sObject or by retrieving a persistent record from Salesforce using SOQL.
- A generic sObject variable can be assigned to any specific sObject, standard or custom, like Account or Book__c.
Manipulate Records with DML
Use DML to insert, update, and delete records. Perform DML statements in bulk. Use upsert to either insert or update a record. Catch a DML Exception. Use a Database method to insert new records with the partial success option and process the results. Know when to use DML statements and when to use Database methods. Perform DML operations on related records.Manipulate Records with DML
- Data Manipulation Language (DML) provides a straightforward way to manage records, including simple statements to
insert
,update
,upsert
,delete
,undelete
, andmerge
records.- Since Apex is a data-focused language it has access to your data in Salesforce - no additional setup is needed to connect data sources, unlike other languages.
- Each DML statement accepts either a single sObject or a list (or array) of sObjects.
upsert
DML operation creates new records and updates existing sObject records within a single statement.- It uses a specific field to determine the presence of existing objects, or the ID if not otherwise specified.
merge
DML statement merges up to three records of the same sObject into one of the records, deleting the others, and re-parenting any related records.
// Create the account sObject
Account acct = new Account(Name='Acme', Phone='(415)555-1212', NumberOfEmployees=100);
// Insert the account by using DML
insert acct;
- ID field Auto-Assigned to new records
- When inserting records, the system assigns an ID for each record and autopopulates it on the sObject variable you used as an argument in the DML call.
- Since the sObject variable contains the ID after the DML call, you can reuse this sObject variable to perform further DML operations, like updates.
- Also possible to retrieve a record to obtain its fields, but this can’t be done with DML - need to write a query using SOQL for this.
- Run the following in Developer Console > Open Execute Anonymous Window:
Account acct = new Account(Name='Acme', Phone='(415)555-1212', NumberOfEmployees=100);
insert acct;
ID acctID = acct.Id; // Get the new ID on the inserted sObject argument
System.debug('ID = ' + acctID);
Bulk DML
- You can perform DML operations either on a single sObject or in bulk on a list of sObjects.
- Bulk DML oeprations aare recommended as they help avoid hitting governor limits - Ex: DML limit of 150 statements per Apex transaction. This limit is in place to ensure fair access to shared resources on the Lightning Platform.
- Performing a DML operation on a list of sObjects counts as one DML statement, not as one stataement for each object.
- Example of bulkified Inserts and Updates:
// Create a list of contacts
List<Contact> conList = new List<Contact> {
new Contact(FirstName='Joe',LastName='Smith',Department='Finance'),
new Contact(FirstName='Kathy',LastName='Smith',Department='Technology'),
new Contact(FirstName='Caroline',LastName='Roth',Department='Finance'),
new Contact(FirstName='Kim',LastName='Shain',Department='Education')};
// Bulk insert all contacts with one DML call
insert conList;
// List to hold the new contacts to update
List<Contact> listToUpdate = new List<Contact>();
// Iterate through the list and add a title only
// if the department is Finance
for(Contact con : conList) {
if (con.Department == 'Finance') {
con.Title = 'Financial analyst';
// Add updated contact sObject to the list.
listToUpdate.add(con);
}
}
// Bulk update all contacts with one DML call
update listToUpdate;
Upserting Records
- If you have a list containing a mix of new and existing records, you can process insertions and updates at the same time using
upsert
.- Upsert matches the sObjects with existing records by comparing values of one field. If you don’t specify the field, the upsert uses the sObject’s ID to match on.
- Alternatively, can specify a field to use for matching.
- Custom objects: can specify a custom field marked as external ID.
- Standard objects: can specify any field that has idLookup set to true (Ex: Email field of Contact or User).
- Upsert Syntax:
upsert sObject | sObject[]
orupsert sObject | sObject[] field
upsert sObjectList Account.Fields.MyExternalId;
// Insert the Josh contact
Contact josh = new Contact(FirstName='Josh',LastName='Kaplan',Department='Finance');
insert josh;
// Josh's record has been inserted
// so the variable josh has now an ID
// which will be used to match the records by upsert
josh.Description = 'Josh\'s record has been updated by the upsert operation.';
// Create the Kathy contact, but don't persist it in the database
Contact kathy = new Contact(FirstName='Kathy',LastName='Brown',Department='Technology');
// List to hold the new contacts to upsert
List<Contact> contacts = new List<Contact> { josh, kathy };
// Call upsert
upsert contacts;
// Result: Josh is updated and Kathy is created.
Contact jane = new Contact(FirstName='Jane',
LastName='Smith',
Email='[email protected]',
Description='Contact of the day');
insert jane;
// 1. Upsert using an idLookup field
// Create a second sObject variable.
// This variable doesn’t have any ID set.
Contact jane2 = new Contact(FirstName='Jane',
LastName='Smith',
Email='[email protected]',
Description='Prefers to be contacted by email.');
// Upsert the contact by using the idLookup field for matching.
upsert jane2 Contact.fields.Email;
// Verify that the contact has been updated
System.assertEquals('Prefers to be contacted by email.',
[SELECT Description FROM Contact WHERE Id=:jane.Id].Description);
Deleting Records
- Deleted records are placed in the Recycle Bin for 15 days, from which they can be restored.
- Executing the following snippet in Developer Console using Anonymous Apex will remove all contacts with last name “Smith”
- Snippet below includes a query to retrieve the contacts (SOQL)
Contact[] contactsDel = [SELECT Id FROM Contact WHERE LastName='Smith'];
delete contactsDel;
DML Statement Exceptions
- If a DML operation fails, it returns an exception of type
DmlException
.- You can catch exceptions in your code to handle error conditions.
- Example below produces a
DmlException
because it attempts to insert an account without the requiredName
field. The exception is caught in the catch block.
try {
Account acct = new Account(); // Causes exception - the required Name field is not provided.
insert acct; // Insert the account
} catch (DmlException e) {
System.debug('A DML exception has occurred: ' +
e.getMessage());
}
Database Methods
- Apex contains the built-in Database class, which provides methods that perform DML operations. The methods are static and called on the class name.
Database.insert()
Database.update()
Database.upsert()
Database.delete()
Database.undelete()
Database.merge()
- These methods have an optional
allOrNone
parameter that lets you specify whether the operation should partially succeed.- If errors occur on a partial set of records, successful records will be committed and errors will be returned for the failed records.
- Ex:
Database.insert(recordList, false);
- Return result includes success or failure information for each record.
- Ex:
Database.SaveResult[] results = Database.insert(recordList, false);
- Upsert returns
Database.UpsertResult
objects, delete returnsDatabase.DeleteResult
objects
- Upsert returns
- Ex:
- By default,
allOrNone
parameter is true - As an example, running the following example code in the Execute Anonymous window produces the result in the image that follows it.
- DML Statements or Database Methods?
- Use DML statements if you want any error that occurs during bulk DML processing to be thrown as an Apex exception that immediately interrupts control flow (
try...catch
blocks).- Similar to how exceptions are handled in most database procedural languages.
- Use Database class methods if you want to allow partial success of a bulk DML operation.
- Application can then inspect the rejected records and possibly retry the operation.
- When using this form, you can write code that never throws DML exception errors. Instead the code can use the results arrays to judge success or failure and iterate.
- Use DML statements if you want any error that occurs during bulk DML processing to be thrown as an Apex exception that immediately interrupts control flow (
Working with Related Records
- Inserting Related Records
- Can insert records related to existing records if a relationship has already been defined between the two objects, such as a lookup or master-detail relationship.
- A record is associated with a related record through a foreign key ID.
- Ex: inserting a new contact, you can specify the contact’s related account record by setting the value of
AccountId
field.
- Example code showing how to add a contact to an account, below. A new account (SFDC Account) was created and has the Mario Ruiz contact in the account’s Contacts related list.
- Can insert records related to existing records if a relationship has already been defined between the two objects, such as a lookup or master-detail relationship.
Account acct = new Account(Name='SFDC Account');
insert acct;
// Once the account is inserted, the sObject will be
// populated with an ID.
// Get this ID.
ID acctID = acct.ID;
// Add a contact to this account.
Contact mario = new Contact(
FirstName='Mario',
LastName='Ruiz',
Phone='415.555.1212',
AccountId=acctID);
insert mario;
- Updating Related Records
- Fields on a related record can’t be updated with the same call to the DML operation. They require a separate DML call.
- Example code below updates a contact and its related account using two subsequent
update
statements.
// Query for the contact, which has been associated with an account.
Contact queriedContact = [SELECT Account.Name
FROM Contact
WHERE FirstName = 'Mario' AND LastName='Ruiz'
LIMIT 1];
queriedContact.Phone = '(415)555-1213'; // Update the contact's phone number
queriedContact.Account.Industry = 'Technology'; // Update the related account industry
// Make two separate calls
// 1. This call is to update the contact's phone.
update queriedContact;
// 2. This call is to update the related account's Industry field.
update queriedContact.Account;
- Deleting Related Records
delete
supports cascading deletions. If you delete a parent object, you delete its children automatically, as long as each child record can be deleted.
- Ex: deleting the SFDC Account created with snippet above also deletes its related contact.
Account[] queriedAccounts = [SELECT Id FROM Account WHERE Name='SFDC Account'];
delete queriedAccounts;
- DML operations execute within a transaction.
- All DML operations in a transaction either complete successfully, or if an error occurs in one operation, the entire transaction is rolled back and no data is committed.
- Ex: if a trigger/class creates two accounts and updates one contact, and the contact update fails because of a validation rule failure, the entire transaction rolls back. none of the accounts are persisted in Salesforce.
- Resources:
Example: Create a method for inserting accounts named after an incoming parameter.
public class AccountHandler {
public static Account insertNewAccount (String name) {
Account acct = new Account(Name=name);
try {
insert acct;
} catch (DmlException e) {
return Null;
}
return acct;
}
}
To test the new method:
System.debug('ID = ' + AccountHandler.insertNewAccount('Test').ID);
The debug log shows:
Write SOQL Queries
Write SOQL queries in Apex. Execute SOQL queries by using the Query Editor in the Developer Console. Execute SOQL queries embedded in Apex by using Anonymous Apex. Query related records.- To read a record from Salesforce, you query the platform using SOQL, or “Salesforce Object Query Language." SOQL is similar to standard SQL but is customized for the Lightning Platform.
- Apex has direct access to Salesforce records that are stored in the database, so you can embed SOQL queries in your Apex code. Embedded SOQL is referred to as inline SOQL.
- To include SOQL queries within your Apex, wrap the SOQL statement within square brackets and assign the return value to an array of sObjects. Example:
Account[] accts = [SELECT Name,Phone FROM Account];
- Execute the following in a trailhead org to create some sample data.
Account acct = new Account(
Name='SFDC Computing',
Phone='(415)555-1212',
NumberOfEmployees=50,
BillingCity='San Francisco');
insert acct;
ID acctID = acct.ID;
// Add a contact to this account.
Contact con = new Contact(
FirstName='Carol',
LastName='Ruiz',
Phone='(415)555-1212',
Department='Wingo',
AccountId=acctID);
insert con;
// Add account with no contact
Account acct2 = new Account(
Name='The SFDC Query Man',
Phone='(310)555-1213',
NumberOfEmployees=50,
BillingCity='Los Angeles',
Description='Expert in wing technologies.');
insert acct2;
Query Editor
- The Query Editor is provided in the Developer Console. This enables you to run SOQL queries and view results. Reference the Query Editor tab.
Basic SOQL Syntax
- The basic syntax of an SOQL query:
SELECT fields FROM ObjectName [WHERE Condition]
- Ex:
SELECT Name,Phone FROM Account
SELECT Name,Phone
- lists fields to retrieve.FROM Account
- specifies the standard or custom object you want to retrieve.
- Unlike other SQL languages, in SOQL you can’t specify
*
for all fields. They must be specified explicitly.
- Ex:
Filter Query Results with Conditions
- The
WHERE
clause retrieves only the records that match specific criteria.- Ex:
SELECT Name,Phone FROM Account WHERE Name='SFDC Computing'
- Ex:
SELECT Name,Phone FROM Account WHERE (Name='SFDC Computing' AND NumberOfEmployees>25)
- Ex:
SELECT Name,Phone FROM Account WHERE (Name='SFDC Computing' OR (NumberOfEmployees>25 AND BillingCity='Los Angeles'))
- Ex:
- Can perform fuzzy matches using the
LIKE
operator.- Ex: can retrieve all accounts whose names start with SFDC by using
WHERE Name LIKE SFDC%
- The
%
wildcard character matches any or no character - The
_
character matches just one character
- The
- Ex: can retrieve all accounts whose names start with SFDC by using
Ordering Query Results
- An
ORDER BY
clause lets you order results based on most types of fields, including numeric and text fields. ASC
andDESC
let you control the ordering as ascending or descending.- Ex:
SELECT Name,Phone FROM Account ORDER BY Name
- Ex:
SELECT Name,Phone FROM Account ORDER BY Name ASC
- Ex:
SELECT Name,Phone FROM Account ORDER BY Name DESC
- Ex:
Limiting the Number of Records Returned
- Use
LIMIT n
to reduce the size of the return set:Account oneAccountOnly = [SELECT Name,Phone FROM Account LIMIT 1];
Examples
SELECT Name,Phone FROM Account
WHERE (Name = 'SFDC Computing' AND NumberOfEmployees>25)
ORDER BY Name
LIMIT 10
Account[] accts = [SELECT Name,Phone FROM Account
WHERE (Name='SFDC Computing' AND NumberOfEmployees>25)
ORDER BY Name
LIMIT 10];
System.debug(accts.size() + ' account(s) returned.');
// Write all account array info
System.debug(accts);
Accessing Varaiables in SOQL Queries
- SOQL statements can reference Apex code variables and expressions.
- Precede them by a colon (
:
), called a bind.
- Precede them by a colon (
String targetDepartment = 'Wingo';
Contact[] techContacts = [SELECT FirstName,LastName
FROM Contact WHERE Department=:targetDepartment];
Querying Related Records
- Records in Salesforce are linked through relationships (lookup, master-detail).
- To get child records related to a parent record, add an inner query for the child records. The
FROM
clause runs against the relationship name, no the Salesforce object name. - Ex:
SELECT Name, (SELECT LastName FROM Contacts) FROM Account WHERE Name = 'SFDC Computing'
- To get child records related to a parent record, add an inner query for the child records. The
Account[] acctsWithContacts = [SELECT Name, (SELECT FirstName,LastName FROM Contacts)
FROM Account
WHERE Name = 'SFDC Computing'];
// Get child records
Contact[] cts = acctsWithContacts[0].Contacts;
System.debug('Name of first associated contact: '
+ cts[0].FirstName + ', ' + cts[0].LastName);
- We can traverse a relationship from a child object (contact) to a field on its parent (Account.Name) by using dot notation.
Contact[] cts = [SELECT Account.Name FROM Contact
WHERE FirstName = 'Carol' AND LastName='Ruiz'];
Contact carol = cts[0];
String acctName = carol.Account.Name;
System.debug('Carol\'s account name is ' + acctName);
Querying in Batches by using SOQL For Loops
- SOQL
for
loops let you include a SOQL query within afor
loop. The results if a SOQL query can be iterated over within the loop.- These loops use a different method for retrieving records: efficient chunking with calls to the
query
andqueryMore
methods of the SOAP API.- This allows you to avoid hitting the heap size limit.
- These loops use a different method for retrieving records: efficient chunking with calls to the
for (variable : [soql_query]) {
code_block
}
OR
for (variable_list : [soql_query]) {
code_block
}
- Both
variable
andvariable_list
must be of the same type as thesObjects
that are returned by thesoql_query
.- Preferable to use the sObject list format of the SOQL for loop as the loop executes once for each batch of 200 sObjects.
insert new Account[]{new Account(Name = 'for loop 1'),
new Account(Name = 'for loop 2'),
new Account(Name = 'for loop 3')};
// The sObject list format executes the for loop once per returned batch
// of records
Integer i=0;
Integer j=0;
for (Account[] tmp : [SELECT Id FROM Account WHERE Name LIKE 'for loop _']) {
j = tmp.size();
i++;
}
System.assertEquals(3, j); // The list should have contained the three accounts
// named 'yyy'
System.assertEquals(1, i); // Since a single batch can hold up to 200 records and,
// only three records should have been returned, the
// loop should have executed only once
Example Apex class that returns contacts based on query parameters
public class ContactSearch {
public static List<Contact> searchForContacts(String lastname, String postalCode) {
Contact[] cts = [SELECT Contact.Id, Contact.Name FROM Contact
WHERE LastName = :lastname AND MailingPostalCode = :postalCode];
return cts;
}
}
- Resource: SOQL and SOSL Reference
Write SOSL Queries
Describe the differences between SOSL and SOQL. Search for fields across multiple objects using SOSL queries. Execute SOSL queries by using the Query Editor in the Developer Console.- SOSL (Salesforce Object Search Language) is a Salesforce search language that performs text searches in records.
- Searches across multiple standard and custom object records.
- Similar to “Apache Lucene”
- Adding SOSL to Apex is simple - you can embed SOSL queries directly in Apex. This is called Inline SOSL. Example:
List<List<SObject>> searchList = [FIND 'SFDC' IN ALL FIELDS
RETURNING Account(Name), Contact(FirstName,LastName)];
- SOQL and SOSL are similar:
- Both allow you to search your organization’s records for info
- SOQL and SOSL are different:
- SOQL searches one object at a time, SOSL searches all objects
- SOQL performs an exact match by default, SOSL matches fields based on word match
- SOSL: ‘Digital’ returns ‘The Digital Company’
- SOQL: ‘Digital’ returns ‘Digital’
- Query Editor tab in the Developer console allows you to run SOSL queries and view results.
- Results are grouped in tabs for each object
FIND {Wingo} IN ALL FIELDS RETURNING Account(Name), Contact(FirstName,LastName,Department)
- Note:
- Search query in the Query Editor and the API must be enclosed within curly brackets
{SFDC}
- Search query in Apex must be enclosed within single quotes
'Wingo'
- Search query in the Query Editor and the API must be enclosed within curly brackets
SOSL Syntax
- SOSL allows you to specify specific search criteria:
- Text expression (single word or phrase) to find
- Scope of fields to search
- List of objects and fields to retrieve
- Conditions for selecting rows in source objects
FIND 'SearchQuery' [IN SearchGroup] [RETURNING ObjectsAndFields]
- SearchQuery: text to search for.
- Can be grouped with logical operators (AND, OR) and parentheses.
- Can include wildcard characters (*, ?)
- *: matches zero or more characters at the middle or end of the search term
- ?: matches only one character at the middle or end of the search term
- SearchGroup: optional, scope of the fields to search
- If not specified, default scope is all fields
- Can take one of the following values:
ALL FIELDS
,NAME FIELDS
,EMAIL FIELDS
,PHONE FIELDS
,SIDEBAR FIELDS
- ObjectsAndFields: optional, info to return in the search result.
- Info to return in the search result. List of one or more sObjects and one or more fields.
- Info to return in the search result. List of one or more sObjects and one or more fields.
- Example of how to run an SOSL query in Apex:
- Results are returned in a list of lists. Each list contains an array of the returned records. Ex:
- Index 0: list contains array of Accounts
- Index 1: list contains the array of Contacts
- Results are returned in a list of lists. Each list contains an array of the returned records. Ex:
List<List<sObject>> searchList = [FIND 'Wingo OR SFDC' IN ALL FIELDS
RETURNING Account(Name),Contact(FirstName,LastName,Department)];
Account[] searchAccounts = (Account[])searchList[0];
Contact[] searchContacts = (Contact[])searchList[1];
System.debug('Found the following accounts.');
for (Account a : searchAccounts) {
System.debug(a.Name);
}
System.debug('Found the following contacts.');
for (Contact c : searchContacts) {
System.debug(c.LastName + ', ' + c.FirstName);
}
- Possible to filter, reorder, and limit results of SOSL query using the
RETURNING
clause.- Add
WHERE
to filter. Ex:RETURNING Account(Name, Industry WHERE Industry='Apparel')
- Add
ORDER BY
to order. Ex:RETURNING Account(Name, Industry ORDER BY Name)
- Add
LIMIT
to subset the records. Ex:RETURNING Account(Name, Industry LIMIT 10)
- Add
Example Apex class that returns both Contacts and Leads based on an input parameter
public class ContactAndLeadSearch {
public static List<List<sObject>> searchContactsAndLeads (String searchName) {
List<List<sObject>> queryResults = [FIND :searchName IN ALL FIELDS RETURNING Contact(FirstName,LastName), Lead(FirstName,Lastname)];
return queryResults;
}
}
To execute this method using Execute Anonymous:
System.Debug(ContactAndLeadSearch.searchContactsAndLeads('Smith'));