Skip to content

Commit ace3d03

Browse files
authored
Merge pull request #333 from eclipse-jnosql/include-query
Include to support the new terms on Eclipse JNosQL [contains, endswith, startwith]
2 parents f3ada9d + 20ef418 commit ace3d03

File tree

34 files changed

+1137
-53
lines changed

34 files changed

+1137
-53
lines changed

CHANGELOG.adoc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,22 @@ and this project adheres to https://semver.org/spec/v2.0.0.html[Semantic Version
88

99
== [Unreleased]
1010

11+
=== Changed
12+
13+
- Update ArangoDB driver to 7.22.0
14+
- Update Apache Cassandra driver to 4.19.0
15+
- Update Couchbase driver to 3.9.0
16+
- Update Neo4J driver to 5.28.9
17+
- Update OrientDB driver to 3.2.43
18+
- Update Elasticsearch driver to 8.19.1
19+
- Update Apache Hbase to version 2.6.3
20+
- Update Jedis version to 6.1.0
21+
- Update Apache Tinkerpop core to 3.7.4
22+
23+
=== Added
24+
25+
- Include support to Contains, StartsWith, EndsWith for NoSQL databases
26+
1127
== [1.1.9] - 2025-07-30
1228

1329
=== Fixed

jnosql-arangodb/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
<description>The Eclipse JNoSQL layer to ArangoDB</description>
3030

3131
<properties>
32-
<arango.driver>7.21.0</arango.driver>
32+
<arango.driver>7.22.0</arango.driver>
3333
</properties>
3434

3535
<dependencies>

jnosql-arangodb/src/main/java/org/eclipse/jnosql/databases/arangodb/communication/QueryAQLConverter.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import jakarta.data.Sort;
2020
import org.eclipse.jnosql.communication.TypeReference;
2121
import org.eclipse.jnosql.communication.ValueUtil;
22+
import org.eclipse.jnosql.communication.driver.StringMatch;
2223
import org.eclipse.jnosql.communication.semistructured.CriteriaCondition;
2324
import org.eclipse.jnosql.communication.semistructured.DeleteQuery;
2425
import org.eclipse.jnosql.communication.semistructured.Element;
@@ -153,6 +154,15 @@ private static void definesCondition(CriteriaCondition condition,
153154
case LIKE:
154155
appendCondition(aql, params, entity, document, LIKE);
155156
return;
157+
case CONTAINS:
158+
appendCondition(aql, params, entity, Element.of(document.name(), StringMatch.CONTAINS.format(document.get(String.class))), LIKE);
159+
return;
160+
case STARTS_WITH:
161+
appendCondition(aql, params, entity, Element.of(document.name(), StringMatch.STARTS_WITH.format(document.get(String.class))), LIKE);
162+
return;
163+
case ENDS_WITH:
164+
appendCondition(aql, params, entity, Element.of(document.name(), StringMatch.ENDS_WITH.format(document.get(String.class))), LIKE);
165+
return;
156166
case AND:
157167

158168
for (CriteriaCondition dc : document.get(new TypeReference<List<CriteriaCondition>>() {

jnosql-arangodb/src/test/java/org/eclipse/jnosql/databases/arangodb/communication/ArangoDBDocumentManagerTest.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,20 @@
1919
import org.assertj.core.api.SoftAssertions;
2020
import org.eclipse.jnosql.communication.TypeReference;
2121
import org.eclipse.jnosql.communication.semistructured.CommunicationEntity;
22+
import org.eclipse.jnosql.communication.semistructured.CriteriaCondition;
2223
import org.eclipse.jnosql.communication.semistructured.DeleteQuery;
2324
import org.eclipse.jnosql.communication.semistructured.Element;
2425
import org.eclipse.jnosql.communication.semistructured.Elements;
2526
import org.eclipse.jnosql.communication.semistructured.SelectQuery;
27+
import org.eclipse.jnosql.mapping.semistructured.MappingQuery;
2628
import org.junit.jupiter.api.AfterEach;
2729
import org.junit.jupiter.api.BeforeEach;
2830
import org.junit.jupiter.api.Test;
2931
import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
3032

3133
import java.util.ArrayList;
3234
import java.util.Arrays;
35+
import java.util.Collections;
3336
import java.util.HashMap;
3437
import java.util.List;
3538
import java.util.Map;
@@ -404,6 +407,51 @@ void shouldFindBetween2() {
404407
});
405408
}
406409

410+
@Test
411+
void shouldFindContains() {
412+
var entity = getEntity();
413+
414+
entityManager.insert(entity);
415+
var query = new MappingQuery(Collections.emptyList(), 0L, 0L, CriteriaCondition.contains(Element.of("name",
416+
"lia")), COLLECTION_NAME, Collections.emptyList());
417+
418+
var result = entityManager.select(query).toList();
419+
SoftAssertions.assertSoftly(softly -> {
420+
softly.assertThat(result).hasSize(1);
421+
softly.assertThat(result.get(0).find("name").orElseThrow().get(String.class)).isEqualTo("Poliana");
422+
});
423+
}
424+
425+
@Test
426+
void shouldStartsWith() {
427+
var entity = getEntity();
428+
429+
entityManager.insert(entity);
430+
var query = new MappingQuery(Collections.emptyList(), 0L, 0L, CriteriaCondition.startsWith(Element.of("name",
431+
"Pol")), COLLECTION_NAME, Collections.emptyList());
432+
433+
var result = entityManager.select(query).toList();
434+
SoftAssertions.assertSoftly(softly -> {
435+
softly.assertThat(result).hasSize(1);
436+
softly.assertThat(result.get(0).find("name").orElseThrow().get(String.class)).isEqualTo("Poliana");
437+
});
438+
}
439+
440+
@Test
441+
void shouldEndsWith() {
442+
var entity = getEntity();
443+
444+
entityManager.insert(entity);
445+
var query = new MappingQuery(Collections.emptyList(), 0L, 0L, CriteriaCondition.endsWith(Element.of("name",
446+
"ana")), COLLECTION_NAME, Collections.emptyList());
447+
448+
var result = entityManager.select(query).toList();
449+
SoftAssertions.assertSoftly(softly -> {
450+
softly.assertThat(result).hasSize(1);
451+
softly.assertThat(result.get(0).find("name").orElseThrow().get(String.class)).isEqualTo("Poliana");
452+
});
453+
}
454+
407455
private CommunicationEntity getEntity() {
408456
CommunicationEntity entity = CommunicationEntity.of(COLLECTION_NAME);
409457
Map<String, Object> map = new HashMap<>();

jnosql-cassandra/pom.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
<description>The Eclipse JNoSQL layer to Cassandra</description>
3030

3131
<properties>
32-
<casandra.driver.version>4.17.0</casandra.driver.version>
32+
<casandra.driver.version>4.19.0</casandra.driver.version>
3333

3434
</properties>
3535
<dependencies>
@@ -43,12 +43,12 @@
4343
<version>${project.version}</version>
4444
</dependency>
4545
<dependency>
46-
<groupId>com.datastax.oss</groupId>
46+
<groupId>org.apache.cassandra</groupId>
4747
<artifactId>java-driver-core</artifactId>
4848
<version>${casandra.driver.version}</version>
4949
</dependency>
5050
<dependency>
51-
<groupId>com.datastax.oss</groupId>
51+
<groupId>org.apache.cassandra</groupId>
5252
<artifactId>java-driver-query-builder</artifactId>
5353
<version>${casandra.driver.version}</version>
5454
</dependency>

jnosql-couchbase/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
<dependency>
5050
<groupId>com.couchbase.client</groupId>
5151
<artifactId>java-client</artifactId>
52-
<version>3.8.3</version>
52+
<version>3.9.0</version>
5353
</dependency>
5454
<dependency>
5555
<groupId>org.testcontainers</groupId>

jnosql-couchbase/src/main/java/org/eclipse/jnosql/databases/couchbase/communication/N1QLBuilder.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import com.couchbase.client.java.json.JsonObject;
1818
import jakarta.data.Direction;
1919
import org.eclipse.jnosql.communication.TypeReference;
20+
import org.eclipse.jnosql.communication.driver.StringMatch;
2021
import org.eclipse.jnosql.communication.semistructured.CriteriaCondition;
2122
import org.eclipse.jnosql.communication.semistructured.Element;
2223
import org.eclipse.jnosql.communication.semistructured.SelectQuery;
@@ -113,6 +114,15 @@ private void condition(CriteriaCondition condition, StringBuilder n1ql, JsonObje
113114
case LIKE:
114115
predicate(n1ql, " LIKE ", document, params);
115116
return;
117+
case CONTAINS:
118+
predicate(n1ql, " LIKE ", Element.of(document.name(), StringMatch.CONTAINS.format(document.get(String.class))), params);
119+
return;
120+
case STARTS_WITH:
121+
predicate(n1ql, " LIKE ", Element.of(document.name(), StringMatch.STARTS_WITH.format(document.get(String.class))), params);
122+
return;
123+
case ENDS_WITH:
124+
predicate(n1ql, " LIKE ", Element.of(document.name(), StringMatch.ENDS_WITH.format(document.get(String.class))), params);
125+
return;
116126
case NOT:
117127
n1ql.append(" NOT ");
118128
condition(document.get(CriteriaCondition.class), n1ql, params, ids);

jnosql-couchbase/src/test/java/org/eclipse/jnosql/databases/couchbase/communication/CouchbaseDocumentManagerTest.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,19 @@
2222
import org.eclipse.jnosql.communication.TypeReference;
2323
import org.eclipse.jnosql.communication.keyvalue.BucketManager;
2424
import org.eclipse.jnosql.communication.semistructured.CommunicationEntity;
25+
import org.eclipse.jnosql.communication.semistructured.CriteriaCondition;
2526
import org.eclipse.jnosql.communication.semistructured.DeleteQuery;
2627
import org.eclipse.jnosql.communication.semistructured.Element;
2728
import org.eclipse.jnosql.communication.semistructured.Elements;
2829
import org.eclipse.jnosql.communication.semistructured.SelectQuery;
30+
import org.eclipse.jnosql.mapping.semistructured.MappingQuery;
2931
import org.junit.jupiter.api.AfterEach;
3032
import org.junit.jupiter.api.BeforeEach;
3133
import org.junit.jupiter.api.Test;
3234
import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
3335

3436
import java.util.ArrayList;
37+
import java.util.Collections;
3538
import java.util.HashMap;
3639
import java.util.HashSet;
3740
import java.util.List;
@@ -294,6 +297,51 @@ void shouldUpdateNull(){
294297
});
295298
}
296299

300+
@Test
301+
void shouldFindContains() {
302+
var entity = getEntity();
303+
304+
entityManager.insert(entity);
305+
var query = new MappingQuery(Collections.emptyList(), 0L, 0L, CriteriaCondition.contains(Element.of("name",
306+
"lia")), COLLECTION_PERSON_NAME, Collections.emptyList());
307+
308+
var result = entityManager.select(query).toList();
309+
SoftAssertions.assertSoftly(softly -> {
310+
softly.assertThat(result).hasSize(1);
311+
softly.assertThat(result.get(0).find("name").orElseThrow().get(String.class)).isEqualTo("Poliana");
312+
});
313+
}
314+
315+
@Test
316+
void shouldStartsWith() {
317+
var entity = getEntity();
318+
319+
entityManager.insert(entity);
320+
var query = new MappingQuery(Collections.emptyList(), 0L, 0L, CriteriaCondition.startsWith(Element.of("name",
321+
"Pol")), COLLECTION_PERSON_NAME, Collections.emptyList());
322+
323+
var result = entityManager.select(query).toList();
324+
SoftAssertions.assertSoftly(softly -> {
325+
softly.assertThat(result).hasSize(1);
326+
softly.assertThat(result.get(0).find("name").orElseThrow().get(String.class)).isEqualTo("Poliana");
327+
});
328+
}
329+
330+
@Test
331+
void shouldEndsWith() {
332+
var entity = getEntity();
333+
334+
entityManager.insert(entity);
335+
var query = new MappingQuery(Collections.emptyList(), 0L, 0L, CriteriaCondition.endsWith(Element.of("name",
336+
"ana")), COLLECTION_PERSON_NAME, Collections.emptyList());
337+
338+
var result = entityManager.select(query).toList();
339+
SoftAssertions.assertSoftly(softly -> {
340+
softly.assertThat(result).hasSize(1);
341+
softly.assertThat(result.get(0).find("name").orElseThrow().get(String.class)).isEqualTo("Poliana");
342+
});
343+
}
344+
297345
private CommunicationEntity getEntity() {
298346
CommunicationEntity entity = CommunicationEntity.of(COLLECTION_PERSON_NAME);
299347
Map<String, Object> map = new HashMap<>();
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright (c) 2025 Contributors to the Eclipse Foundation
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* and Apache License v2.0 which accompanies this distribution.
6+
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
7+
* and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php.
8+
*
9+
* You may elect to redistribute this code under either of these licenses.
10+
*
11+
* Contributors:
12+
*
13+
* Otavio Santana
14+
*/
15+
package org.eclipse.jnosql.communication.driver;
16+
17+
import java.util.Objects;
18+
19+
20+
/**
21+
* Represents strategies for matching string values in database queries,
22+
* typically for SQL {@code LIKE} clauses or NoSQL regex-like searches.
23+
* <p>
24+
* Each constant defines a specific way to wrap the given value
25+
* with wildcard symbols ({@code %}) to produce a matching pattern.
26+
* </p>
27+
* <p>
28+
* Example usage:
29+
* <pre>{@code
30+
* String pattern = StringMatch.CONTAINS.format("Ota"); // "%Ota%"
31+
* }</pre>
32+
*/
33+
public enum StringMatch {
34+
35+
/**
36+
* Exact match.
37+
* <p>
38+
* The given value will be used as-is, without adding any wildcards.
39+
* For SQL, this corresponds to {@code column = 'value'}.
40+
* </p>
41+
*/
42+
DEFAULT {
43+
@Override
44+
public String apply(String value) {
45+
return value;
46+
}
47+
},
48+
49+
/**
50+
* Contains match.
51+
* <p>
52+
* The given value will be wrapped with wildcards on both sides:
53+
* {@code %value%}. For SQL, this corresponds to
54+
* {@code column LIKE '%value%'}.
55+
* </p>
56+
*/
57+
CONTAINS {
58+
@Override
59+
public String apply(String value) {
60+
return "%" + value + "%";
61+
}
62+
},
63+
64+
/**
65+
* Starts-with match.
66+
* <p>
67+
* The given value will be followed by a wildcard:
68+
* {@code value%}. For SQL, this corresponds to
69+
* {@code column LIKE 'value%'}.
70+
* </p>
71+
*/
72+
STARTS_WITH {
73+
@Override
74+
public String apply(String value) {
75+
return value + "%";
76+
}
77+
},
78+
79+
/**
80+
* Ends-with match.
81+
* <p>
82+
* The given value will be preceded by a wildcard:
83+
* {@code %value}. For SQL, this corresponds to
84+
* {@code column LIKE '%value'}.
85+
* </p>
86+
*/
87+
ENDS_WITH {
88+
@Override
89+
public String apply(String value) {
90+
return "%" + value;
91+
}
92+
};
93+
94+
/**
95+
* Applies the match strategy to the given value, producing a pattern string.
96+
*
97+
* @param value the value to be transformed into a pattern
98+
* @return the pattern string, with wildcards applied according to the match strategy
99+
*/
100+
abstract String apply(String value);
101+
102+
/**
103+
* Formats the given value by applying the match strategy.
104+
* <p>
105+
* This method ensures the value is not {@code null} before applying the strategy.
106+
* </p>
107+
*
108+
* @param value the value to be transformed into a pattern
109+
* @return the pattern string, with wildcards applied according to the match strategy
110+
* @throws NullPointerException if {@code value} is {@code null}
111+
*/
112+
public String format(String value) {
113+
Objects.requireNonNull(value, "value cannot be null");
114+
return apply(value);
115+
}
116+
117+
}

0 commit comments

Comments
 (0)