7.2 SOSL Search Fundamentals
Key Takeaways
- SOSL uses FIND 'term' IN SearchGroup RETURNING Object(fields…), Object2(fields…) to search text across one or more objects
- Choose SOSL for free-text / keyword search across objects; choose SOQL for structured filters, relationships, and aggregates on known objects
- SearchGroup options include ALL FIELDS, NAME FIELDS, EMAIL FIELDS, PHONE FIELDS, and SIDEBAR FIELDS
- Apex SOSL returns List<List<SObject>>; each inner list matches a RETURNING object in order—cast elements to the concrete type
- SOSL has its own governor and result limits; wildcards and short tokens behave differently than SOQL LIKE filters
7.2 SOSL Search Fundamentals
Quick Answer: SOSL (Salesforce Object Search Language) finds records by text search across one or more objects with FIND … IN … RETURNING …. Use SOSL when users type keywords and you do not know which object or field holds the match. Use SOQL when you filter structured data (IDs, picklists, amounts, exact relationships). In Apex, SOSL returns
List<List<SObject>>—one list per RETURNING target.
Platform Developer I expects you to pick the right query language and to read SOSL results without off-by-one casting mistakes. Many “global search box” and “find this phone/email somewhere” scenarios map to SOSL.
FIND … IN … RETURNING Syntax
Core shape:
String term = 'Acme';
List<List<SObject>> searchResults = [
FIND :term
IN ALL FIELDS
RETURNING
Account(Id, Name, Industry WHERE Industry != null ORDER BY Name LIMIT 20),
Contact(Id, FirstName, LastName, Email),
Lead(Id, Name, Company)
];
| Piece | Meaning |
|---|---|
| FIND | Search term (string or bind). Quotes/syntax rules differ slightly between Apex inline SOSL and API forms—follow Apex bind style in code. |
| IN SearchGroup | Which field categories participate (ALL FIELDS, NAME FIELDS, etc.). |
| RETURNING | Objects to return, each with its own field list and optional WHERE / ORDER BY / LIMIT. |
Optional clauses on each RETURNING object refine results after the text match—for example, only Accounts in a certain Industry, or a LIMIT so one object cannot dominate the payload.
Binding the search term
String userInput = 'acme.com';
List<List<SObject>> hits = [
FIND :userInput IN EMAIL FIELDS
RETURNING Contact(Id, Email), Lead(Id, Email)
];
Bind variables keep dynamic terms clean. SOSL still has rules about reserved characters and minimum token length; very short strings may return no rows. Leading wildcards are limited compared with casual “%anything%” thinking from SQL.
When SOSL vs SOQL
| Need | Prefer |
|---|---|
| Keyword / free-text across multiple objects | SOSL |
| Search Name, Email, Phone-like fields like global search | SOSL |
| Filter by Amount, Stage, custom number, exact Id set | SOQL |
| Parent-child relationship queries and aggregates | SOQL |
| Know the object and structured WHERE is enough | SOQL (usually faster and clearer) |
| “Find records mentioning X somewhere in text” | SOSL |
Exam decision frame: If the prompt says the user types a string into a search box and results may include Accounts, Contacts, and Leads, SOSL is the tool. If the prompt says “all Opportunities with Stage = Closed Won and Amount > 50000,” that is SOQL—not SOSL.
SOSL is not a replacement for relationship traversal. You cannot express “Contacts whose Account Industry is Healthcare” as cleanly as SOQL; you search text, then optionally filter RETURNING rows, or follow up with SOQL on IDs.
SearchGroup Options
The IN clause narrows which field groups are searched:
| SearchGroup | Typical fields covered (conceptually) |
|---|---|
| ALL FIELDS | Broadest text search across searchable fields |
| NAME FIELDS | Name-oriented fields (useful for people/company name search) |
| EMAIL FIELDS | Email-type fields |
| PHONE FIELDS | Phone-type fields |
| SIDEBAR FIELDS | Fields used in sidebar / limited search UI behavior |
// Narrow: only phone-like fields
List<List<SObject>> phoneHits = [
FIND '415*' IN PHONE FIELDS
RETURNING Contact(Id, Phone, MobilePhone), Account(Id, Phone)
];
Exam tip: Matching SearchGroup to the requirement shows intent. Searching emails with IN PHONE FIELDS is wrong even if syntax is legal. Prefer the narrowest group that satisfies the requirement when the question emphasizes efficiency or precision.
Returned Structure: List of Lists
Apex assigns SOSL results to List<List<SObject>>. Order of inner lists matches RETURNING order:
List<List<SObject>> raw = [
FIND 'Acme' IN NAME FIELDS
RETURNING Account(Id, Name), Contact(Id, LastName), Lead(Id, Name)
];
List<Account> accounts = (List<Account>)raw[0];
List<Contact> contacts = (List<Contact>)raw[1];
List<Lead> leads = (List<Lead>)raw[2];
for (Account a : accounts) {
System.debug(a.Name);
}
Common bugs:
- Treating
rawasList<Account>— wrong type. - Off-by-one index after reordering RETURNING objects.
- Forgetting empty inner lists are valid (no matches for that object).
- Accessing fields not included in that object’s RETURNING field list.
Dynamic SOSL uses Search.query with a string and the same list-of-lists result shape. Prefer static SOSL when the shape is fixed so the compiler helps.
Filters, ORDER BY, and LIMIT on RETURNING Objects
Each target can carry SOQL-like clauses:
List<List<SObject>> filtered = [
FIND 'renewal' IN ALL FIELDS
RETURNING
Opportunity(
Id, Name, StageName, Amount
WHERE StageName != 'Closed Lost'
ORDER BY Amount DESC
LIMIT 25
),
Case(Id, Subject, Status LIMIT 25)
];
Use these to keep payloads exam- and production-friendly. Overall SOSL still returns only records that matched the FIND text (subject to indexing/searchability); WHERE further restricts that set.
Limits and Operational Awareness
Candidates should remember conceptual SOSL constraints (verify exact numbers in the current Apex Developer Guide if Salesforce revises them):
- Per-transaction SOSL query counts are limited (historically a small number such as 20 SOSL queries per synchronous transaction—far fewer than SOQL’s 100).
- Each SOSL search returns a maximum number of rows overall (commonly discussed as 2,000 rows across returned objects—confirm current docs).
- Search indexes may lag slightly after DML; brand-new records might not appear instantly in SOSL the way they do in SOQL in the same transaction in every scenario—design UIs accordingly.
- Not every field is searchable; encrypted, some formula, and non-indexed text behaviors can exclude matches.
Bulkification still applies: Do not run SOSL inside a per-record loop. One search for a term (or carefully designed dynamic searches) is the pattern; never for (Contact c : Trigger.new) { FIND … }.
SOSL Anti-Patterns on the Exam
| Anti-pattern | Better approach |
|---|---|
| SOSL to load records by Id set | SOQL WHERE Id IN :ids |
| SOSL for SUM/COUNT of Amount | SOQL aggregate |
| Building FIND strings with unescaped user punctuation carelessly | Bind variables; validate input |
| Ignoring list-of-lists casting | Index and cast each RETURNING object |
| Assuming SOSL sees uncommitted DML always | Re-query with SOQL when same-transaction consistency matters |
End-to-End Example: Omnibox Search Service
public with sharing class GlobalFind {
public class Result {
public List<Account> accounts;
public List<Contact> contacts;
}
public static Result search(String term) {
Result r = new Result();
if (String.isBlank(term) || term.length() < 2) {
r.accounts = new List<Account>();
r.contacts = new List<Contact>();
return r;
}
List<List<SObject>> raw = [
FIND :term IN ALL FIELDS
RETURNING
Account(Id, Name ORDER BY Name LIMIT 50),
Contact(Id, Name, Email ORDER BY LastName LIMIT 50)
];
r.accounts = (List<Account>)raw[0];
r.contacts = (List<Contact>)raw[1];
return r;
}
}
This pattern shows with sharing, bind FIND, dual RETURNING, LIMIT, and correct casting—the cluster of ideas exams combine in one scenario.
Putting It Together for the Exam
Ask on every search question:
- Free text across objects → SOSL; structured single-object filter → SOQL.
- Is the SearchGroup aligned with emails, phones, names, or all fields?
- Do I unpack
List<List<SObject>>in RETURNING order? - Did I avoid SOSL-in-a-loop and respect SOSL’s tighter query count?
Section 7.3 connects search/query results to DML changes; section 7.4 places both under governor ceilings.
A Lightning page must let users type a keyword and return matching Accounts, Contacts, and Leads in one operation. Which approach fits best?
In Apex, what is the result type of an inline SOSL query that returns Accounts and Contacts?
When should a developer choose SOQL instead of SOSL?