Repository: jakartaee/data
Pull request #947 (2025-01-23 18:59:22)

Need a way to indicate no additional restrictions
This came up during discussion of one of the other issues.
Given a repository method that allows Restrictions to be supplied at run time, such as,

```
@Find
List<Person> bornIn(int year, Restriction<Person> restrictions, Order<Person> order);
```

There is currently no way for a repository user to use the method when a case arises where they don't need to make any additional restrictions.

Currently, allowing for that possibility would require the repository to duplicate the method and omit the Restriction parameter,

```
@Find
List<Person> bornIn(int year, Order<Person> order);
```

One idea to avoid the need to duplicate repository methods is to allow an empty Restriction instance that indicates the decision to not apply any additional restrictions,

```
everyoneBornIn2000 = people.bornIn(2000, Restrict.none(), Order.by(_Person.ssn.asc()));
```

I'm not sure what a good name for it is. This PR calls it `Restrict.none()`, but open to better ideas for names.


Patch:
diff --git a/api/src/main/java/jakarta/data/metamodel/restrict/Restrict.java b/api/src/main/java/jakarta/data/metamodel/restrict/Restrict.java
index ccdb89f9..cd4a89e7 100644
--- a/api/src/main/java/jakarta/data/metamodel/restrict/Restrict.java
+++ b/api/src/main/java/jakarta/data/metamodel/restrict/Restrict.java
@@ -234,4 +234,9 @@ public class Restrict {
         }
         return s.toString();
     }
+
+    @SuppressWarnings("unchecked")
+    public static final <T> Restriction<T> unrestricted() {
+        return (Restriction<T>) Unrestricted.INSTANCE;
+    }
 }
diff --git a/api/src/main/java/jakarta/data/metamodel/restrict/Unmatchable.java b/api/src/main/java/jakarta/data/metamodel/restrict/Unmatchable.java
new file mode 100644
index 00000000..ec02886c
--- /dev/null
+++ b/api/src/main/java/jakarta/data/metamodel/restrict/Unmatchable.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2025 Contributors to the Eclipse Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+package jakarta.data.metamodel.restrict;
+
+import java.util.List;
+
+class Unmatchable<T> implements CompositeRestriction<T> {
+    static final Unmatchable<?> INSTANCE = new Unmatchable<>();
+
+    // prevent instantiation by others
+    private Unmatchable() {
+    }
+
+    @Override
+    public boolean isNegated() {
+        return false;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public CompositeRestriction<T> negate() {
+        return (CompositeRestriction<T>) Unrestricted.INSTANCE;
+    }
+
+    @Override
+    public List<Restriction<T>> restrictions() {
+        return List.of();
+    }
+
+    @Override
+    public Type type() {
+        return Type.ANY;
+    }
+}
diff --git a/api/src/main/java/jakarta/data/metamodel/restrict/Unrestricted.java b/api/src/main/java/jakarta/data/metamodel/restrict/Unrestricted.java
new file mode 100644
index 00000000..7604ced9
--- /dev/null
+++ b/api/src/main/java/jakarta/data/metamodel/restrict/Unrestricted.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2025 Contributors to the Eclipse Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+package jakarta.data.metamodel.restrict;
+
+import java.util.List;
+
+class Unrestricted<T> implements CompositeRestriction<T> {
+    static final Unrestricted<?> INSTANCE = new Unrestricted<>();
+
+    // prevent instantiation by others
+    private Unrestricted() {
+    }
+
+    @Override
+    public boolean isNegated() {
+        return false;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public CompositeRestriction<T> negate() {
+        return (CompositeRestriction<T>) Unmatchable.INSTANCE;
+    }
+
+    @Override
+    public List<Restriction<T>> restrictions() {
+        return List.of();
+    }
+
+    @Override
+    public Type type() {
+        return Type.ALL;
+    }
+}
diff --git a/spec/src/main/asciidoc/method-query.asciidoc b/spec/src/main/asciidoc/method-query.asciidoc
index 894fed1c..dfbfd960 100644
--- a/spec/src/main/asciidoc/method-query.asciidoc
+++ b/spec/src/main/asciidoc/method-query.asciidoc
@@ -108,16 +108,6 @@ Absence of an operator implies the equality condition.
 
 The conditions belonging to the restriction determine the parameters of the method, as specified below in <<Query by Method Name Conditions>>.
 
-[WARNING]
-====
-When using NoSQL databases, the support for restrictions varies depending on the database type:
-
-- **Key-Value Databases**: Support for the equals restriction is required for the `Id` attribute. There is no requirement to support other types of restrictions, the `And` and `Or` keywords, or restrictions on other entity attributes. Queries in key-value databases are typically limited to operations using the `Id` attribute only.
-- **Wide-Column Databases**: Wide-column databases must support the `AND` operator but are not required to support the `Or` operator. Restrictions must be supported for the key attribute that is annotated with `jakarta.nosql.Id`. Support for restrictions on other attributes is not required. Typically they can be used if they are indexed as secondary indexes, although support varies by database provider.
-- **Graph and Document Databases**: The `And` and `Or` keywords and all of the restrictions described in this section must be supported. Precedence between `And` and `Or` operators is not guaranteed and may vary significantly based on the NoSQL provider.
-
-====
-
 === Orders
 
 A `find` query may have an _order_. The _order_ specifies how records must be sorted. It is formed by concatenating `OrderBy` with one or more ordered pairs of an entity attribute name and a direction of sorting, `Asc` or `Desc`. The direction may be omitted if there is only one entity attribute, in which case `Asc` is implied.
@@ -126,25 +116,6 @@ The order is lexicographic, that is, ordered pairs occurring earlier take preced
 
 If no order is specified, the records are not sorted.
 
-[WARNING]
-====
-When using NoSQL databases, sorting support varies by database type:
-
-- **Key-Value Databases**: Support for sorting of results is not required.
-
-- **Wide-Column Databases**: Support for sorting of results is not required. In general, sorting is not natively supported. When sorting is available, it is typically limited to:
-  * The key attribute, defined by an annotation such as `jakarta.nosql.Id`.
-  * Fields that are indexed as secondary indexes.
-
-- **Graph and Document Databases**: Support for sorting by a single entity attribute is required. Support for compound sorting (sorting by multiple entity attributes) is not required and may vary due to:
-  * Potential instability with tied values, where sorting for equivalent values may differ across queries.
-  * Schema flexibility and mixed data types.
-  * Dependence on indexes and internal storage order, requiring proper indexing to ensure predictable sorting.
-  * The distributed nature of sharded clusters, where sorting across shards may introduce additional complexity.
-
-====
-
-
 === Example query methods
 
 The following table displays some examples of legal method signatures.
@@ -363,24 +334,6 @@ List<Product> findByNameLikeAndYearMadeBetweenAndPriceLessThan(String namePatter
                                                                Order<Product> sortBy)
 ----
 
-[WARNING]
-====
-When using NoSQL databases, support for the listed method conditions may vary significantly:
-
-- **Textual Conditions**: The `Contains`, `EndsWith`, `IgnoreCase`, `Like`, and `StartsWith` restrictions depend on text processing capabilities and are not required to be supported by Key-Value, Wide-Column, Document and Graph databases. The ability to handle these operations varies widely and depends on the database's support for text-based queries.
-
-- **Null Conditions**: The handling of `Null` values differs across NoSQL databases. In schemaless databases, a field's absence is often treated as non-existent rather than `Null`. This distinction can affect query behavior because some databases might not support `Null` checks or might interpret them differently. This specification does not make any requirements on how `Null` and non-existent fields are handled for Key-Value, Wide-Column, Document and Graph databases.
-
-- **Key-Value Databases**: Support is required for the `=` (equals) restriction when the attribute is the key. The key attribute is defined by `jakarta.nosql.Id`.
-
-- **Wide-Column Databases**: Support for restrictions on attributes other than the `Id` attribute is not required. Some databases might allow support for restrictions on other entity attributes if a secondary index is configured. Handling of `Null` values is not required to be supported and might vary in behavior because some wide-column stores might treat missing columns as `Null`, while others might not store the column at all.
-
-- **Document and Graph Databases**: These databases are generally more feature-rich. Support for the `Contains`, `EndsWith`, `IgnoreCase`, `Like`, and `StartsWith` restrictions is not required. Similarly, `Null` values are not required to be supported. Handling of `Null` in these databases may vary, especially when dealing with omitted fields versus explicitly set `Null` values.
-
-Developers are encouraged to consult the documentation of their chosen NoSQL database provider to confirm the supported operations and clarify the behavior of `Null` versus non-existent fields.
-====
-
-
 === Return Types
 
 The return type of a Query by Method Name is determined as indicated in the following table, where `E` is the queried entity type.
@@ -399,17 +352,6 @@ The return type of a Query by Method Name is determined as indicated in the foll
 | `find` accepting a `PageRequest` | `Page<E>` or `CursoredPage<E>` | For use with pagination
 |===
 
-[WARNING]
-====
-When working with NoSQL databases, the `count` operation might not be supported, depending on the database structure and capabilities:
-
-- **Key-Value Databases**: Support for count is not required. The majority of Key-Value databases do not support counting. Their design focuses on retrieving values by keys rather than aggregating data.
-
-- **Wide-Column Databases**: Support for count is not required. For databases that are capable of returning a count, it might require additional configuration, such as enabling secondary indexes or performing costly full table scans, which can affect performance.
-- **Graph and Document Databases**: Support for count is required. Performance might vary based on factors like indexing and query complexity.
-
-====
-
 
 === Persistent Attribute Names in Query by Method Name
 
diff --git a/spec/src/main/asciidoc/query-language.asciidoc b/spec/src/main/asciidoc/query-language.asciidoc
index 401e5fbf..787e30d6 100644
--- a/spec/src/main/asciidoc/query-language.asciidoc
+++ b/spec/src/main/asciidoc/query-language.asciidoc
@@ -51,22 +51,6 @@ The character sequences `+`, `-`, `*`, `/`, `||`, `=`, `<`, `>`, `<>`, `&lt;=`,
 
 The characters `(`, `)`, and `,` are _punctuation characters_.
 
-[WARNING]
-====
-When working with NoSQL databases, the support for arithmetic operations and support of parentheses for precedence might vary significantly:
-
-- **Key-Value Databases**: Arithmetic operations (`+`, `-`, `*`, `/`) are not supported. These databases are designed for simple key-based lookups and lack query capabilities for complex operations.
-
-- **Wide-Column Databases**: Arithmetic operations are not required to be supported. Some wide-column databases might offer limited support, which might require secondary indexing even for basic querying.
-
-- **Document Databases**: Support of arithmetic operations and support of parenthesis for precedence are not required, although databases typically offer these capabilities. Behavior and extent of support can vary significantly between providers.
-
-- **Graph Databases**: Support for arithmetic operations and parentheses for precedence are not required but is typically offered by databases. Behavior and extent of support can vary significantly between providers.
-
-Due to the diversity of NoSQL database types and their querying capabilities, there is no guarantee that all NoSQL providers will support punctuation characters such as parentheses `(`, `)` for defining operation precedence. It is recommended to consult your NoSQL provider's documentation to confirm the supported query features and their behavior.
-====
-
-
 ==== String literals
 
 A _literal string_ is a character sequence quoted using the character `'`.
@@ -295,17 +279,6 @@ The operands of an equality or inequality operator must have the same type.
 
 NOTE: Portability is maximized when Jakarta Data providers interpret equality and inequality operators in a manner consistent with the implementation of `Object.equals()` or `Comparable.compareTo()` for the assigned Java type.
 
-[WARNING]
-====
-When using NoSQL databases, there are limitations to the support of equality and inequality operators:
-
-1. **Key-Value Databases**: Support for the equality restriction on the key attribute is required. The key attribute is defined by the annotation `jakarta.nosql.Id`. Key-value databases are not required to support any other restrictions.
-
-2. **Wide-Column Databases**: Support for equality restriction and the inequality restriction on the `Id` attribute is required. Support for restrictions on other entity attributes is not required. These operations typically work only with the `Id` by default but might be compatible for other entity attributes if secondary indexes are configured in the database schema.
-
-3. **Graph and Document Databases**: Support for all equality and inequality operators is required.
-====
-
 ==== Ordering
 
 Every <<Basic Types,basic type>> can, in principle, be equipped with a total order. An order for a type determines the result of inequality comparisons, and the effect of the <<Order clause>>.
@@ -324,25 +297,6 @@ For other types, there is at least some freedom in the choice of order. Usually,
 
 - This specification does not define an order for UUID values, which is provider-dependent.
 
-[WARNING]
-====
-When using NoSQL databases, sorting support varies by database type:
-
-- **Key-Value Databases**: Sorting of results is not supported.
-
-- **Wide-Column Databases**: Support for sorting of results is not required. In general, sorting is not natively supported. When sorting is available, it is typically limited to:
-  * The key attribute, defined by an annotation such as `jakarta.nosql.Id`.
-  * Fields that are indexed as secondary indexes.
-
-- **Graph and Document Databases**: Support for sorting by a single entity attribute is required. Support for compound sorting (sorting by multiple entity attributes) is not required and may vary due to:
-  * Potential instability with tied values, where sorting for equivalent values may differ across queries.
-  * Schema flexibility and mixed data types.
-  * Dependence on indexes and internal storage order, requiring proper indexing to ensure predictable sorting.
-  * The distributed nature of sharded clusters, where sorting across shards may introduce additional complexity.
-
-====
-
-
 ==== Logical operators
 
 The logical operators are `and`, `or`, and `not`.
@@ -357,17 +311,6 @@ CAUTION: A compliant implementation of JDQL might feature SQL/JPQL-style ternary
 
 Syntactically, logical operators are parsed with lower precedence than <<Equality and inequality operators,equality and inequality operators>> and other <<Conditional expressions,conditional expressions listed above>>. The `not` operator has higher precedence than `and` and `or`. The `and` operator has higher precedence than `or`.
 
-[WARNING]
-====
-When using NoSQL databases, the support for restrictions varies depending on the database type:
-
-- **Key-Value Databases**: Support for the equality restriction is required for the `Id` attribute. There is no requirement to support other types of restrictions or restrictions on other entity attributes.
-- **Wide-Column Databases**: Wide-column databases are not required to support the `AND` operator or the `OR` operator. Restrictions must be supported for the key attribute that is annotated with `jakarta.nosql.Id`. Support for restrictions on other attributes is not required. Typically they can be used if they are indexed as secondary indexes, although support varies by database provider.
-- **Graph and Document Databases**: The `AND` and `OR` operators and all of the restrictions described in this section must be supported. Precedence between `AND` and `OR` operators is not guaranteed and may vary significantly based on the NoSQL provider.
-
-====
-
-
 === Clauses
 
 Each JDQL statement is built from a sequence of _clauses_. The beginning of a clause is identified by a keyword: `from`, `where`, `select`, `set`, or `order`.
@@ -428,22 +371,6 @@ If the JDQL implementation does not support `count(this)`, it must throw `Unsupp
 
 The `select` clause is optional in `select` statements. When it is missing, the query returns the queried entity.
 
-[WARNING]
-====
-When working with NoSQL databases, the `select` clause behavior may vary depending on the database structure and capabilities:
-
-- **Key-Value Databases**: These databases generally do not support `select` clauses beyond retrieving values by their keys. Support for complex path expressions and aggregate functions like `count(this)` is not required.
-
-- **Wide-Column Databases**: The ability to use a `select` clause may depend on the presence of secondary indexes. Without secondary indexes, selection is often restricted to key-based operations. Support for `count(this)` is not required.
-
-- **Graph and Document Databases**: Support for flexible `select` clauses, including path expressions and aggregate functions like `count(this)` is required. Performance might vary based on the size and indexing of the dataset.
-
-For `count(this)` in particular, if the NoSQL datastore does not natively support counting query results, the Jakarta Data provider is encouraged to implement this operation in Java. However, providers are not required to do so. If `count(this)` is unsupported, an `UnsupportedOperationException` must be thrown during query execution, or repository methods using this expression may be rejected at compilation time.
-
-It is advisable to review your NoSQL provider's documentation to confirm the support and performance implications of `select` clauses and aggregate functions in your queries.
-====
-
-
 ==== Set clause
 
 The `set` clause, with syntax given by `set_clause`, specifies a list of updates to attributes of the queried entity. For each record which satisfies the restriction imposed by the `where` clause, and for each element of the list, the scalar expression is evaluated and assigned to the entity attribute identified by the path expression.


Test patch:
diff --git a/api/src/test/java/jakarta/data/metamodel/restrict/RestrictTest.java b/api/src/test/java/jakarta/data/metamodel/restrict/RestrictTest.java
index 0a1c244d..979a1c96 100644
--- a/api/src/test/java/jakarta/data/metamodel/restrict/RestrictTest.java
+++ b/api/src/test/java/jakarta/data/metamodel/restrict/RestrictTest.java
@@ -20,19 +20,53 @@ package jakarta.data.metamodel.restrict;
 import org.assertj.core.api.SoftAssertions;
 import org.junit.jupiter.api.Test;
 
+import jakarta.data.metamodel.SortableAttribute;
+import jakarta.data.metamodel.TextAttribute;
+import jakarta.data.metamodel.impl.SortableAttributeRecord;
+import jakarta.data.metamodel.impl.TextAttributeRecord;
+
 import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
 import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
 
+import java.util.List;
+
 class RestrictTest {
+    // Mock static metamodel class for tests
+    interface _Employee {
+        String BADGENUM = "badgeNum";
+        String NAME = "name";
+        String POSITION = "position";
+        String YEARHIRED = "yearHired";
+
+        SortableAttribute<Employee> badgeNum = new SortableAttributeRecord<>(BADGENUM);
+        TextAttribute<Employee> name = new TextAttributeRecord<>(NAME);
+        TextAttribute<Employee> position = new TextAttributeRecord<>(POSITION);
+        SortableAttribute<Employee> yearHired = new SortableAttributeRecord<>(YEARHIRED);
+    }
 
+    // Mock entity class for tests
+    class Employee {
+        int badgeNum;
+        String name;
+        String position;
+        int yearHired;
+    }
+
+    /**
+     * Mock repository method for tests.
+     */
+    List<Employee> findByPosition(String position,
+                                  Restriction<Employee> restriction) {
+        return List.of();
+    }
 
     @Test
     void shouldCreateEqualToRestriction() {
-        Restriction<String> restriction = Restrict.equalTo("value", "attributeName");
+        Restriction<Employee> restriction = Restrict.equalTo("value", "attributeName");
 
         assertThat(restriction).isInstanceOf(TextRestrictionRecord.class);
 
-        TextRestrictionRecord<String> basic = (TextRestrictionRecord<String>) restriction;
+        TextRestrictionRecord<Employee> basic = (TextRestrictionRecord<Employee>) restriction;
         SoftAssertions.assertSoftly(soft -> {
             soft.assertThat(basic.attribute()).isEqualTo("attributeName");
             soft.assertThat(basic.comparison()).isEqualTo(Operator.EQUAL);
@@ -42,11 +76,11 @@ class RestrictTest {
 
     @Test
     void shouldCreateNotEqualToRestriction() {
-        Restriction<String> restriction = Restrict.notEqualTo("value", "attributeName");
+        Restriction<Employee> restriction = Restrict.notEqualTo("value", "attributeName");
 
         assertThat(restriction).isInstanceOf(TextRestrictionRecord.class);
 
-        TextRestrictionRecord<String> basic = (TextRestrictionRecord<String>) restriction;
+        TextRestrictionRecord<Employee> basic = (TextRestrictionRecord<Employee>) restriction;
         SoftAssertions.assertSoftly(soft -> {
             soft.assertThat(basic.attribute()).isEqualTo("attributeName");
             soft.assertThat(basic.comparison()).isEqualTo(Operator.NOT_EQUAL);
@@ -56,14 +90,14 @@ class RestrictTest {
 
     @Test
     void shouldCombineAllRestrictionsWithNegation() {
-        Restriction<String> r1 = Restrict.notEqualTo("value1", "attributeName1");
-        Restriction<String> r2 = Restrict.greaterThan(100, "attributeName2");
+        Restriction<Employee> r1 = Restrict.notEqualTo("value1", "attributeName1");
+        Restriction<Employee> r2 = Restrict.greaterThan(100, "attributeName2");
 
-        Restriction<String> combined = Restrict.all(r1, r2);
+        Restriction<Employee> combined = Restrict.all(r1, r2);
 
         assertThat(combined).isInstanceOf(CompositeRestrictionRecord.class);
 
-        CompositeRestrictionRecord<String> composite = (CompositeRestrictionRecord<String>) combined;
+        CompositeRestrictionRecord<Employee> composite = (CompositeRestrictionRecord<Employee>) combined;
         SoftAssertions.assertSoftly(soft -> {
             soft.assertThat(composite.type()).isEqualTo(CompositeRestriction.Type.ALL);
             soft.assertThat(composite.restrictions()).containsExactly(r1, r2);
@@ -73,7 +107,7 @@ class RestrictTest {
 
     @Test
     void shouldCreateContainsRestriction() {
-        TextRestriction<String> restriction = Restrict.contains("substring", "attributeName");
+        TextRestriction<Employee> restriction = Restrict.contains("substring", "attributeName");
 
         SoftAssertions.assertSoftly(soft -> {
             soft.assertThat(restriction.attribute()).isEqualTo("attributeName");
@@ -84,7 +118,7 @@ class RestrictTest {
 
     @Test
     void shouldCreateNegatedContainsRestriction() {
-        TextRestriction<String> restriction = Restrict.notContains("substring", "attributeName");
+        TextRestriction<Employee> restriction = Restrict.notContains("substring", "attributeName");
 
         SoftAssertions.assertSoftly(soft -> {
             soft.assertThat(restriction.attribute()).isEqualTo("attributeName");
@@ -95,7 +129,7 @@ class RestrictTest {
 
     @Test
     void shouldCreateStartsWithRestriction() {
-        TextRestriction<String> restriction = Restrict.startsWith("prefix", "attributeName");
+        TextRestriction<Employee> restriction = Restrict.startsWith("prefix", "attributeName");
 
         SoftAssertions.assertSoftly(soft -> {
             soft.assertThat(restriction.attribute()).isEqualTo("attributeName");
@@ -106,7 +140,7 @@ class RestrictTest {
 
     @Test
     void shouldCreateNegatedStartsWithRestriction() {
-        TextRestriction<String> restriction = Restrict.notStartsWith("prefix", "attributeName");
+        TextRestriction<Employee> restriction = Restrict.notStartsWith("prefix", "attributeName");
 
         SoftAssertions.assertSoftly(soft -> {
             soft.assertThat(restriction.attribute()).isEqualTo("attributeName");
@@ -117,7 +151,7 @@ class RestrictTest {
 
     @Test
     void shouldCreateEndsWithRestriction() {
-        TextRestriction<String> restriction = Restrict.endsWith("suffix", "attributeName");
+        TextRestriction<Employee> restriction = Restrict.endsWith("suffix", "attributeName");
 
         SoftAssertions.assertSoftly(soft -> {
             soft.assertThat(restriction.attribute()).isEqualTo("attributeName");
@@ -128,7 +162,7 @@ class RestrictTest {
 
     @Test
     void shouldCreateNegatedEndsWithRestriction() {
-        TextRestriction<String> restriction = Restrict.notEndsWith("suffix", "attributeName");
+        TextRestriction<Employee> restriction = Restrict.notEndsWith("suffix", "attributeName");
 
         SoftAssertions.assertSoftly(soft -> {
             soft.assertThat(restriction.attribute()).isEqualTo("attributeName");
@@ -137,6 +171,39 @@ class RestrictTest {
         });
     }
 
+    @Test
+    void shouldCreateUnmatchableRestrictionWhenNegatingUnrestricted() {
+        Restriction<Employee> restriction = Restrict.unrestricted();
+        Restriction<Employee> negated = restriction.negate();
+
+        assertThat(negated).isInstanceOf(Unmatchable.class);
+
+        Unmatchable<Employee> unmatchable = (Unmatchable<Employee>) negated;
+
+        SoftAssertions.assertSoftly(soft -> {
+            soft.assertThat(unmatchable.isNegated()).isEqualTo(false);
+            soft.assertThat(unmatchable.negate()).isEqualTo(restriction);
+            soft.assertThat(unmatchable.restrictions()).isEmpty();
+            soft.assertThat(unmatchable.type()).isEqualTo(CompositeRestriction.Type.ANY);
+        });
+    }
+
+    @Test
+    void shouldCreateUnrestricted() {
+        Restriction<Employee> restriction = Restrict.unrestricted();
+
+        assertThat(restriction).isInstanceOf(Unrestricted.class);
+
+        Unrestricted<Employee> unrestricted =
+                (Unrestricted<Employee>) restriction;
+
+        SoftAssertions.assertSoftly(soft -> {
+            soft.assertThat(unrestricted.isNegated()).isEqualTo(false);
+            soft.assertThat(unrestricted.restrictions()).isEmpty();
+            soft.assertThat(unrestricted.type()).isEqualTo(CompositeRestriction.Type.ALL);
+        });
+    }
+
     @Test
     void shouldEscapeToLikePatternCorrectly() {
         String result = Restrict.endsWith("test_value", "attributeName").value();
@@ -144,6 +211,11 @@ class RestrictTest {
         assertThat(result).isEqualTo("%test\\_value");
     }
 
+    @Test
+    void shouldSupplyUnrestrictedToRepositoryMethod() {
+        this.findByPosition("Software Engineer", Restrict.unrestricted());
+    }
+
     @Test
     void shouldThrowExceptionForInvalidWildcard() {
         assertThatThrownBy(() -> Restrict.like("pattern_value", '_', '_', "attributeName"))