Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Addition of simple SOSL support #37

Merged
merged 5 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions force-app/factory/RepoFactoryMock.cls
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ public class RepoFactoryMock {
return this.results;
}

public override List<List<SObject>> getSosl(String searchTerm, List<Query> queries) {
QueriesMade.addAll(queries);
return new List<List<SObject>>{ this.results };
}

public override List<AggregateRecord> aggregate(List<Aggregation> aggregations, List<Query> queries) {
AggregatesMade.addAll(aggregations);
QueriesMade.addAll(queries);
Expand Down
4 changes: 4 additions & 0 deletions force-app/repository/IRepository.cls
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ public interface IRepository extends IDML {
List<SObject> get(List<Query> queries);
List<SObject> getAll();

List<List<SObject>> getSosl(String searchTerm, Query query);
List<List<SObject>> getSosl(String searchTerm, List<Query> queries);
IRepository setSearchGroup(SearchGroup searchGroup);

IRepository setAccessLevel(System.AccessLevel accessLevel);
IRepository setLimit(Integer limitAmount);
IRepository addSortOrder(Schema.SObjectField fieldToken, RepositorySortOrder sortOrder);
Expand Down
38 changes: 38 additions & 0 deletions force-app/repository/Query.cls
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,15 @@ public virtual class Query {
return this.field + ' ' + this.getOperator() + ' ' + this.predicate;
}

public String toSoslString() {
String startingString = this.toString();
for (String key : this.bindVars.keySet()) {
startingString = startingString.replace(':' + key, this.getSoslPredicate(this.bindVars.get(key)));
}
startingString = startingString.replaceAll('= \\(', 'IN \\(').replaceAll('!= \\(', 'NOT IN \\(');
return startingString;
}

public Boolean equals(Object thatObject) {
if (thatObject instanceof Query) {
Query that = (Query) thatObject;
Expand Down Expand Up @@ -269,4 +278,33 @@ public virtual class Query {
}
return builtUpFieldName;
}

private String getSoslPredicate(Object predicate) {
if (predicate == null) {
return 'null';
} else if (predicate instanceof Datetime) {
// the most annoying one
Datetime dt = (Datetime) predicate;
return dt.format('yyyy-MM-dd\'T\'HH:mm:ss\'Z\'', 'Greenwich Mean Time');
} else if (predicate instanceof Iterable<Object>) {
Iterable<Object> localPredicates = (Iterable<Object>) predicate;
if (localPredicates.iterator().hasNext() == false) {
return '';
}
List<String> innerStrings = new List<String>();
for (Object innerPred : localPredicates) {
// recurse for string value
String innerString = this.getSoslPredicate(innerPred);
innerStrings.add(innerString);
}
String start = innerStrings.size() > 1 ? '(' : '';
String ending = innerStrings.size() > 1 ? ')' : '';
return start + String.join(innerStrings, ',') + ending;
} else if (predicate instanceof String) {
String input = (String) predicate;
return '\'' + String.escapeSingleQuotes(input) + '\'';
}

return String.valueOf(predicate);
}
}
41 changes: 38 additions & 3 deletions force-app/repository/Repository.cls
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ public virtual without sharing class Repository implements IRepository {
protected System.AccessLevel accessLevel = System.AccessLevel.SYSTEM_MODE;
protected final Map<String, RepositorySortOrder> fieldToSortOrder = new Map<String, RepositorySortOrder>();

private Boolean baseSelectUsed = false;
private Boolean isSosl = false;
private Boolean shouldAddChildFields = true;
private Integer limitAmount;
private Boolean baseSelectUsed = false;
private SearchGroup soslSearchGroup = SearchGroup.ALL_FIELDS;

public Repository(Schema.SObjectType repoType, List<Schema.SObjectField> queryFields, RepoFactory repoFactory) {
this.dml = repoFactory.getDml();
Expand Down Expand Up @@ -176,14 +178,14 @@ public virtual without sharing class Repository implements IRepository {
private String addWheres(List<Query> queries) {
List<String> wheres = new List<String>();
for (Query qry : queries) {
wheres.add(qry.toString());
wheres.add(this.isSosl ? qry.toSoslString() : qry.toString());
this.bindVars.putAll(qry.getBindVars());
}
return wheres.isEmpty() ? '' : '\nWHERE ' + String.join(wheres, '\nAND ');
}

private List<SObject> performQuery(String finalQuery) {
System.debug('Query: \n' + finalQuery);
System.debug('Query:\n' + finalQuery);
List<SObject> results = Database.queryWithBinds(finalQuery, this.bindVars, this.accessLevel);
this.clearState();
System.debug('Number of results: ' + results.size() + '\nResults: \n' + results);
Expand All @@ -207,6 +209,39 @@ public virtual without sharing class Repository implements IRepository {
return (limitAmount != null ? '\nLIMIT ' + limitAmount : '');
}

// SOSL

public List<List<SObject>> getSosl(String searchTerm, Query queryFilter) {
return this.getSosl(searchTerm, new List<Query>{ queryFilter });
}

public virtual List<List<SObject>> getSosl(String searchTerm, List<Query> queryFilters) {
this.isSosl = true;
String searchQuery =
'FIND \'' +
String.escapeSingleQuotes(searchTerm) +
'\' IN ' +
this.soslSearchGroup.name().replace('_', ' ') +
' RETURNING ' +
this.repoType +
'(' +
String.join(this.addSelectFields(), ',') +
this.addWheres(queryFilters) +
this.getLimitAmount(this.limitAmount) +
')';
System.debug('Search query:\n' + searchQuery);
List<List<SObject>> results = Search.query(searchQuery, this.accessLevel);
System.debug('Number of results: ' + results.size() + '\nResults: \n' + results);
this.clearState();
this.isSosl = false;
return results;
}

public Repository setSearchGroup(SearchGroup searchGroup) {
this.soslSearchGroup = searchGroup;
return this;
}

// DML
public Database.SaveResult doInsert(SObject record) {
return this.dml.doInsert(record);
Expand Down
26 changes: 26 additions & 0 deletions force-app/repository/RepositoryTests.cls
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,32 @@ private class RepositoryTests {
System.assertEquals(event, DMLMock.Published.firstOrDefault);
}

@IsTest
static void it_performs_sosl_queries() {
ContactPointAddress cpa = new ContactPointAddress(Name = 'hello world', PreferenceRank = 1);
insert cpa;
Test.setFixedSearchResults(new List<Id>{ cpa.Id });

List<List<SObject>> results = new ContactPointAddressRepo()
.setSearchGroup(SearchGroup.NAME_FIELDS)
.getSosl('hel', Query.equals(ContactPointAddress.PreferenceRank, 1));

System.assertEquals(cpa.Id, results.get(0).get(0).Id);
}

@IsTest
static void it_performs_sosl_set_backed_queries() {
ContactPointAddress cpa = new ContactPointAddress(Name = 'hello world', PreferenceRank = 1);
insert cpa;
Test.setFixedSearchResults(new List<Id>{ cpa.Id });

List<List<SObject>> results = new ContactPointAddressRepo()
.setSearchGroup(SearchGroup.NAME_FIELDS)
.getSosl('hel', Query.equals(ContactPointAddress.PreferenceRank, new Set<Integer>{ 1, 2 }));

System.assertEquals(cpa.Id, results.get(0).get(0).Id);
}

private class GroupMemberRepo extends Repository {
public GroupMemberRepo() {
super(GroupMember.SObjectType, new List<Schema.SObjectField>{ GroupMember.GroupId }, new RepoFactory());
Expand Down
7 changes: 7 additions & 0 deletions force-app/repository/SearchGroup.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
public enum SearchGroup {
ALL_FIELDS,
EMAIL_FIELDS,
NAME_FIELDS,
PHONE_FIELDS,
SIDEBAR_FIELDS
}
5 changes: 5 additions & 0 deletions force-app/repository/SearchGroup.cls-meta.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>60.0</apiVersion>
<status>Active</status>
</ApexClass>
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"devDependencies": {
"@salesforce/cli": "latest",
"prettier-plugin-apex": "2.1.0",
"prettier": "2.6.2"
"prettier": "3.2.5"
},
"repository": {
"type": "git",
Expand Down
12 changes: 3 additions & 9 deletions scripts/validate-history-query.apex
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
private class OpportunityFieldHistoryExample extends FieldLevelHistory {
protected override String getParentFieldName() {
return OpportunityFieldHistory.OpportunityId.getDescribe().getName();
}
}

if ([SELECT COUNT() FROM OpportunityFieldHistory] == 0) {
List<Opportunity> opps = [SELECT Id, Name FROM Opportunity LIMIT 1];
if (opps.isEmpty()) {
Expand All @@ -15,6 +9,7 @@ if ([SELECT COUNT() FROM OpportunityFieldHistory] == 0) {
Opportunity opp = opps[0];
opp.Name = 'Something New';
update opp;
System.debug('Updated opportunity to create history record');
} else {
System.debug('Histories already exist, continuing ...');
}
Expand All @@ -23,11 +18,10 @@ Exception ex;
List<FieldLevelHistory> histories;
try {
histories = new FieldLevelHistoryRepo(
OpportunityFieldHistory.OpportunityId,
OpportunityFieldHistory.SObjectType,
OpportunityFieldHistoryExample.class,
new List<Schema.SObjectField>(),
new RepoFactory()
).getAllHistory();
).setParentField(OpportunityFieldHistory.OpportunityId).getAllHistory();
System.debug(histories);
} catch (Exception e) {
ex = e;
Expand Down
4 changes: 2 additions & 2 deletions sfdx-project.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@
],
"namespace": "",
"sfdcLoginUrl": "https://login.salesforce.com",
"sourceApiVersion": "56.0"
}
"sourceApiVersion": "60.0"
}
Loading