Skip to content

Commit 18cc18b

Browse files
authored
NOT LIKE operator filter implementation (#75)
NOT LIKE operator filter
1 parent 3da1e4b commit 18cc18b

File tree

3 files changed

+164
-4
lines changed

3 files changed

+164
-4
lines changed

force-app/main/default/classes/SOQL.cls

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,13 @@ public virtual inherited sharing class SOQL implements Queryable {
182182
Filter greaterOrEqual(Object value); // >= :value
183183
Filter containsSome(List<String> values); // LIKE :values
184184
Filter contains(String value); // LIKE :'%' + value + '%'
185+
Filter notContains(String value); //NOT field LIKE :'%' + value + '%'
185186
Filter endsWith(String value); // LIKE :'%' + value
187+
Filter notEndsWith(String value); // NOT field LIKE :'%' + value
186188
Filter startsWith(String value); // LIKE :value + '%'
189+
Filter notStartsWith(String value); // NOT field LIKE :value + '%'
187190
Filter contains(String prefix, String value, String suffix); // custom LIKE
191+
Filter notContains(String prefix, String value, String suffix); // custom NOT field LIKE
188192
Filter isIn(Iterable<Object> iterable); // IN :inList or inSet
189193
Filter isIn(List<Object> inList); // IN :inList
190194
Filter isIn(InnerJoin joinQuery); // SOQL.InnerJoin
@@ -1043,6 +1047,7 @@ public virtual inherited sharing class SOQL implements Queryable {
10431047
private String field;
10441048
private String comperator;
10451049
private Object value;
1050+
private String wrapper = '{0}';
10461051
private DisplayType fieldType;
10471052
private Boolean skipBinding = false;
10481053

@@ -1121,18 +1126,34 @@ public virtual inherited sharing class SOQL implements Queryable {
11211126
return contains('%', formattedString(value), '%');
11221127
}
11231128

1129+
public Filter notContains(String value) {
1130+
return notLike().contains(value);
1131+
}
1132+
11241133
public Filter endsWith(String value) {
11251134
return contains('%', formattedString(value), '');
11261135
}
1136+
1137+
public Filter notEndsWith(String value) {
1138+
return notLike().endsWith(value);
1139+
}
11271140

11281141
public Filter startsWith(String value) {
11291142
return contains('', formattedString(value), '%');
11301143
}
11311144

1145+
public Filter notStartsWith(String value) {
1146+
return notLike().startsWith(value);
1147+
}
1148+
11321149
public Filter contains(String prefix, String value, String suffix) {
11331150
return set('LIKE', prefix + formattedString(value) + suffix);
11341151
}
11351152

1153+
public Filter notContains(String prefix, String value, String suffix) {
1154+
return notLike().contains(prefix, value, suffix);
1155+
}
1156+
11361157
private String formattedString(String value) {
11371158
return value == null ? value : String.escapeSingleQuotes(value.trim());
11381159
}
@@ -1146,6 +1167,11 @@ public virtual inherited sharing class SOQL implements Queryable {
11461167
return set('IN', joinQuery);
11471168
}
11481169

1170+
private Filter notLike() {
1171+
this.wrapper = '(NOT {0})';
1172+
return this;
1173+
}
1174+
11491175
public Filter notIn(Iterable<Object> iterable) {
11501176
return set('NOT IN', iterable);
11511177
}
@@ -1196,13 +1222,13 @@ public virtual inherited sharing class SOQL implements Queryable {
11961222
}
11971223
return this;
11981224
}
1199-
1225+
12001226
public override String toString() {
12011227
if (skipBinding) {
1202-
return field + ' ' + comperator + ' ' + value;
1228+
return String.format(wrapper, new List<String> { field + ' ' + comperator + ' ' + value });
12031229
}
1204-
1205-
return field + ' ' + comperator + ' :' + binder.bind(value);
1230+
1231+
return String.format(wrapper, new List<String> { field + ' ' + comperator + ' :' + binder.bind(value) });
12061232
}
12071233
}
12081234

force-app/main/default/classes/SOQL_Test.cls

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,20 @@ private class SOQL_Test {
694694
Assert.areEqual('%Test%', binding.get('v1'));
695695
}
696696

697+
@IsTest
698+
static void notContains() {
699+
// Test
700+
SOQL builder = SOQL.of(Account.SObjectType)
701+
.whereAre(SOQL.Filter.with(Account.Name).notContains('Test'));
702+
703+
// Verify
704+
String soql = builder.toString();
705+
Assert.areEqual('SELECT Id FROM Account WHERE (NOT Name LIKE :v1)', soql);
706+
707+
Map<String, Object> binding = builder.binding();
708+
Assert.areEqual('%Test%', binding.get('v1'));
709+
}
710+
697711
@IsTest
698712
static void containsValues() {
699713
// Setup
@@ -725,6 +739,20 @@ private class SOQL_Test {
725739
Assert.areEqual('%Test', binding.get('v1'));
726740
}
727741

742+
@IsTest
743+
static void notEndsWith() {
744+
// Test
745+
SOQL builder = SOQL.of(Account.SObjectType)
746+
.whereAre(SOQL.Filter.with(Account.Name).notEndsWith('Test'));
747+
748+
// Verify
749+
String soql = builder.toString();
750+
Assert.areEqual('SELECT Id FROM Account WHERE (NOT Name LIKE :v1)', soql);
751+
752+
Map<String, Object> binding = builder.binding();
753+
Assert.areEqual('%Test', binding.get('v1'));
754+
}
755+
728756
@IsTest
729757
static void startsWith() {
730758
// Test
@@ -738,6 +766,20 @@ private class SOQL_Test {
738766
Map<String, Object> binding = builder.binding();
739767
Assert.areEqual('Test%', binding.get('v1'));
740768
}
769+
770+
@IsTest
771+
static void notStartsWith() {
772+
// Test
773+
SOQL builder = SOQL.of(Account.SObjectType)
774+
.whereAre(SOQL.Filter.with(Account.Name).notStartsWith('Test'));
775+
776+
// Verify
777+
String soql = builder.toString();
778+
Assert.areEqual('SELECT Id FROM Account WHERE (NOT Name LIKE :v1)', soql);
779+
780+
Map<String, Object> binding = builder.binding();
781+
Assert.areEqual('Test%', binding.get('v1'));
782+
}
741783

742784
@IsTest
743785
static void customContains() {
@@ -753,6 +795,20 @@ private class SOQL_Test {
753795
Assert.areEqual('_Test%', binding.get('v1'));
754796
}
755797

798+
@IsTest
799+
static void customNotContains() {
800+
// Test
801+
SOQL builder = SOQL.of(Account.SObjectType)
802+
.whereAre(SOQL.Filter.with(Account.Name).notContains('_', 'Test', '%'));
803+
804+
// Verify
805+
String soql = builder.toString();
806+
Assert.areEqual('SELECT Id FROM Account WHERE (NOT Name LIKE :v1)', soql);
807+
808+
Map<String, Object> binding = builder.binding();
809+
Assert.areEqual('_Test%', binding.get('v1'));
810+
}
811+
756812
@IsTest
757813
static void isInSet() {
758814
// Setup

website/docs/api/soql-filter.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,13 @@ The following are methods for `Filter`.
3333
- [`greaterOrEqual(Object value)`](#greaterorequal)
3434
- [`containsSome(List<String> values)`](#containssome)
3535
- [`contains(String value)`](#contains)
36+
- [`notContains(String value)`](#notcontains)
3637
- [`endsWith(String value)`](#endswith)
38+
- [`notEndsWith(String value)`](#notendswith)
3739
- [`startsWith(String value)`](#startswith)
40+
- [`notStartsWith(String value)`](#notstartswith)
3841
- [`contains(String prefix, String value, String suffix)`](#contains)
42+
- [`notContains(String prefix, String value, String suffix)`](#notcontains)
3943
- [`isIn(Iterable<Object> iterable)`](#isin)
4044
- [`isIn(List<Object> inList)`](#isin)
4145
- [`isIn(InnerJoin joinQuery)`](#isin-join-query)
@@ -501,6 +505,34 @@ SOQL.of(Contact.SObjectType)
501505
.toList();
502506
```
503507

508+
### notcontains
509+
510+
- `WHERE NOT Name LIKE '%My%'`
511+
512+
**Signature**
513+
514+
```apex
515+
Filter notContains(String value)
516+
Filter notContains(String prefix, String value, String suffix);
517+
```
518+
519+
**Example**
520+
521+
```sql
522+
SELECT Id
523+
FROM Account
524+
WHERE NOT Name LIKE '%My%'
525+
```
526+
```apex
527+
SOQL.of(Contact.SObjectType)
528+
.whereAre(SOQL.Filter.name().notContains('My'))
529+
.toList();
530+
531+
SOQL.of(Contact.SObjectType)
532+
.whereAre(SOQL.Filter.name().notContains('_', 'My', '%'))
533+
.toList();
534+
```
535+
504536
### endsWith
505537

506538
- `WHERE Name LIKE '%My'`
@@ -524,6 +556,29 @@ SOQL.of(Contact.SObjectType)
524556
.toList();
525557
```
526558

559+
### notEndsWith
560+
561+
- `WHERE NOT Name LIKE '%My'`
562+
563+
**Signature**
564+
565+
```apex
566+
Filter notEndsWith(String value)
567+
```
568+
569+
**Example**
570+
571+
```sql
572+
SELECT Id
573+
FROM Account
574+
WHERE NOT Name LIKE '%My'
575+
```
576+
```apex
577+
SOQL.of(Contact.SObjectType)
578+
.whereAre(SOQL.Filter.name().notEndsWith('My'))
579+
.toList();
580+
```
581+
527582
### startsWith
528583

529584
- `WHERE Name LIKE 'My%'`
@@ -547,6 +602,29 @@ SOQL.of(Contact.SObjectType)
547602
.toList();
548603
```
549604

605+
### notStartsWith
606+
607+
- `WHERE NOT Name LIKE 'My%'`
608+
609+
**Signature**
610+
611+
```apex
612+
Filter notStartsWith(String value)
613+
```
614+
615+
**Example**
616+
617+
```sql
618+
SELECT Id
619+
FROM Account
620+
WHERE NOT Name LIKE 'My%'
621+
```
622+
```apex
623+
SOQL.of(Contact.SObjectType)
624+
.whereAre(SOQL.Filter.name().notStartsWith('My'))
625+
.toList();
626+
```
627+
550628
### isIn
551629

552630
- `WHERE Id IN :accountIds`

0 commit comments

Comments
 (0)