Skip to content

Commit 978b392

Browse files
authored
Merge pull request #286 from eclipse/fix-query-with-not-mongodb
Fixed the support to negation queries on the Eclipse JNoSQL layer to MongoDB
2 parents 2a45a43 + 8d1c6e4 commit 978b392

File tree

6 files changed

+206
-13
lines changed

6 files changed

+206
-13
lines changed

CHANGELOG.adoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ and this project adheres to https://semver.org/spec/v2.0.0.html[Semantic Version
2929
- Upgrade Redis to version 5.1.4
3030
- Upgrade Solr to version 9.6.1
3131

32+
== Fixed
33+
34+
- Fixed the support to negation queries on the Eclipse JNoSQL layer to MongoDB
3235

3336
== [1.1.1] - 2023-05-25
3437

jnosql-mongodb/src/main/java/org/eclipse/jnosql/databases/mongodb/communication/DocumentQueryConversor.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.eclipse.jnosql.communication.semistructured.Element;
2626

2727
import java.util.List;
28+
import java.util.regex.Pattern;
2829

2930
final class DocumentQueryConversor {
3031

@@ -48,14 +49,13 @@ public static Bson convert(CriteriaCondition condition) {
4849
var criteriaCondition = document.get(CriteriaCondition.class);
4950
if (Condition.EQUALS.equals(criteriaCondition.condition())) {
5051
Element element = criteriaCondition.element();
51-
if(element.get() == null) {
52+
if (element.get() == null) {
5253
yield Filters.exists(element.name(), true);
5354
}
54-
yield Filters.ne(element.name(), element.get());
5555
}
56-
yield Filters.not(convert(criteriaCondition));
56+
yield Filters.nor(convert(criteriaCondition));
5757
}
58-
case LIKE -> Filters.regex(document.name(), value.toString());
58+
case LIKE -> Filters.regex(document.name(), Pattern.compile(prepareRegexValue(value.toString())));
5959
case AND -> {
6060
List<CriteriaCondition> andList = condition.element().value().get(new TypeReference<>() {
6161
});
@@ -67,7 +67,8 @@ public static Bson convert(CriteriaCondition condition) {
6767
});
6868
yield Filters.or(orList.stream()
6969
.map(DocumentQueryConversor::convert).toList());
70-
}case BETWEEN -> {
70+
}
71+
case BETWEEN -> {
7172
List<Object> betweenList = ValueUtil.convertToList(document.value());
7273
yield Filters.and(Filters.gte(document.name(), betweenList.get(0)),
7374
Filters.lte(document.name(), betweenList.get(1)));
@@ -78,5 +79,12 @@ public static Bson convert(CriteriaCondition condition) {
7879
};
7980
}
8081

82+
public static String prepareRegexValue(String rawData) {
83+
if (rawData == null)
84+
return "^$";
85+
return "^" + rawData
86+
.replaceAll("_", ".{1}")
87+
.replaceAll("%", ".{1,}");
88+
}
8189

8290
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright (c) 2022 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+
* Maximillian Arruda
14+
*/
15+
package org.eclipse.jnosql.databases.mongodb.communication;
16+
17+
import org.junit.jupiter.api.Test;
18+
import org.junit.jupiter.params.ParameterizedTest;
19+
import org.junit.jupiter.params.provider.CsvSource;
20+
21+
import static org.assertj.core.api.Assertions.assertThat;
22+
23+
class DocumentQueryConversorTest {
24+
25+
@ParameterizedTest
26+
@CsvSource(textBlock = """
27+
Max_;^Max.{1}
28+
Max%;^Max.{1,}
29+
M_x;^M.{1}x
30+
M%x;^M.{1,}x
31+
_ax;^.{1}ax
32+
%ax;^.{1,}ax
33+
;^$
34+
""", delimiterString = ";")
35+
void shouldPrepareRegexValueSupportedByMongoDB(String rawValue, String expectedValue) {
36+
assertThat(DocumentQueryConversor.prepareRegexValue(rawValue))
37+
.as("The value should be prepared to be used in a MongoDB regex query: " +
38+
"the '_' character should matches any single character, and " +
39+
"the '%' character should matches any sequence of characters.")
40+
.isEqualTo(expectedValue);
41+
}
42+
43+
@Test
44+
void shouldReturnEmptyRegexWhenRawValueIsNull() {
45+
assertThat(DocumentQueryConversor.prepareRegexValue(null))
46+
.as("should return an empty regex when the raw value is null")
47+
.isEqualTo("^$");
48+
}
49+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) 2024 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+
* Maximillian Arruda
14+
*/
15+
package org.eclipse.jnosql.databases.mongodb.integration;
16+
17+
import jakarta.nosql.Column;
18+
import jakarta.nosql.Entity;
19+
import jakarta.nosql.Id;
20+
21+
@Entity
22+
public record AsciiCharacter(
23+
@Id
24+
Long id,
25+
@Column
26+
Integer numericValue,
27+
@Column
28+
String hexadecimal,
29+
@Column
30+
Character thisCharacter,
31+
@Column
32+
Boolean isoControl) {
33+
34+
public static AsciiCharacter of(int value) {
35+
char thisCharacter = (char) value;
36+
return new AsciiCharacter(
37+
Integer.toUnsignedLong(value),
38+
value,
39+
Integer.toHexString(value),
40+
thisCharacter,
41+
Character.isISOControl(thisCharacter)
42+
);
43+
}
44+
45+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright (c) 2022 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+
* Maximillian Arruda
14+
*/
15+
package org.eclipse.jnosql.databases.mongodb.integration;
16+
17+
import jakarta.data.repository.DataRepository;
18+
import jakarta.data.repository.Query;
19+
import jakarta.data.repository.Repository;
20+
import jakarta.data.repository.Save;
21+
22+
import java.util.LinkedList;
23+
import java.util.List;
24+
import java.util.stream.IntStream;
25+
26+
@Repository
27+
public interface AsciiCharacters extends DataRepository<AsciiCharacter, Integer> {
28+
29+
long countByHexadecimalNotNull();
30+
31+
@Save
32+
List<AsciiCharacter> saveAll(List<AsciiCharacter> characters);
33+
34+
@Query("select thisCharacter" +
35+
" where hexadecimal like '4_'" +
36+
" and hexadecimal not like '%0'" +
37+
" and thisCharacter not in ('E', 'G')" +
38+
" and id not between 72 and 78" +
39+
" order by id asc")
40+
Character[] getABCDFO();
41+
42+
43+
@Query("" +
44+
" order by id asc")
45+
AsciiCharacter[] getAllCharacters();
46+
47+
48+
default void populate() {
49+
if (this.countByHexadecimalNotNull() >= 127)
50+
return;
51+
52+
53+
var dictonary = new LinkedList<AsciiCharacter>();
54+
55+
IntStream.range(1, 128)
56+
.mapToObj(AsciiCharacter::of)// Some databases don't support ASCII NULL character (0)
57+
.forEach(dictonary::add);
58+
59+
this.saveAll(dictonary);
60+
}
61+
62+
63+
}

jnosql-mongodb/src/test/java/org/eclipse/jnosql/databases/mongodb/integration/RepositoryIntegrationTest.java

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,22 @@
1010
*
1111
* Contributors:
1212
*
13+
* Otavio Santana
1314
* Maximillian Arruda
1415
*/
1516
package org.eclipse.jnosql.databases.mongodb.integration;
1617

1718
import jakarta.inject.Inject;
18-
import org.assertj.core.api.SoftAssertions;
1919
import org.eclipse.jnosql.databases.mongodb.communication.MongoDBDocumentConfigurations;
2020
import org.eclipse.jnosql.databases.mongodb.mapping.MongoDBTemplate;
21-
import jakarta.nosql.Convert;
22-
import org.eclipse.jnosql.mapping.core.Converters;
2321
import org.eclipse.jnosql.mapping.Database;
2422
import org.eclipse.jnosql.mapping.DatabaseType;
23+
import org.eclipse.jnosql.mapping.core.Converters;
2524
import org.eclipse.jnosql.mapping.core.config.MappingConfigurations;
25+
import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension;
2626
import org.eclipse.jnosql.mapping.document.DocumentTemplate;
2727
import org.eclipse.jnosql.mapping.document.spi.DocumentExtension;
2828
import org.eclipse.jnosql.mapping.reflection.Reflections;
29-
import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension;
3029
import org.eclipse.jnosql.mapping.semistructured.EntityConverter;
3130
import org.jboss.weld.junit5.auto.AddExtensions;
3231
import org.jboss.weld.junit5.auto.AddPackages;
@@ -37,8 +36,8 @@
3736
import java.util.List;
3837

3938
import static java.util.UUID.randomUUID;
40-
import static org.assertj.core.api.Assertions.as;
4139
import static org.assertj.core.api.Assertions.assertThat;
40+
import static org.assertj.core.api.Assertions.assertThatCode;
4241
import static org.assertj.core.api.SoftAssertions.assertSoftly;
4342
import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES;
4443
import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED;
@@ -58,6 +57,7 @@ class RepositoryIntegrationTest {
5857
INSTANCE.get("library");
5958
System.setProperty(MongoDBDocumentConfigurations.HOST.get() + ".1", INSTANCE.host());
6059
System.setProperty(MappingConfigurations.DOCUMENT_DATABASE.get(), "library");
60+
INSTANCE.get("library");
6161
}
6262

6363
@Inject
@@ -69,6 +69,10 @@ class RepositoryIntegrationTest {
6969
@Database(DatabaseType.DOCUMENT)
7070
BookStore bookStore;
7171

72+
@Inject
73+
@Database(DatabaseType.DOCUMENT)
74+
AsciiCharacters characters;
75+
7276
@Test
7377
void shouldSave() {
7478
Book book = new Book(randomUUID().toString(), "Effective Java", 1);
@@ -91,8 +95,8 @@ void shouldSave() {
9195
randomUUID().toString(),
9296
List.of(
9397
new BookOrderItem(new Book(randomUUID().toString(), "Effective Java", 3), 1)
94-
,new BookOrderItem(new Book(randomUUID().toString(), "Java Persistence Layer", 1), 10)
95-
,new BookOrderItem(new Book(randomUUID().toString(), "Jakarta EE Cookbook", 1), 5)
98+
, new BookOrderItem(new Book(randomUUID().toString(), "Java Persistence Layer", 1), 10)
99+
, new BookOrderItem(new Book(randomUUID().toString(), "Jakarta EE Cookbook", 1), 5)
96100
)
97101
);
98102

@@ -103,7 +107,7 @@ void shouldSave() {
103107
.isPresent()
104108
.get()
105109
.as("the loaded the persisted BookOrder doesn't matches with the BookOrder origin")
106-
.satisfies(persistedOrder ->{
110+
.satisfies(persistedOrder -> {
107111
softly.assertThat(persistedOrder.id())
108112
.as("the loaded the persisted BookOrder id is not equals to the BookOrder origin id")
109113
.isEqualTo(order.id());
@@ -156,4 +160,25 @@ void shouldDeleteAll() {
156160
assertThat(bookStore.findAll()).as("the bookStore is not empty").isEmpty();
157161
}
158162

163+
@Test
164+
public void testQueryWithNot() {
165+
// Given
166+
characters.populate();
167+
168+
assertThatCode(() -> characters.getABCDFO())
169+
.as("Should not throw any exception because it should be supported by this implementation")
170+
.doesNotThrowAnyException();
171+
172+
var abcdfo = characters.getABCDFO();
173+
174+
assertSoftly(softly -> {
175+
176+
softly.assertThat(abcdfo)
177+
.as("should return a non null reference")
178+
.isNotNull()
179+
.as("Should return the characters 'A', 'B', 'C', 'D', 'F', and 'O'")
180+
.contains('A', 'B', 'C', 'D', 'F', 'O');
181+
182+
});
183+
}
159184
}

0 commit comments

Comments
 (0)