Skip to content

Releases: beyond-the-cloud-dev/soql-lib

v6.10.0

12 May 18:48
db9807d

Choose a tag to compare

v6.10.0 - 12-May-2026

Scope

  • New Feature: @NamespaceAccessible Support for Managed Packages
  • Enhancement: CacheManager Fallback to Apex Transaction Cache
  • Fix: Cache.Session Availability Handling
  • Fix: Mocking with Schema-Prefixed SObject Types
  • Fix: Encrypted Field Usage in Tests
  • Maintenance: PMD Suppressions, Prettier Formatting, Dependency Bumps

SOQL, SOQLCache, SOQLEvaluator, and CacheManager

  • Added @NamespaceAccessible annotation to public access points across SOQL, SOQLCache, and SOQLEvaluator
  • CacheManager now falls back to in-memory Apex transaction cache when the platform cache partition is unavailable
  • Hardened Cache.Session and Cache.Org partition retrieval against exceptions and unavailable session contexts

New Feature: @NamespaceAccessible Support for Managed Packages

All public access points of SOQL, SOQLCache, and SOQLEvaluator are now annotated with @NamespaceAccessible. This allows the library to be consumed from other Apex code in subscriber orgs when distributed as a managed package, without requiring global visibility.

Affected public surface (examples)

@NamespaceAccessible
public virtual inherited sharing class SOQLCache implements Cacheable {
    @NamespaceAccessible
    public interface Selector {
        Cacheable query();
    }

    @NamespaceAccessible
    public static Cacheable of(SObjectType ofObject) {
        return new SOQLCache(ofObject);
    }
    // ...
}

The annotation has been applied to:

  • Static factory methods (SOQL.of(...), SOQLCache.of(...), SOQLEvaluator.of(...))
  • Public interfaces (Selector, Queryable, Cacheable, Mockable, etc.)
  • Public constructors and entry-point methods (mock, removeFromCache, fluent builders)

No behavioral change for unmanaged usage — the annotation only affects access from outside the package namespace.

Enhancement: CacheManager Fallback to Apex Transaction Cache

CacheManager (v1.1.0) now gracefully degrades when a platform cache partition is unavailable (e.g. partition not provisioned, no cache capacity allocated, or session cache not available in the current context). Instead of throwing an exception, reads and writes are transparently routed to an in-memory ApexTransactionCache fallback for the lifetime of the transaction.

What changed

  • Added a private fallback field of type ApexTransactionCache to the abstract PlatformCache class
  • All Cacheable operations (contains, getKeys, get, put, remove) check isAvailable() and route to either the platform partition or the fallback
  • OrgPlatformCache.getPartition(...) now catches Cache.Org.OrgCacheException and returns null
  • SessionPlatformCache.getPartition(...) now checks Cache.Session.isAvailable() and catches Cache.Session.SessionCacheException, returning null on either condition

Behavior

// When the SOQL cache partition is missing or session cache is unavailable,
// SOQLCache continues to function — caching happens in-memory for the current transaction.
SOQLCache.of(Account.SObjectType)
    .cacheInSessionCache()
    .whereEqual(Account.Name, 'Test')
    .toObject();

Fix: Cache.Session Availability Handling

Refactored SessionPlatformCache.getPartition(...) so that the Cache.Session.isAvailable() check is performed inside the try block. This ensures consistent null handling when the session cache is genuinely unavailable (e.g. in contexts where no default cache partition is configured).

Before

public override Cache.Partition getPartition(String partitionName) {
    if (!Cache.Session.isAvailable()) {
        return null;
    }
    try {
        return Cache.Session.getPartition(partitionName);
    } catch (Cache.Session.SessionCacheException e) {
        return null;
    }
}

After

public override Cache.Partition getPartition(String partitionName) {
    try {
        if (!Cache.Session.isAvailable()) {
            return null;
        }
        return Cache.Session.getPartition(partitionName);
    } catch (Cache.Session.SessionCacheException e) {
        return null;
    }
}

Fix: Mocking with Schema-Prefixed SObject Types

Fixed an issue in SOQL.stripAdditionalFields(...) where constructing the cleaned record list via Type.forName('List<' + objectTypeName + '>') could fail to resolve the SObject type in certain namespaces or when the type token did not round-trip through Type.forName. The list type is now built using the explicit Schema. prefix for known SObject types.

Before

List<SObject> cleanedRecords = (List<SObject>) Type.forName('List<' + objectTypeName + ' >').newInstance();

After

List<SObject> cleanedRecords = (List<SObject>) Type.forName(
    'List<' + (this.sObjectType != null ? 'Schema.' + this.sObjectType.toString() : 'SObject') + ' >'
).newInstance();

Fix: Encrypted Field Usage in Tests

Updated tests that previously filtered or projected on Account.Name and Account.Industry in scenarios where the record was expected not to exist. These tests now use Account.DunsNumber and Account.Industry instead, avoiding flakiness in orgs that have classic encryption (or platform encryption) enabled on the Name field.

Affected tests in SOQL_Test / SOQL_Full_Test:

  • toValueOfWhenRecordNotExist
  • toValuesOfRelationshipFieldWhenRelatedRecordNotExist
  • previewWithConditions

Maintenance

PMD Suppressions

  • Added PMD.NcssCount to the suppressed warnings on the SOQL class to align with existing PMD.NcssTypeCount suppression (the library intentionally lives in a single class for fluent usage).

Prettier Formatting

  • Repository-wide reformatting with Prettier (incl. prettier-plugin-apex and @prettier/plugin-xml).
  • Added a root package.json declaring the Prettier toolchain as dev dependencies:
{
    "name": "soql-lib",
    "private": true,
    "version": "1.0.0",
    "devDependencies": {
        "@prettier/plugin-xml": "^3.4.1",
        "prettier": "^3.4.2",
        "prettier-plugin-apex": "^2.2.1"
    }
}

v6.9.0

27 Mar 20:56

Choose a tag to compare

v6.9.0 - 27-March-2026

Scope

  • New Feature: toLabel() Support for SubQuery
  • New Feature: toLabel() with List of Fields
  • Project Structure Reorganization
  • AI Coding Rules & Skills

SOQL, SOQLCache, and SOQLEvaluator

  • Added toLabel() methods to SubQuery interface (5 overloads)
  • Added toLabel(Iterable<String> fields) method to Queryable interface

New Feature: toLabel() Support for SubQuery

SubQuery now supports the toLabel() function, allowing you to retrieve translated picklist values in sub-queries. Five overloads are available, matching the parent query API.

Example: SubQuery toLabel with SObjectField

String soql = SOQL.of(Account.SObjectType)
    .with(SOQL.SubQuery.of('Contacts')
        .with(Contact.LastName)
        .toLabel(Contact.Salutation)
    ).toString();

// SELECT Id , (SELECT LastName, toLabel(Salutation) FROM Contacts) FROM Account

Example: SubQuery toLabel with Alias

String soql = SOQL.of(Account.SObjectType)
    .with(SOQL.SubQuery.of('Contacts')
        .with(Contact.LastName)
        .toLabel(Contact.Salutation, 'contactSalutation')
    ).toString();

// SELECT Id , (SELECT LastName, toLabel(Salutation) contactSalutation FROM Contacts) FROM Account

Example: SubQuery toLabel with String Field (for relationship fields)

String soql = SOQL.of(Account.SObjectType)
    .with(SOQL.SubQuery.of('Contacts')
        .with(Contact.LastName)
        .toLabel('MyCustomObject__r.Name')
    ).toString();

// SELECT Id , (SELECT LastName, toLabel(MyCustomObject__r.Name) FROM Contacts) FROM Account

Example: SubQuery toLabel with List of String Fields

String soql = SOQL.of(Account.SObjectType)
    .with(SOQL.SubQuery.of('Contacts')
        .toLabel(new List<String>{ 'Salutation', 'MyCustomObject__r.Name' })
    ).toString();

// SELECT Id , (SELECT toLabel(Salutation), toLabel(MyCustomObject__r.Name) FROM Contacts) FROM Account

New Feature: toLabel() with List of Fields

Added a new toLabel(Iterable<String> fields) method that allows applying toLabel() to multiple fields at once, reducing boilerplate when translating several picklist fields.

Before (v6.8.0)

SOQL.of(Lead.SObjectType)
    .with(Lead.Company, Lead.Status)
    .toLabel('Status')
    .toLabel('MyCustomObject__r.Name')
    .toString();

After (v6.9.0)

SOQL.of(Lead.SObjectType)
    .with(Lead.Company, Lead.Status)
    .toLabel(new List<String>{ 'Status', 'MyCustomObject__r.Name' })
    .toString();

// SELECT Company, Status, toLabel(Status), toLabel(MyCustomObject__r.Name) FROM Lead

Project Structure Reorganization

Restructured the project directories for better separation between the distributable package and development/test resources.

Before (v6.8.0)

force-app/     → Main package source (SOQL, SOQLCache, SOQLEvaluator, CacheManager)
internal/      → Full test classes (SOQL_Full_Test, SOQLCache_Full_Test, SOQLEvaluator_Full_Test)
examples/      → Example selectors and usage

After (v6.9.0)

package/       → Distributable package source (SOQL, SOQLCache, SOQLEvaluator, CacheManager + Full Test classes)
force-app/     → Development source (main classes + reduced test classes)
examples/      → Example selectors and usage (+ new cached selectors)

Key changes:

  • The managed/unlocked package path moved from force-app/ to package/
  • Full test classes (SOQL_Full_Test, SOQLCache_Full_Test, SOQLEvaluator_Full_Test) moved into the package/ directory alongside the source
  • The internal/ directory was removed
  • Cache Partition metadata (SOQL.cachePartition-meta.xml) added to the package for SOQLCache Org/Session cache support

AI Coding Rules & Skills

Added AI-assisted development rules and skills for Claude Code, Cursor, and A4D (Agentforce for Developers):

  • Claude Code rules (.claude/rules/): soql-lib-queries.md, soql-lib-selectors.md, soql-lib-testing.md
  • Cursor skills (.cursor/skills/): soql-lib-query-builder, soql-lib-selector, soql-lib-testing
  • Agentforce rules (.a4drules/): 01-soql-lib-queries.md, 02-soql-lib-selectors.md, 03-soql-lib-testing.md

These rules help AI coding assistants generate correct SOQL Lib code by providing patterns, conventions, and best practices specific to this library.

v6.8.0

02 Mar 20:24

Choose a tag to compare

v6.8.0 - 02-March-2026

Scope

  • New Feature: Pagination Cursor Support
  • Performance Improvements
  • Test Suite Reorganization
  • API Version Update

SOQL and SOQLCache

  • Added toPaginationCursor() method for pagination support
  • Optimized toIdsOf() and toValuesOf() query performance
  • Enhanced SOQLCache.toId() and SOQLCache.toIdOf() methods
  • Updated to Salesforce API 66.0

New Feature: Pagination Cursor Support

SOQL Lib now supports Database Pagination Cursors, enabling efficient pagination through large result sets. This is particularly useful for building paginated user interfaces and REST APIs.

Example: Basic Pagination Cursor Usage

Database.PaginationCursor cursor = SOQL.of(Account.SObjectType)
    .with(Account.Id, Account.Name)
    .orderBy(Account.Name)
    .toPaginationCursor();

// Use the cursor for pagination
// cursor.getPageSize(), cursor.hasNext(), etc.

Example: Pagination Cursor with Sharing Mode

// Respect sharing rules
Database.PaginationCursor cursor = SOQL.of(Opportunity.SObjectType)
    .systemMode()
    .withSharing()
    .toPaginationCursor();

// Bypass sharing rules
Database.PaginationCursor cursor = SOQL.of(Opportunity.SObjectType)
    .systemMode()
    .withoutSharing()
    .toPaginationCursor();

Performance Improvements

Optimized toIdsOf() and toValuesOf() Query Performance

Enhanced toIdsOf() and toValuesOf() methods to use COUNT(Id) aggregate function, significantly reducing the number of query rows consumed. This optimization helps stay within Salesforce governor limits when extracting unique values from large datasets.

Before (v6.7.0)

-- Generated query consumed all matching rows
SELECT ParentId, Id
FROM Account
WHERE ParentId != null
GROUP BY ParentId

After (v6.8.0)

-- Generated query consumes only unique groups
SELECT ParentId, Id, COUNT(Id)
FROM Account
WHERE ParentId != null
GROUP BY ParentId

Benefits:

  • Significantly reduces query row consumption
  • Helps avoid "Too many query rows" governor limit errors
  • Particularly beneficial when working with large datasets containing many duplicate values
  • No changes required to your code - the optimization is automatic

Example:

// Automatically optimized to use COUNT(Id)
Set<Id> parentIds = SOQL.of(Account.SObjectType)
    .toIdsOf(Account.ParentId);

Set<String> accountNames = SOQL.of(Account.SObjectType)
    .toValuesOf(Account.Name);

SOQLCache Enhancements

Improved toId() and toIdOf() Methods

Enhanced SOQLCache.toId() and SOQLCache.toIdOf() methods to automatically include the required fields in the query, eliminating the need for manual field selection.

Before (v6.7.0)

// Had to manually specify Id field
Id accountId = SOQL_Account.query()
    .with(Account.Id)
    .byId(someId)
    .toId();

After (v6.8.0)

// Id field is automatically included
Id accountId = SOQL_Account.query()
    .byId(someId)
    .toId();

// Also works for related fields
Id parentId = SOQL_Account.query()
    .byId(someId)
    .toIdOf(Account.ParentId);

Test Suite Reorganization

Refactored the test suite for better maintainability and faster deployment:

  • Created internal/ folder - Houses comprehensive test classes that don't deploy to production
  • Split test classes:
    • SOQL_Full_Test.cls (5,260+ lines) - Comprehensive SOQL tests
    • SOQLCache_Full_Test.cls (1,244+ lines) - Comprehensive cache tests
    • SOQLEvaluator_Full_Test.cls (674+ lines) - Comprehensive evaluator tests
  • Reduced main test classes - Core SOQL_Test.cls reduced from ~5,000 to ~1,000 lines for faster deployment
  • Added .prettierrc - Configured code formatting standards

This reorganization significantly improves deployment speed while maintaining comprehensive test coverage for development.

API Version Update

Updated all meta files to support Salesforce API version 66.0, ensuring compatibility with the latest Salesforce features and improvements.

🚨 Breaking Changes 🚨

There are no breaking changes in this release. All improvements are internal optimizations or additive features that maintain full backward compatibility:

  • Existing queries continue to work exactly as before
  • Performance optimizations are automatic and transparent
  • New toPaginationCursor() method is an addition to the existing API

v6.7.0

20 Jan 21:09

Choose a tag to compare

v6.7.0 - 20-January-2026

Scope

  • New Feature: Database Cursor Support
  • Mocking Improvements
  • Filter Enhancements

SOQL

  • Added toCursor() method for Database Cursor support
  • Simplified mocking with automatic SObjectType-based mocking
  • Enhanced isIn() and notIn() filters to accept SObject collections
  • Improved mocking documentation and examples

New Feature: Database Cursor Support

SOQL Lib now supports Database Cursors, enabling efficient processing of large datasets with reduced memory footprint. Cursors are ideal for batch operations and handling millions of records.

Example: Basic Cursor Usage

Database.Cursor queryCursor = SOQL.of(Account.SObjectType)
    .with(Account.Id, Account.Name)
    .toCursor();

System.debug('Total Records: ' + queryCursor.getNumRecords());

Example: Cursor with Sharing Mode

// Respect sharing rules
Database.Cursor queryCursor = SOQL.of(Task.SObjectType)
    .systemMode()
    .withSharing()
    .toCursor();

// Bypass sharing rules
Database.Cursor queryCursor = SOQL.of(Task.SObjectType)
    .systemMode()
    .withoutSharing()
    .toCursor();

Mocking Improvements

Automatic SObjectType-Based Mocking

Previously, every query required a unique mockId() to be set. Now, SOQL Lib automatically uses the SObjectType for mocking, eliminating the need for explicit mock IDs in most cases.

Before (v6.6.0)

// Controller
public static List<Account> getAllAccounts() {
    return SOQL.of(Account.SObjectType)
        .with(Account.Name)
        .mockId('ExampleController.getAllAccounts') // Required
        .toList();
}

// Test
SOQL.mock('ExampleController.getAllAccounts').thenReturn(mockAccounts);

After (v6.7.0)

// Controller - No mockId() needed!
public static List<Account> getAllAccounts() {
    return SOQL.of(Account.SObjectType)
        .with(Account.Name)
        .toList();
}

// Test - Mock by SObjectType
SOQL.mock(Account.SObjectType).thenReturn(mockAccounts);

How It Works:

  • Every query automatically gets a default mock ID based on its SObjectType hash
  • You can still use explicit mockId() when you need different mocks for the same SObjectType
  • Explicit mockId() always takes precedence over SObjectType-based mocking

Example: Using Both Approaches

SOQL.mock('specificQuery').thenReturn(specificMock);
SOQL.mock(Account.SObjectType).thenReturn(defaultMock);

// This uses the specific mock
queryable.mockId('specificQuery').toList();

// This uses the SObjectType mock
SOQL.of(Account.SObjectType).toList();

Filter Enhancements

Skip Collection Looping with isIn() and notIn()

New overloaded methods for isIn() and notIn() that accept SObject collections, eliminating the need for manual collection iteration and value extraction.

Before (v6.6.0)

Set<String> accountNames = new Set<String>();
for (Account acc : accounts) {
    accountNames.add(acc.Name);
}

List<Contact> contacts = SOQL.of(Contact.SObjectType)
    .whereAre(SOQL.Filter.with(Contact.AccountName__c).isIn(accountNames))
    .toList();

After (v6.7.0)

List<Contact> contacts = SOQL.of(Contact.SObjectType)
    .whereAre(SOQL.Filter.with(Contact.AccountName__c).isIn(accounts, Account.Name))
    .toList();

Key Features:

  • Automatically extracts values from the specified field
  • Skips null values to prevent query errors
  • Works with any SObjectField
  • Supports both isIn() and notIn() filters

Example: Filtering with Null Handling

// Even if some accounts have null Names, the query works correctly
List<Account> accounts = new List<Account>{
    new Account(Name = 'Account 1'),
    new Account(Name = 'Account 2'),
    new Account(Name = null) // Automatically skipped
};

List<Contact> contacts = SOQL.of(Contact.SObjectType)
    .whereAre(SOQL.Filter.with(Contact.AccountName__c).isIn(accounts, Account.Name))
    .toList();

v6.6.0

21 Dec 15:34

Choose a tag to compare

v6.6.0 - 21-December-2025

Scope

  • Bug Fixes
  • Mocking Improvements

SOQL

  • Fixed Base64/Blob field deserialization issue in mocking
  • Fixed empty list mocking for toIdsOf() and toValuesOf()

Bug Fixes

Base64/Blob Field Deserialization Fix

Fixed an issue where mocking objects with Base64 or Blob fields (such as StaticResource.Body) would cause JSON deserialization errors. The library now gracefully falls back to returning the original record when JSON deserialization fails.

Example: Mocking StaticResource with Blob

@IsTest
static void mockingSingleRecordWithBlob() {
    StaticResource testResource = new StaticResource(
        Name = 'Test 1', 
        Body = Blob.valueOf('Test 1 Body')
    );

    SOQL.mock('mockingQuery').thenReturn(testResource);
    
    StaticResource result = (StaticResource) SOQL.of(StaticResource.SObjectType)
        .with(StaticResource.Name, StaticResource.Body)
        .mockId('mockingQuery')
        .toObject();

    Assert.areEqual(testResource, result);
}

Empty List Mocking Fix

Fixed an issue where mocking toIdsOf() and toValuesOf() with an empty list would throw an error instead of returning an empty set.

Example: Mocking Empty Results

@IsTest
static void toIdsOfWithMockingEmptyList() {
    SOQL.mock('mockingQuery').thenReturn(new List<Account>());

    Set<Id> parentIds = SOQL.of(Account.SObjectType)
        .mockId('mockingQuery')
        .toIdsOf(Account.ParentId);

    Assert.areEqual(0, parentIds.size());
}

🚨 Breaking Changes 🚨

There are no breaking changes in this release. All changes are bug fixes and internal improvements that maintain backward compatibility.

v6.5.0

15 Nov 15:22

Choose a tag to compare

v6.5.0 - 15-November-2025

Scope

  • Mocking Improvements
  • API Version Update
  • Bug Fixes

SOQL

  • Simplified mocking for toIdsOf() and toValuesOf() methods
  • Updated to Salesforce API 65.0

Mocking Improvements

Simplified toIdsOf() and toValuesOf() Mocking

Previously, mocking toIdsOf() and toValuesOf() required complex AggregateResult JSON deserialization. Now, you can simply pass a list of SObjects.

Before (v6.4.0)

SOQL.mock('mockingQuery').thenReturn(
    (List<AggregateResult>) JSON.deserialize(
        JSON.serialize(new List<Map<String, Object>>{
            new Map<String, Object>{ 'Id' => 'Account Name 1' },
            new Map<String, Object>{ 'Id' => 'Account Name 2' }
        }),
        List<AggregateResult>.class
    )
);

After (v6.5.0)

SOQL.mock('mockingQuery').thenReturn(new List<Account>{
    new Account(Name = 'Test 1'),
    new Account(Name = 'Test 2')
});

API Version Update

Updated all meta files to support Salesforce API version 65.0, ensuring compatibility with the latest Salesforce features and improvements.

v6.4.0

04 Nov 20:26

Choose a tag to compare

v6.4.0 - 04-November-2025

Unlocked package fix.

v6.3.0

04 Nov 20:24

Choose a tag to compare

v6.3.0 - 04-November-2025

Unlocked package fix.

v6.2.0

18 Oct 21:27

Choose a tag to compare

v6.2.0 - 18-October-2024

Scope

  • Enhanced Mocking Capabilities
  • Documentation Update

SOQL

  • Added support for mocking query exceptions
  • Improved error testing capabilities

Mocking Query Exceptions

SOQL Lib now supports mocking query exceptions, enabling you to test error handling scenarios in your Apex code. This is essential for validating that your application gracefully handles database errors, security violations, and other query-related exceptions.

Default Exception Mock

Use .throwException() to simulate a standard query exception with the default message: "List has no rows for assignment to SObject".

Controller

public with sharing class ExampleController {
    public static Account getAccountById(Id accountId) {
        try {
            return (Account) SOQL.of(Account.SObjectType)
                .with(Account.Name, Account.BillingCity)
                .byId(accountId)
                .mockId('ExampleController.getAccountById')
                .toObject();
        } catch (Exception e) {
            // Logger here
            throw e;
        }
    }
}

Test Class

@IsTest
static void getAccountByIdException() {
    SOQL.mock('ExampleController.getAccountById').throwException();

    Test.startTest();
    Exception error;
    try {
        Account result = ExampleController.getAccountById('001000000000000AAA');
    } catch (Exception e) {
        error = e;
    }
    Test.stopTest();

    Assert.isNotNull(error, 'The query exception should be thrown.');
}

Custom Exception Message Mock

Use .throwException(message) to simulate a query exception with a custom error message, such as field-level security errors or invalid field references.

Test Class

@IsTest
static void getAccountByIdException() {
    String errorMessage = 'No such column \'InvalidField__c\' on entity \'Account\'.';
    SOQL.mock('ExampleController.getAccountById').throwException(errorMessage);

    Test.startTest();
    Exception error;
    try {
        Account result = ExampleController.getAccountById('001000000000000AAA');
    } catch (Exception e) {
        error = e;
    }
    Test.stopTest();

    Assert.isNotNull(error, 'The query exception should be thrown.');
    Assert.isTrue(error.getMessage().contains('InvalidField__c'));
}

Documentation Update

Added comprehensive documentation for exception mocking, including:

  • Detailed examples of both default and custom exception messages
  • Best practices for testing error handling scenarios
  • Integration examples with controller error handling patterns

🚨 Breaking Changes 🚨

There are no breaking changes in this release. All new features are additive.

v6.1.0

14 Sep 08:01

Choose a tag to compare

v6.1.0 - September 14, 2025

Scope

  • NEW: Enhanced Result Mapping with ID-based Methods
  • IMPROVED: Automatic Performance Optimization with Null Checking
  • IMPROVED: Documentation Website UI/UX Enhancements
  • IMPROVED: Code Formatting Optimization - Converted Indentation to Tabs

Major New Features

Enhanced Result Mapping - ID-based Methods

The biggest addition to v6.1.0 is the introduction of new ID-focused result mapping methods that provide more precise control over how query results are transformed into maps using ID keys.

New ID Mapping Methods

// Map records by any ID field
Map<Id, Account> ownerIdToAccount = (Map<Id, Account>) SOQL.of(Account.SObjectType)
    .with(Account.Id, Account.Name)
    .toIdMapBy(Account.OwnerId);

// Map records by relationship ID fields  
Map<Id, Account> parentIdToAccount = (Map<Id, Account>) SOQL.of(Account.SObjectType)
    .with(Account.Id, Account.Name)
    .toIdMapBy('Parent', Account.Id);

// Group multiple records by ID field
Map<Id, List<Account>> ownerIdToAccounts = (Map<Id, List<Account>>) SOQL.of(Account.SObjectType)
    .with(Account.Id, Account.Name)
    .toAggregatedIdMapBy(Account.OwnerId);

// Group multiple records by relationship ID field
Map<Id, List<Account>> parentIdToAccounts = (Map<Id, List<Account>>) SOQL.of(Account.SObjectType)
    .with(Account.Id, Account.Name)
    .toAggregatedIdMapBy('Parent', Account.Id);

Automatic Performance Optimization

Smart Null Checking for Better Query Performance

All mapping methods (toMap, toAggregatedMap, toIdMapBy, toAggregatedIdMapBy) now automatically add null-checking conditions to improve query performance:

  • WHERE keyField != null - Automatically added for direct field mapping
  • WHERE relationshipName.targetKeyField != null - Automatically added for relationship field mapping

This optimization reduces result set size and improves query execution time by filtering out records with null key values that would be unusable in the resulting maps.

Traditional Approach:

// Manual null checking required
Map<Id, Account> ownerIdToAccount = new Map<Id, Account>();
for (Account acc : [SELECT Id, Name, OwnerId FROM Account WHERE OwnerId != null]) {
    ownerIdToAccount.put(acc.OwnerId, acc);
}

SOQL Lib Approach:

// Automatic null checking built-in
Map<Id, Account> ownerIdToAccount = (Map<Id, Account>) SOQL.of(Account.SObjectType)
    .with(Account.Id, Account.Name)
    .toIdMapBy(Account.OwnerId);

Documentation and UI Improvements

Enhanced Documentation Website

Light/Dark Mode Compatibility

  • Fixed homepage text visibility issues in light mode
  • Improved contrast and readability across all themes
  • Enhanced user experience with responsive color schemes

Comprehensive Method Documentation

  • Added complete documentation for all new ID mapping methods
  • Included practical examples showing traditional vs SOQL Lib approaches
  • Added performance optimization notes for all mapping methods
  • Enhanced code examples with field selection for better clarity

Code Formatting Optimization

Indentation Converted to Tabs

  • Converted all code indentation from spaces to tabs across the entire codebase
  • Reduced character count by approximately 10% improving file size and load times

New Methods Reference

SOQL Result Methods

toIdMapBy(SObjectField field)

Creates a map where the key is the Id extracted from the specified field and the value is the SObject record.

toIdMapBy(String relationshipName, SObjectField targetKeyField)

Creates a map where the key is the Id extracted from the specified relationship field and the value is the SObject record.

toAggregatedIdMapBy(SObjectField keyField)

Creates a map where the key is the Id extracted from the specified field and the value is a list of SObject records grouped by that Id.

toAggregatedIdMapBy(String relationshipName, SObjectField targetKeyField)

Creates a map where the key is the Id extracted from the specified relationship field and the value is a list of SObject records grouped by that Id.

Contributors

Thank you @kaboomkazoom for your ideas and support!


Download: v6.1.0 Release

Documentation: https://soql.beyondthecloud.dev

GitHub: https://github.com/beyond-the-cloud-dev/soql-lib