Skip to content
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
16 changes: 16 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,22 @@ and this project adheres to https://semver.org/spec/v2.0.0.html[Semantic Version

== [Unreleased]

=== Changed

- Update ArangoDB driver to 7.22.0
- Update Apache Cassandra driver to 4.19.0
- Update Couchbase driver to 3.9.0
- Update Neo4J driver to 5.28.9
- Update OrientDB driver to 3.2.43
- Update Elasticsearch driver to 8.19.1
- Update Apache Hbase to version 2.6.3
- Update Jedis version to 6.1.0
- Update Apache Tinkerpop core to 3.7.4

=== Added

- Include support to Contains, StartsWith, EndsWith for NoSQL databases

== [1.1.9] - 2025-07-30

=== Changed
Expand Down
2 changes: 1 addition & 1 deletion jnosql-arangodb/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<description>The Eclipse JNoSQL layer to ArangoDB</description>

<properties>
<arango.driver>7.21.0</arango.driver>
<arango.driver>7.22.0</arango.driver>
</properties>

<dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import jakarta.data.Sort;
import org.eclipse.jnosql.communication.TypeReference;
import org.eclipse.jnosql.communication.ValueUtil;
import org.eclipse.jnosql.communication.driver.StringMatch;
import org.eclipse.jnosql.communication.semistructured.CriteriaCondition;
import org.eclipse.jnosql.communication.semistructured.DeleteQuery;
import org.eclipse.jnosql.communication.semistructured.Element;
Expand Down Expand Up @@ -153,6 +154,15 @@ private static void definesCondition(CriteriaCondition condition,
case LIKE:
appendCondition(aql, params, entity, document, LIKE);
return;
case CONTAINS:
appendCondition(aql, params, entity, Element.of(document.name(), StringMatch.CONTAINS.format(document.get(String.class))), LIKE);
return;
case STARTS_WITH:
appendCondition(aql, params, entity, Element.of(document.name(), StringMatch.STARTS_WITH.format(document.get(String.class))), LIKE);
return;
case ENDS_WITH:
appendCondition(aql, params, entity, Element.of(document.name(), StringMatch.ENDS_WITH.format(document.get(String.class))), LIKE);
return;
case AND:

for (CriteriaCondition dc : document.get(new TypeReference<List<CriteriaCondition>>() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,20 @@
import org.assertj.core.api.SoftAssertions;
import org.eclipse.jnosql.communication.TypeReference;
import org.eclipse.jnosql.communication.semistructured.CommunicationEntity;
import org.eclipse.jnosql.communication.semistructured.CriteriaCondition;
import org.eclipse.jnosql.communication.semistructured.DeleteQuery;
import org.eclipse.jnosql.communication.semistructured.Element;
import org.eclipse.jnosql.communication.semistructured.Elements;
import org.eclipse.jnosql.communication.semistructured.SelectQuery;
import org.eclipse.jnosql.mapping.semistructured.MappingQuery;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfSystemProperty;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -404,6 +407,51 @@ void shouldFindBetween2() {
});
}

@Test
void shouldFindContains() {
var entity = getEntity();

entityManager.insert(entity);
var query = new MappingQuery(Collections.emptyList(), 0L, 0L, CriteriaCondition.contains(Element.of("name",
"lia")), COLLECTION_NAME, Collections.emptyList());

var result = entityManager.select(query).toList();
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(result).hasSize(1);
softly.assertThat(result.get(0).find("name").orElseThrow().get(String.class)).isEqualTo("Poliana");
});
}

@Test
void shouldStartsWith() {
var entity = getEntity();

entityManager.insert(entity);
var query = new MappingQuery(Collections.emptyList(), 0L, 0L, CriteriaCondition.startsWith(Element.of("name",
"Pol")), COLLECTION_NAME, Collections.emptyList());

var result = entityManager.select(query).toList();
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(result).hasSize(1);
softly.assertThat(result.get(0).find("name").orElseThrow().get(String.class)).isEqualTo("Poliana");
});
}

@Test
void shouldEndsWith() {
var entity = getEntity();

entityManager.insert(entity);
var query = new MappingQuery(Collections.emptyList(), 0L, 0L, CriteriaCondition.endsWith(Element.of("name",
"ana")), COLLECTION_NAME, Collections.emptyList());

var result = entityManager.select(query).toList();
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(result).hasSize(1);
softly.assertThat(result.get(0).find("name").orElseThrow().get(String.class)).isEqualTo("Poliana");
});
}

private CommunicationEntity getEntity() {
CommunicationEntity entity = CommunicationEntity.of(COLLECTION_NAME);
Map<String, Object> map = new HashMap<>();
Expand Down
6 changes: 3 additions & 3 deletions jnosql-cassandra/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<description>The Eclipse JNoSQL layer to Cassandra</description>

<properties>
<casandra.driver.version>4.17.0</casandra.driver.version>
<casandra.driver.version>4.19.0</casandra.driver.version>
</properties>
<dependencies>
<dependency>
Expand All @@ -42,12 +42,12 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.datastax.oss</groupId>
<groupId>org.apache.cassandra</groupId>
<artifactId>java-driver-core</artifactId>
<version>${casandra.driver.version}</version>
</dependency>
<dependency>
<groupId>com.datastax.oss</groupId>
<groupId>org.apache.cassandra</groupId>
<artifactId>java-driver-query-builder</artifactId>
<version>${casandra.driver.version}</version>
</dependency>
Expand Down
2 changes: 1 addition & 1 deletion jnosql-couchbase/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
<dependency>
<groupId>com.couchbase.client</groupId>
<artifactId>java-client</artifactId>
<version>3.8.3</version>
<version>3.9.0</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.couchbase.client.java.json.JsonObject;
import jakarta.data.Direction;
import org.eclipse.jnosql.communication.TypeReference;
import org.eclipse.jnosql.communication.driver.StringMatch;
import org.eclipse.jnosql.communication.semistructured.CriteriaCondition;
import org.eclipse.jnosql.communication.semistructured.Element;
import org.eclipse.jnosql.communication.semistructured.SelectQuery;
Expand Down Expand Up @@ -113,6 +114,15 @@ private void condition(CriteriaCondition condition, StringBuilder n1ql, JsonObje
case LIKE:
predicate(n1ql, " LIKE ", document, params);
return;
case CONTAINS:
predicate(n1ql, " LIKE ", Element.of(document.name(), StringMatch.CONTAINS.format(document.get(String.class))), params);
return;
case STARTS_WITH:
predicate(n1ql, " LIKE ", Element.of(document.name(), StringMatch.STARTS_WITH.format(document.get(String.class))), params);
return;
case ENDS_WITH:
predicate(n1ql, " LIKE ", Element.of(document.name(), StringMatch.ENDS_WITH.format(document.get(String.class))), params);
return;
case NOT:
n1ql.append(" NOT ");
condition(document.get(CriteriaCondition.class), n1ql, params, ids);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,19 @@
import org.eclipse.jnosql.communication.TypeReference;
import org.eclipse.jnosql.communication.keyvalue.BucketManager;
import org.eclipse.jnosql.communication.semistructured.CommunicationEntity;
import org.eclipse.jnosql.communication.semistructured.CriteriaCondition;
import org.eclipse.jnosql.communication.semistructured.DeleteQuery;
import org.eclipse.jnosql.communication.semistructured.Element;
import org.eclipse.jnosql.communication.semistructured.Elements;
import org.eclipse.jnosql.communication.semistructured.SelectQuery;
import org.eclipse.jnosql.mapping.semistructured.MappingQuery;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfSystemProperty;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -210,6 +213,51 @@ void shouldCount() {
assertTrue(counted > 0);
}

@Test
void shouldFindContains() {
var entity = getEntity();

entityManager.insert(entity);
var query = new MappingQuery(Collections.emptyList(), 0L, 0L, CriteriaCondition.contains(Element.of("name",
"lia")), COLLECTION_PERSON_NAME, Collections.emptyList());

var result = entityManager.select(query).toList();
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(result).hasSize(1);
softly.assertThat(result.get(0).find("name").orElseThrow().get(String.class)).isEqualTo("Poliana");
});
}

@Test
void shouldStartsWith() {
var entity = getEntity();

entityManager.insert(entity);
var query = new MappingQuery(Collections.emptyList(), 0L, 0L, CriteriaCondition.startsWith(Element.of("name",
"Pol")), COLLECTION_PERSON_NAME, Collections.emptyList());

var result = entityManager.select(query).toList();
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(result).hasSize(1);
softly.assertThat(result.get(0).find("name").orElseThrow().get(String.class)).isEqualTo("Poliana");
});
}

@Test
void shouldEndsWith() {
var entity = getEntity();

entityManager.insert(entity);
var query = new MappingQuery(Collections.emptyList(), 0L, 0L, CriteriaCondition.endsWith(Element.of("name",
"ana")), COLLECTION_PERSON_NAME, Collections.emptyList());

var result = entityManager.select(query).toList();
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(result).hasSize(1);
softly.assertThat(result.get(0).find("name").orElseThrow().get(String.class)).isEqualTo("Poliana");
});
}

private CommunicationEntity createSubdocumentList() {
CommunicationEntity entity = CommunicationEntity.of(COLLECTION_APP_NAME);
entity.add(Element.of("_id", "ids"));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@

/*
* Copyright (c) 2025 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:
*
* Otavio Santana
*/
package org.eclipse.jnosql.communication.driver;

import java.util.Objects;


/**
* Represents strategies for matching string values in database queries,
* typically for SQL {@code LIKE} clauses or NoSQL regex-like searches.
* <p>
* Each constant defines a specific way to wrap the given value
* with wildcard symbols ({@code %}) to produce a matching pattern.
* </p>
* <p>
* Example usage:
* <pre>{@code
* String pattern = StringMatch.CONTAINS.format("Ota"); // "%Ota%"
* }</pre>
*/
public enum StringMatch {

/**
* Exact match.
* <p>
* The given value will be used as-is, without adding any wildcards.
* For SQL, this corresponds to {@code column = 'value'}.
* </p>
*/
DEFAULT {
@Override
public String apply(String value) {
return value;
}
},

/**
* Contains match.
* <p>
* The given value will be wrapped with wildcards on both sides:
* {@code %value%}. For SQL, this corresponds to
* {@code column LIKE '%value%'}.
* </p>
*/
CONTAINS {
@Override
public String apply(String value) {
return "%" + value + "%";
}
},

/**
* Starts-with match.
* <p>
* The given value will be followed by a wildcard:
* {@code value%}. For SQL, this corresponds to
* {@code column LIKE 'value%'}.
* </p>
*/
STARTS_WITH {
@Override
public String apply(String value) {
return value + "%";
}
},

/**
* Ends-with match.
* <p>
* The given value will be preceded by a wildcard:
* {@code %value}. For SQL, this corresponds to
* {@code column LIKE '%value'}.
* </p>
*/
ENDS_WITH {
@Override
public String apply(String value) {
return "%" + value;
}
};

/**
* Applies the match strategy to the given value, producing a pattern string.
*
* @param value the value to be transformed into a pattern
* @return the pattern string, with wildcards applied according to the match strategy
*/
abstract String apply(String value);

/**
* Formats the given value by applying the match strategy.
* <p>
* This method ensures the value is not {@code null} before applying the strategy.
* </p>
*
* @param value the value to be transformed into a pattern
* @return the pattern string, with wildcards applied according to the match strategy
* @throws NullPointerException if {@code value} is {@code null}
*/
public String format(String value) {
Objects.requireNonNull(value, "value cannot be null");
return apply(value);
}

}
Loading