Skip to content

Fixed the support to negation queries on the Eclipse JNoSQL layer to MongoDB #286

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

Merged
merged 6 commits into from
Sep 7, 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
3 changes: 3 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ and this project adheres to https://semver.org/spec/v2.0.0.html[Semantic Version
- Upgrade Redis to version 5.1.4
- Upgrade Solr to version 9.6.1

== Fixed

- Fixed the support to negation queries on the Eclipse JNoSQL layer to MongoDB

== [1.1.1] - 2023-05-25

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.eclipse.jnosql.communication.semistructured.Element;

import java.util.List;
import java.util.regex.Pattern;

final class DocumentQueryConversor {

Expand All @@ -48,14 +49,13 @@ public static Bson convert(CriteriaCondition condition) {
var criteriaCondition = document.get(CriteriaCondition.class);
if (Condition.EQUALS.equals(criteriaCondition.condition())) {
Element element = criteriaCondition.element();
if(element.get() == null) {
if (element.get() == null) {
yield Filters.exists(element.name(), true);
}
yield Filters.ne(element.name(), element.get());
}
yield Filters.not(convert(criteriaCondition));
yield Filters.nor(convert(criteriaCondition));
}
case LIKE -> Filters.regex(document.name(), value.toString());
case LIKE -> Filters.regex(document.name(), Pattern.compile(prepareRegexValue(value.toString())));
case AND -> {
List<CriteriaCondition> andList = condition.element().value().get(new TypeReference<>() {
});
Expand All @@ -67,7 +67,8 @@ public static Bson convert(CriteriaCondition condition) {
});
yield Filters.or(orList.stream()
.map(DocumentQueryConversor::convert).toList());
}case BETWEEN -> {
}
case BETWEEN -> {
List<Object> betweenList = ValueUtil.convertToList(document.value());
yield Filters.and(Filters.gte(document.name(), betweenList.get(0)),
Filters.lte(document.name(), betweenList.get(1)));
Expand All @@ -78,5 +79,12 @@ public static Bson convert(CriteriaCondition condition) {
};
}

public static String prepareRegexValue(String rawData) {
if (rawData == null)
return "^$";
return "^" + rawData
.replaceAll("_", ".{1}")
.replaceAll("%", ".{1,}");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2022 Contributors to the Eclipse Foundation
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php.
*
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
*
* Maximillian Arruda
*/
package org.eclipse.jnosql.databases.mongodb.communication;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

import static org.assertj.core.api.Assertions.assertThat;

class DocumentQueryConversorTest {

@ParameterizedTest
@CsvSource(textBlock = """
Max_;^Max.{1}
Max%;^Max.{1,}
M_x;^M.{1}x
M%x;^M.{1,}x
_ax;^.{1}ax
%ax;^.{1,}ax
;^$
""", delimiterString = ";")
void shouldPrepareRegexValueSupportedByMongoDB(String rawValue, String expectedValue) {
assertThat(DocumentQueryConversor.prepareRegexValue(rawValue))
.as("The value should be prepared to be used in a MongoDB regex query: " +
"the '_' character should matches any single character, and " +
"the '%' character should matches any sequence of characters.")
.isEqualTo(expectedValue);
}

@Test
void shouldReturnEmptyRegexWhenRawValueIsNull() {
assertThat(DocumentQueryConversor.prepareRegexValue(null))
.as("should return an empty regex when the raw value is null")
.isEqualTo("^$");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2024 Contributors to the Eclipse Foundation
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php.
*
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
*
* Maximillian Arruda
*/
package org.eclipse.jnosql.databases.mongodb.integration;

import jakarta.nosql.Column;
import jakarta.nosql.Entity;
import jakarta.nosql.Id;

@Entity
public record AsciiCharacter(
@Id
Long id,
@Column
Integer numericValue,
@Column
String hexadecimal,
@Column
Character thisCharacter,
@Column
Boolean isoControl) {

public static AsciiCharacter of(int value) {
char thisCharacter = (char) value;
return new AsciiCharacter(
Integer.toUnsignedLong(value),
value,
Integer.toHexString(value),
thisCharacter,
Character.isISOControl(thisCharacter)
);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2022 Contributors to the Eclipse Foundation
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php.
*
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
*
* Maximillian Arruda
*/
package org.eclipse.jnosql.databases.mongodb.integration;

import jakarta.data.repository.DataRepository;
import jakarta.data.repository.Query;
import jakarta.data.repository.Repository;
import jakarta.data.repository.Save;

import java.util.LinkedList;
import java.util.List;
import java.util.stream.IntStream;

@Repository
public interface AsciiCharacters extends DataRepository<AsciiCharacter, Integer> {

long countByHexadecimalNotNull();

@Save
List<AsciiCharacter> saveAll(List<AsciiCharacter> characters);

@Query("select thisCharacter" +
" where hexadecimal like '4_'" +
" and hexadecimal not like '%0'" +
" and thisCharacter not in ('E', 'G')" +
" and id not between 72 and 78" +
" order by id asc")
Character[] getABCDFO();


@Query("" +
" order by id asc")
AsciiCharacter[] getAllCharacters();


default void populate() {
if (this.countByHexadecimalNotNull() >= 127)
return;


var dictonary = new LinkedList<AsciiCharacter>();

IntStream.range(1, 128)
.mapToObj(AsciiCharacter::of)// Some databases don't support ASCII NULL character (0)
.forEach(dictonary::add);

this.saveAll(dictonary);
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,22 @@
*
* Contributors:
*
* Otavio Santana
* Maximillian Arruda
*/
package org.eclipse.jnosql.databases.mongodb.integration;

import jakarta.inject.Inject;
import org.assertj.core.api.SoftAssertions;
import org.eclipse.jnosql.databases.mongodb.communication.MongoDBDocumentConfigurations;
import org.eclipse.jnosql.databases.mongodb.mapping.MongoDBTemplate;
import jakarta.nosql.Convert;
import org.eclipse.jnosql.mapping.core.Converters;
import org.eclipse.jnosql.mapping.Database;
import org.eclipse.jnosql.mapping.DatabaseType;
import org.eclipse.jnosql.mapping.core.Converters;
import org.eclipse.jnosql.mapping.core.config.MappingConfigurations;
import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension;
import org.eclipse.jnosql.mapping.document.DocumentTemplate;
import org.eclipse.jnosql.mapping.document.spi.DocumentExtension;
import org.eclipse.jnosql.mapping.reflection.Reflections;
import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension;
import org.eclipse.jnosql.mapping.semistructured.EntityConverter;
import org.jboss.weld.junit5.auto.AddExtensions;
import org.jboss.weld.junit5.auto.AddPackages;
Expand All @@ -37,8 +36,8 @@
import java.util.List;

import static java.util.UUID.randomUUID;
import static org.assertj.core.api.Assertions.as;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.SoftAssertions.assertSoftly;
import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES;
import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED;
Expand All @@ -58,6 +57,7 @@ class RepositoryIntegrationTest {
INSTANCE.get("library");
System.setProperty(MongoDBDocumentConfigurations.HOST.get() + ".1", INSTANCE.host());
System.setProperty(MappingConfigurations.DOCUMENT_DATABASE.get(), "library");
INSTANCE.get("library");
}

@Inject
Expand All @@ -69,6 +69,10 @@ class RepositoryIntegrationTest {
@Database(DatabaseType.DOCUMENT)
BookStore bookStore;

@Inject
@Database(DatabaseType.DOCUMENT)
AsciiCharacters characters;

@Test
void shouldSave() {
Book book = new Book(randomUUID().toString(), "Effective Java", 1);
Expand All @@ -91,8 +95,8 @@ void shouldSave() {
randomUUID().toString(),
List.of(
new BookOrderItem(new Book(randomUUID().toString(), "Effective Java", 3), 1)
,new BookOrderItem(new Book(randomUUID().toString(), "Java Persistence Layer", 1), 10)
,new BookOrderItem(new Book(randomUUID().toString(), "Jakarta EE Cookbook", 1), 5)
, new BookOrderItem(new Book(randomUUID().toString(), "Java Persistence Layer", 1), 10)
, new BookOrderItem(new Book(randomUUID().toString(), "Jakarta EE Cookbook", 1), 5)
)
);

Expand All @@ -103,7 +107,7 @@ void shouldSave() {
.isPresent()
.get()
.as("the loaded the persisted BookOrder doesn't matches with the BookOrder origin")
.satisfies(persistedOrder ->{
.satisfies(persistedOrder -> {
softly.assertThat(persistedOrder.id())
.as("the loaded the persisted BookOrder id is not equals to the BookOrder origin id")
.isEqualTo(order.id());
Expand Down Expand Up @@ -156,4 +160,25 @@ void shouldDeleteAll() {
assertThat(bookStore.findAll()).as("the bookStore is not empty").isEmpty();
}

@Test
public void testQueryWithNot() {
// Given
characters.populate();

assertThatCode(() -> characters.getABCDFO())
.as("Should not throw any exception because it should be supported by this implementation")
.doesNotThrowAnyException();

var abcdfo = characters.getABCDFO();

assertSoftly(softly -> {

softly.assertThat(abcdfo)
.as("should return a non null reference")
.isNotNull()
.as("Should return the characters 'A', 'B', 'C', 'D', 'F', and 'O'")
.contains('A', 'B', 'C', 'D', 'F', 'O');

});
}
}