Extends RSQL standalone tests (#2431)

* add more complex filters (and / or)
* add tests for legacy specification builders (visitors) in order to clearly check backward compatibility

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-06-05 17:08:10 +03:00
committed by GitHub
parent ff3043b97a
commit ebdcab2aaa
4 changed files with 373 additions and 106 deletions

View File

@@ -563,7 +563,11 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends
}
private Expression<String> caseWise(final CriteriaBuilder cb, final Expression<String> expression) {
return ensureIgnoreCase ? cb.upper(expression) : expression;
if (expression instanceof Path<String> path && path.getJavaType() != String.class) {
return expression; // non string values doesn't support uppercase
} else {
return ensureIgnoreCase ? cb.upper(expression) : expression;
}
}
private String caseWise(final String str) {

View File

@@ -47,6 +47,7 @@ class RSQLToSQLTest {
@Test
void printPG() {
printFrom(JpaTarget.class, TargetFields.class, "metadata.x==y");
printFrom(JpaTarget.class, TargetFields.class, "tag!=TAG1 and tag==TAG2");
printFrom(JpaTarget.class, TargetFields.class, "tag==TAG1 and tag!=TAG2");
}

View File

@@ -0,0 +1,118 @@
/**
* Copyright (c) 2025 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.rsql.sa;
import static org.eclipse.hawkbit.repository.rsql.RsqlConfigHolder.RsqlToSpecBuilder.LEGACY_G1;
import static org.eclipse.hawkbit.repository.rsql.RsqlConfigHolder.RsqlToSpecBuilder.LEGACY_G2;
import java.util.Arrays;
import java.util.List;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.jpa.rsqllegacy.SpecificationBuilderLegacy;
import org.eclipse.hawkbit.repository.rsql.RsqlConfigHolder;
import org.eclipse.hawkbit.repository.rsql.RsqlConfigHolder.RsqlToSpecBuilder;
import org.junit.jupiter.api.Test;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.orm.jpa.vendor.Database;
@SuppressWarnings("java:S2699") // java:S2699 - assertions are un the super methods that are called
@Slf4j
class SpecificationBuilderLegacyTest extends SpecificationBuilderTest {
private final SpecificationBuilderLegacy<RootField, Root> builder = new SpecificationBuilderLegacy<>(RootField.class, null, Database.H2);
private static void runWithRsqlToSpecBuilder(final Runnable runnable, final RsqlToSpecBuilder rsqlToSpecBuilder) {
final RsqlToSpecBuilder defaultBuilder = RsqlConfigHolder.getInstance().getRsqlToSpecBuilder();
RsqlConfigHolder.getInstance().setRsqlToSpecBuilder(rsqlToSpecBuilder);
try {
runnable.run();
} finally {
RsqlConfigHolder.getInstance().setRsqlToSpecBuilder(defaultBuilder);
}
}
@Test
void singularStringAttributeG1() {
runWithRsqlToSpecBuilder(super::singularStringAttribute, LEGACY_G1);
}
@Override
@Test
void singularStringAttribute() {
runWithRsqlToSpecBuilder(super::singularStringAttribute, LEGACY_G2);
}
@Test
void singularIntAttributeG1() {
runWithRsqlToSpecBuilder(super::singularIntAttribute, LEGACY_G1);
}
@Override
@Test
void singularIntAttribute() {
runWithRsqlToSpecBuilder(super::singularIntAttribute, LEGACY_G2);
}
@Test
void singularEntityAttributeG1() {
runWithRsqlToSpecBuilder(super::singularEntityAttribute, LEGACY_G1);
}
@Override
@Test
void singularEntityAttribute() {
runWithRsqlToSpecBuilder(super::singularEntityAttribute, LEGACY_G2);
}
@Test
void pluralSubSetAttributeG1() {
runWithRsqlToSpecBuilder(super::pluralSubSetAttribute, LEGACY_G1);
}
@Override
@Test
void pluralSubSetAttribute() {
runWithRsqlToSpecBuilder(super::pluralSubSetAttribute, LEGACY_G2);
}
// Legacy G1 doesn't support hibernate maps
@Override
@Test
void pluralSubMapAttribute() {
runWithRsqlToSpecBuilder(super::pluralSubMapAttribute, LEGACY_G2);
}
@Override
protected Specification<Root> getSpecification(final String rsql) {
return builder.specification(rsql);
}
@Getter
private enum RootField implements RsqlQueryField {
INTVALUE("intValue"),
STRVALUE("strValue"),
SUBENTITY("subEntity", "strValue", "intValue"),
SUBSET("subSet", "strValue", "intValue"),
SUBMAP("subMap");
private final String jpaEntityFieldName;
private final List<String> subEntityAttributes;
RootField(final String jpaEntityFieldName, final String... subFields) {
this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityAttributes = Arrays.asList(subFields);
}
@Override
public boolean isMap() {
return this == SUBMAP;
}
}
}

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.jpa.rsql.sa;
import static org.assertj.core.api.Assertions.assertThat;
import static org.eclipse.hawkbit.repository.rsql.RsqlConfigHolder.RsqlToSpecBuilder.G3;
import static org.eclipse.hawkbit.repository.rsql.RsqlConfigHolder.RsqlToSpecBuilder.LEGACY_G1;
import java.util.List;
import java.util.Map;
@@ -23,24 +24,24 @@ import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLToSQL;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlParser;
import org.eclipse.hawkbit.repository.jpa.rsql.SpecificationBuilder;
import org.eclipse.hawkbit.repository.rsql.RsqlConfigHolder;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.orm.jpa.vendor.Database;
@SuppressWarnings("java:S5961") // complex check because the matter is very complex
@DataJpaTest(properties = {
"logging.level.org.eclipse.hawkbit.repository.jpa.rsql=DEBUG"
}, excludeAutoConfiguration = { FlywayAutoConfiguration.class } )
}, excludeAutoConfiguration = { FlywayAutoConfiguration.class })
@EnableAutoConfiguration
@Slf4j
class SpecificationBuilderTest {
private static final Database DATABASE = Database.H2;
@Autowired
private SubRepository subRepository;
@Autowired
@@ -48,191 +49,326 @@ class SpecificationBuilderTest {
@Autowired
private EntityManager entityManager;
private final SpecificationBuilder<Root> builder = new SpecificationBuilder<>(null, false, DATABASE);
private final SpecificationBuilder<Root> builder = new SpecificationBuilder<>(null, false, Database.H2);
@Test
void singularStringAttribute() {
final Root root = rootRepository.save(new Root().setStrValue("rootX"));
final Root root2 = rootRepository.save(new Root().setStrValue("rootX"));
final Root root3 = rootRepository.save(new Root().setStrValue("rootY"));
final Root root4 = rootRepository.save(new Root().setStrValue("rootY"));
final Root root1 = rootRepository.save(new Root().setStrValue("rootx"));
final Root root2 = rootRepository.save(new Root().setStrValue("rootx"));
final Root root3 = rootRepository.save(new Root().setStrValue("rooty"));
final Root root4 = rootRepository.save(new Root().setStrValue("rooty"));
final Root root5 = rootRepository.save(new Root()); // null
assertThat(filter("strValue==rootX")).hasSize(2).containsExactlyInAnyOrder(root, root2);
assertThat(filter("strValue==rootx")).hasSize(2).containsExactlyInAnyOrder(root1, root2);
assertThat(filter("strValue==nostr")).isEmpty();
assertThat(filter("strValue=is=null")).hasSize(1).containsExactlyInAnyOrder(root5);
assertThat(filter("strValue!=rootX")).hasSize(3).containsExactlyInAnyOrder(root3, root4, root5);
if (getRsqlToSpecBuilder() != LEGACY_G1) {
assertThat(filter("strValue=is=null")).hasSize(1).containsExactlyInAnyOrder(root5);
}
assertThat(filter("strValue!=rootx")).hasSize(3).containsExactlyInAnyOrder(root3, root4, root5);
assertThat(filter("strValue!=nostr")).hasSize(5);
assertThat(filter("strValue=not=null")).hasSize(4).containsExactlyInAnyOrder(root, root2, root3, root4);
assertThat(filter("strValue<rootY")).hasSize(2).containsExactlyInAnyOrder(root, root2);
assertThat(filter("strValue<=rootY")).hasSize(4).containsExactlyInAnyOrder(root, root2, root3, root4);
assertThat(filter("strValue>rootX")).hasSize(2).containsExactlyInAnyOrder(root3, root4);
assertThat(filter("strValue>=rootX")).hasSize(4);
assertThat(filter("strValue=in=rootX")).hasSize(2).containsExactlyInAnyOrder(root, root2);
assertThat(filter("strValue=in=(rootX, rootY)")).hasSize(4).containsExactlyInAnyOrder(root, root2, root3, root4);
assertThat(filter("strValue=in=(rootZ, rootT)")).isEmpty();
assertThat(filter("strValue=out=rootX")).hasSize(3).containsExactlyInAnyOrder(root3, root4, root5);
assertThat(filter("strValue=out=(rootX, rootY)")).hasSize(1).containsExactlyInAnyOrder(root5);
if (getRsqlToSpecBuilder() != LEGACY_G1) {
assertThat(filter("strValue=not=null")).hasSize(4).containsExactlyInAnyOrder(root1, root2, root3, root4);
}
assertThat(filter("strValue<rooty")).hasSize(2).containsExactlyInAnyOrder(root1, root2);
assertThat(filter("strValue<=rooty")).hasSize(4).containsExactlyInAnyOrder(root1, root2, root3, root4);
assertThat(filter("strValue>rootx")).hasSize(2).containsExactlyInAnyOrder(root3, root4);
assertThat(filter("strValue>=rootx")).hasSize(4).containsExactlyInAnyOrder(root1, root2, root3, root4);
assertThat(filter("strValue=in=rootx")).hasSize(2).containsExactlyInAnyOrder(root1, root2);
assertThat(filter("strValue=in=(rootx, rooty)")).hasSize(4).containsExactlyInAnyOrder(root1, root2, root3, root4);
assertThat(filter("strValue=in=(rootz, roott)")).isEmpty();
assertThat(filter("strValue=out=rootx")).hasSize(3).containsExactlyInAnyOrder(root3, root4, root5);
assertThat(filter("strValue=out=(rootx, rooty)")).hasSize(1).containsExactlyInAnyOrder(root5);
// wildcard, like
assertThat(filter("strValue==root*")).hasSize(4).containsExactlyInAnyOrder(root, root2, root3, root4);
assertThat(filter("strValue==*tX")).hasSize(2).containsExactlyInAnyOrder(root, root2);
assertThat(filter("strValue==root*")).hasSize(4).containsExactlyInAnyOrder(root1, root2, root3, root4);
assertThat(filter("strValue==*tx")).hasSize(2).containsExactlyInAnyOrder(root1, root2);
assertThat(filter("strValue!=root*")).hasSize(1).containsExactlyInAnyOrder(root5);
assertThat(filter("strValue!=*tX")).hasSize(3).containsExactlyInAnyOrder(root3, root4, root5);
assertThat(filter("strValue!=*tx")).hasSize(3).containsExactlyInAnyOrder(root3, root4, root5);
assertThat(filter("strValue==*")).hasSize(4).containsExactlyInAnyOrder(root1, root2, root3, root4);
assertThat(filter("strValue!=*")).hasSize(1).containsExactlyInAnyOrder(root5);
if (getRsqlToSpecBuilder() != LEGACY_G1) {
// null checks
assertThat(filter("strValue=is=null")).hasSize(1).containsExactlyInAnyOrder(root5);
assertThat(filter("strValue=not=null")).hasSize(4).containsExactlyInAnyOrder(root1, root2, root3, root4);
}
assertThat(filter("strValue==*tx and strValue==rooty")).isEmpty();
assertThat(filter("strValue==*tx and strValue!=rootx")).isEmpty();
assertThat(filter("strValue==*tx and strValue==rootx")).hasSize(2).containsExactlyInAnyOrder(root1, root2);
assertThat(filter("strValue==*tx or strValue==rooty")).hasSize(4).containsExactlyInAnyOrder(root1, root2, root3, root4);
assertThat(filter("strValue==*tx or strValue!=rootx")).hasSize(5).containsExactlyInAnyOrder(root1, root2, root3, root4, root5);
if (getRsqlToSpecBuilder() != LEGACY_G1) {
assertThat(filter("strValue==*tx or strValue=is=null")).hasSize(3).containsExactlyInAnyOrder(root1, root2, root5);
}
}
@Test
void singularIntAttribute() {
final Root root = rootRepository.save(new Root().setIntValue(0));
final Root root1 = rootRepository.save(new Root().setIntValue(0));
final Root root2 = rootRepository.save(new Root().setIntValue(0));
final Root root3 = rootRepository.save(new Root().setIntValue(1));
final Root root4 = rootRepository.save(new Root().setIntValue(1));
assertThat(filter("intValue==0")).hasSize(2).containsExactlyInAnyOrder(root, root2);
assertThat(filter("intValue==0")).hasSize(2).containsExactlyInAnyOrder(root1, root2);
assertThat(filter("intValue==2")).isEmpty();
assertThat(filter("intValue!=0")).hasSize(2).containsExactlyInAnyOrder(root3, root4);
assertThat(filter("intValue!=2")).hasSize(4).containsExactlyInAnyOrder(root, root2, root3, root4);
assertThat(filter("intValue<1")).hasSize(2).containsExactlyInAnyOrder(root, root2);
assertThat(filter("intValue<=1")).hasSize(4).containsExactlyInAnyOrder(root, root2, root3, root4);
assertThat(filter("intValue!=2")).hasSize(4).containsExactlyInAnyOrder(root1, root2, root3, root4);
assertThat(filter("intValue<1")).hasSize(2).containsExactlyInAnyOrder(root1, root2);
assertThat(filter("intValue<=1")).hasSize(4).containsExactlyInAnyOrder(root1, root2, root3, root4);
assertThat(filter("intValue>0")).hasSize(2).containsExactlyInAnyOrder(root3, root4);
assertThat(filter("intValue>=0")).hasSize(4).containsExactlyInAnyOrder(root, root2, root3, root4);
assertThat(filter("intValue=in=0")).hasSize(2).containsExactlyInAnyOrder(root, root2);
assertThat(filter("intValue=in=(0, 1)")).hasSize(4).containsExactlyInAnyOrder(root, root2, root3, root4);
assertThat(filter("intValue>=0")).hasSize(4).containsExactlyInAnyOrder(root1, root2, root3, root4);
assertThat(filter("intValue=in=0")).hasSize(2).containsExactlyInAnyOrder(root1, root2);
assertThat(filter("intValue=in=(0, 1)")).hasSize(4).containsExactlyInAnyOrder(root1, root2, root3, root4);
assertThat(filter("intValue=in=(2, 3)")).isEmpty();
assertThat(filter("intValue=out=0")).hasSize(2).containsExactlyInAnyOrder(root3, root4);
assertThat(filter("intValue=out=(0, 1)")).isEmpty();
assertThat(filter("intValue==0 and intValue==1")).isEmpty();
assertThat(filter("intValue==0 and intValue!=1")).hasSize(2).containsExactlyInAnyOrder(root1, root2);
assertThat(filter("intValue==0 and intValue==0")).hasSize(2).containsExactlyInAnyOrder(root1, root2);
assertThat(filter("intValue==0 or intValue==1")).hasSize(4).containsExactlyInAnyOrder(root1, root2, root3, root4);
assertThat(filter("intValue==0 or intValue!=1")).hasSize(2).containsExactlyInAnyOrder(root1, root2);
}
@Test
void singularEntityAttribute() {
final Sub sub = subRepository.save(new Sub().setStrValue("subX").setIntValue(0));
final Sub sub2 = subRepository.save(new Sub().setStrValue("subY").setIntValue(1));
final Root root = rootRepository.save(new Root().setSubEntity(sub));
final Root root2 = rootRepository.save(new Root().setSubEntity(sub));
final Sub sub1 = subRepository.save(new Sub().setStrValue("subx").setIntValue(0));
final Sub sub2 = subRepository.save(new Sub().setStrValue("suby").setIntValue(1));
final Root root1 = rootRepository.save(new Root().setSubEntity(sub1));
final Root root2 = rootRepository.save(new Root().setSubEntity(sub1));
final Root root3 = rootRepository.save(new Root().setSubEntity(sub2));
final Root root4 = rootRepository.save(new Root().setSubEntity(sub2));
final Root root5 = rootRepository.save(new Root()); // no sub set
// by sub entity string
assertThat(filter("subEntity.strValue==subX")).hasSize(2).containsExactlyInAnyOrder(root, root2);
assertThat(filter("subEntity.strValue==subx")).hasSize(2).containsExactlyInAnyOrder(root1, root2);
assertThat(filter("subEntity.strValue==nostr")).isEmpty();
// TODO / recheck - when missing entity shall it be included or not in != or =out=? - now it is
assertThat(filter("subEntity.strValue!=subX")).hasSize(3).containsExactlyInAnyOrder(root3, root4, root5);
assertThat(filter("subEntity.strValue!=subx")).hasSize(3).containsExactlyInAnyOrder(root3, root4, root5);
assertThat(filter("subEntity.strValue!=nostr")).hasSize(5);
assertThat(filter("subEntity.strValue<subY")).hasSize(2).containsExactlyInAnyOrder(root, root2);
assertThat(filter("subEntity.strValue<=subY")).hasSize(4).containsExactlyInAnyOrder(root, root2, root3, root4);
assertThat(filter("subEntity.strValue>subX")).hasSize(2).containsExactlyInAnyOrder(root3, root4);
assertThat(filter("subEntity.strValue>=subX")).hasSize(4).containsExactlyInAnyOrder(root, root2, root3, root4);
assertThat(filter("subEntity.strValue=in=subX")).hasSize(2).containsExactlyInAnyOrder(root, root2);
assertThat(filter("subEntity.strValue=in=(subX, subY)")).hasSize(4).containsExactlyInAnyOrder(root, root2, root3, root4);
assertThat(filter("subEntity.strValue<suby")).hasSize(2).containsExactlyInAnyOrder(root1, root2);
assertThat(filter("subEntity.strValue<=suby")).hasSize(4).containsExactlyInAnyOrder(root1, root2, root3, root4);
assertThat(filter("subEntity.strValue>subx")).hasSize(2).containsExactlyInAnyOrder(root3, root4);
assertThat(filter("subEntity.strValue>=subx")).hasSize(4).containsExactlyInAnyOrder(root1, root2, root3, root4);
assertThat(filter("subEntity.strValue=in=subx")).hasSize(2).containsExactlyInAnyOrder(root1, root2);
assertThat(filter("subEntity.strValue=in=(subx, suby)")).hasSize(4).containsExactlyInAnyOrder(root1, root2, root3, root4);
assertThat(filter("subEntity.strValue=in=(subZ, subT)")).isEmpty();
assertThat(filter("subEntity.strValue=out=subX")).hasSize(3).containsExactlyInAnyOrder(root3, root4, root5);
assertThat(filter("subEntity.strValue=out=(subX, subY)")).hasSize(1).containsExactlyInAnyOrder(root5);
assertThat(filter("subEntity.strValue=out=subx")).hasSize(3).containsExactlyInAnyOrder(root3, root4, root5);
assertThat(filter("subEntity.strValue=out=(subx, suby)")).hasSize(1).containsExactlyInAnyOrder(root5);
// wildcard, like
assertThat(filter("subEntity.strValue==sub*")).hasSize(4).containsExactlyInAnyOrder(root, root2, root3, root4);
assertThat(filter("subEntity.strValue==*bX")).hasSize(2).containsExactlyInAnyOrder(root, root2);
assertThat(filter("subEntity.strValue==sub*")).hasSize(4).containsExactlyInAnyOrder(root1, root2, root3, root4);
assertThat(filter("subEntity.strValue==*bx")).hasSize(2).containsExactlyInAnyOrder(root1, root2);
assertThat(filter("subEntity.strValue!=sub*")).hasSize(1).containsExactlyInAnyOrder(root5);
assertThat(filter("subEntity.strValue!=*bX")).hasSize(3).containsExactlyInAnyOrder(root3, root4, root5);
assertThat(filter("subEntity.strValue!=*bx")).hasSize(3).containsExactlyInAnyOrder(root3, root4, root5);
assertThat(filter("subEntity.strValue==*")).hasSize(4).containsExactlyInAnyOrder(root1, root2, root3, root4);
assertThat(filter("subEntity.strValue!=*")).hasSize(1).containsExactlyInAnyOrder(root5);
if (getRsqlToSpecBuilder() != LEGACY_G1) {
// null checks
assertThat(filter("subEntity.strValue=is=null")).hasSize(1).containsExactlyInAnyOrder(root5);
assertThat(filter("subEntity.strValue=not=null")).hasSize(4).containsExactlyInAnyOrder(root1, root2, root3, root4);
}
assertThat(filter("subEntity.strValue==*bx and subEntity.strValue==suby")).isEmpty();
assertThat(filter("subEntity.strValue==*bx and subEntity.strValue!=subx")).isEmpty();
assertThat(filter("subEntity.strValue==*bx and subEntity.strValue==subx")).hasSize(2).containsExactlyInAnyOrder(root1, root2);
assertThat(filter("subEntity.strValue==*bx or subEntity.strValue==suby"))
.hasSize(4).containsExactlyInAnyOrder(root1, root2, root3, root4);
if (getRsqlToSpecBuilder() != LEGACY_G1) {
// G1 doesn't get null entity in not, and doesn't support =is= and =not=
assertThat(filter("subEntity.strValue==*bx or subEntity.strValue!=subx"))
.hasSize(5).containsExactlyInAnyOrder(root1, root2, root3, root4, root5);
assertThat(filter("subEntity.strValue==*bx or subEntity.strValue=is=null")).hasSize(3)
.containsExactlyInAnyOrder(root1, root2, root5);
}
// by sub entity int
assertThat(filter("subEntity.intValue==0")).hasSize(2).containsExactlyInAnyOrder(root, root2);
assertThat(filter("subEntity.intValue==0")).hasSize(2).containsExactlyInAnyOrder(root1, root2);
assertThat(filter("subEntity.intValue==2")).isEmpty();
assertThat(filter("subEntity.intValue!=0")).hasSize(3).containsExactlyInAnyOrder(root3, root4, root5);
assertThat(filter("subEntity.intValue!=2")).hasSize(5);
assertThat(filter("subEntity.intValue<1")).hasSize(2).containsExactlyInAnyOrder(root, root2);
assertThat(filter("subEntity.intValue<=1")).hasSize(4).containsExactlyInAnyOrder(root, root2, root3, root4);
if (getRsqlToSpecBuilder() != LEGACY_G1) {
// G1 doesn't get null entity in not
assertThat(filter("subEntity.intValue!=0")).hasSize(3).containsExactlyInAnyOrder(root3, root4, root5);
assertThat(filter("subEntity.intValue!=2")).hasSize(5);
}
assertThat(filter("subEntity.intValue<1")).hasSize(2).containsExactlyInAnyOrder(root1, root2);
assertThat(filter("subEntity.intValue<=1")).hasSize(4).containsExactlyInAnyOrder(root1, root2, root3, root4);
assertThat(filter("subEntity.intValue>0")).hasSize(2).containsExactlyInAnyOrder(root3, root4);
assertThat(filter("subEntity.intValue>=0")).hasSize(4);
assertThat(filter("subEntity.intValue=in=0")).hasSize(2).containsExactlyInAnyOrder(root, root2);
assertThat(filter("subEntity.intValue=in=(0, 1)")).hasSize(4).containsExactlyInAnyOrder(root, root2, root3, root4);
assertThat(filter("subEntity.intValue=in=0")).hasSize(2).containsExactlyInAnyOrder(root1, root2);
assertThat(filter("subEntity.intValue=in=(0, 1)")).hasSize(4).containsExactlyInAnyOrder(root1, root2, root3, root4);
assertThat(filter("subEntity.intValue=in=(2, 3)")).isEmpty();
assertThat(filter("subEntity.intValue=out=0")).hasSize(3).containsExactlyInAnyOrder(root3, root4, root5);
assertThat(filter("subEntity.intValue=out=(0, 1)")).hasSize(1).containsExactlyInAnyOrder(root5);
assertThat(filter("subEntity.intValue==0 and subEntity.intValue==1")).isEmpty();
assertThat(filter("subEntity.intValue==0 and subEntity.intValue!=1")).hasSize(2).containsExactlyInAnyOrder(root1, root2);
assertThat(filter("subEntity.intValue==0 and subEntity.intValue==0")).hasSize(2).containsExactlyInAnyOrder(root1, root2);
assertThat(filter("subEntity.intValue==0 or subEntity.intValue==1")).hasSize(4).containsExactlyInAnyOrder(root1, root2, root3, root4);
if (getRsqlToSpecBuilder() != LEGACY_G1) {
// G1 doesn't get null entity in not
assertThat(filter("subEntity.intValue==0 or subEntity.intValue!=1")).hasSize(3).containsExactlyInAnyOrder(root1, root2, root5);
}
assertThat(filter("subEntity.strValue==subx and subEntity.intValue==1")).isEmpty();
assertThat(filter("subEntity.strValue==subx and subEntity.intValue!=1")).hasSize(2).containsExactlyInAnyOrder(root1, root2);
assertThat(filter("subEntity.strValue==subx and subEntity.intValue==0")).hasSize(2).containsExactlyInAnyOrder(root1, root2);
assertThat(filter("subEntity.strValue==subx or subEntity.intValue==1")).hasSize(4).containsExactlyInAnyOrder(root1, root2, root3, root4);
if (getRsqlToSpecBuilder() != LEGACY_G1) {
// G1 doesn't get null entity in not
assertThat(filter("subEntity.strValue==subx or subEntity.intValue!=1")).hasSize(3).containsExactlyInAnyOrder(root1, root2, root5);
}
}
@Test
void pluralSubSetAttribute() {
final Sub sub = subRepository.save(new Sub().setStrValue("subX").setIntValue(0));
final Sub sub2 = subRepository.save(new Sub().setStrValue("subY").setIntValue(1));
final Sub sub3 = subRepository.save(new Sub().setStrValue("subY").setIntValue(0));
final Root root = rootRepository.save(new Root().setSubSet(Set.of(sub)));
final Sub sub1 = subRepository.save(new Sub().setStrValue("subx").setIntValue(0));
final Sub sub2 = subRepository.save(new Sub().setStrValue("suby").setIntValue(1));
final Sub sub3 = subRepository.save(new Sub().setStrValue("suby").setIntValue(0));
final Root root1 = rootRepository.save(new Root().setSubSet(Set.of(sub1)));
final Root root2 = rootRepository.save(new Root().setSubSet(Set.of(sub2)));
final Root root3 = rootRepository.save(new Root().setSubSet(Set.of(sub3)));
final Root root4 = rootRepository.save(new Root().setSubSet(Set.of(sub, sub2)));
final Root root5 = rootRepository.save(new Root().setSubSet(Set.of(sub, sub3)));
final Root root4 = rootRepository.save(new Root().setSubSet(Set.of(sub1, sub2)));
final Root root5 = rootRepository.save(new Root().setSubSet(Set.of(sub1, sub3)));
final Root root6 = rootRepository.save(new Root()); // no sub set
// by sub entity string
assertThat(filter("subSet.strValue==subX")).hasSize(3).containsExactlyInAnyOrder(root, root4, root5);
assertThat(filter("subSet.strValue==subx")).hasSize(3).containsExactlyInAnyOrder(root1, root4, root5);
assertThat(filter("subSet.strValue==nostr")).isEmpty();
assertThat(filter("subSet.strValue!=subX")).hasSize(3).containsExactlyInAnyOrder(root2, root3, root6);
assertThat(filter("subSet.strValue!=subx")).hasSize(3).containsExactlyInAnyOrder(root2, root3, root6);
assertThat(filter("subSet.strValue!=nostr")).hasSize(6);
assertThat(filter("subSet.strValue<subY")).hasSize(3).containsExactlyInAnyOrder(root, root4, root5);
assertThat(filter("subSet.strValue<=subY")).hasSize(5).containsExactlyInAnyOrder(root, root2, root3, root4, root5);
assertThat(filter("subSet.strValue>subX")).hasSize(4).containsExactlyInAnyOrder(root2, root3, root4, root5);
assertThat(filter("subSet.strValue>=subX")).hasSize(5).containsExactlyInAnyOrder(root, root2, root3, root4, root5);
assertThat(filter("subSet.strValue=in=subX")).hasSize(3).containsExactlyInAnyOrder(root, root4, root5);
assertThat(filter("subSet.strValue=in=(subX, subY)"))
.hasSize(5).containsExactlyInAnyOrder(root, root2, root3, root4, root5);
assertThat(filter("subSet.strValue<suby")).hasSize(3).containsExactlyInAnyOrder(root1, root4, root5);
assertThat(filter("subSet.strValue<=suby")).hasSize(5).containsExactlyInAnyOrder(root1, root2, root3, root4, root5);
assertThat(filter("subSet.strValue>subx")).hasSize(4).containsExactlyInAnyOrder(root2, root3, root4, root5);
assertThat(filter("subSet.strValue>=subx")).hasSize(5).containsExactlyInAnyOrder(root1, root2, root3, root4, root5);
assertThat(filter("subSet.strValue=in=subx")).hasSize(3).containsExactlyInAnyOrder(root1, root4, root5);
assertThat(filter("subSet.strValue=in=(subx, suby)")).hasSize(5).containsExactlyInAnyOrder(root1, root2, root3, root4, root5);
assertThat(filter("subSet.strValue=in=(subZ, subT)")).isEmpty();
assertThat(filter("subSet.strValue=out=subX")).hasSize(3).containsExactlyInAnyOrder(root2, root3, root6);
assertThat(filter("subSet.strValue=out=(subX, subY)")).hasSize(1).containsExactlyInAnyOrder(root6);
assertThat(filter("subSet.strValue=out=subx")).hasSize(3).containsExactlyInAnyOrder(root2, root3, root6);
assertThat(filter("subSet.strValue=out=(subx, suby)")).hasSize(1).containsExactlyInAnyOrder(root6);
// wildcard, like
assertThat(filter("subSet.strValue==sub*")).hasSize(5).containsExactlyInAnyOrder(root, root2, root3, root4, root5);
assertThat(filter("subSet.strValue==*bX")).hasSize(3).containsExactlyInAnyOrder(root, root4, root5);
assertThat(filter("subSet.strValue==sub*")).hasSize(5).containsExactlyInAnyOrder(root1, root2, root3, root4, root5);
assertThat(filter("subSet.strValue==*bx")).hasSize(3).containsExactlyInAnyOrder(root1, root4, root5);
assertThat(filter("subSet.strValue!=sub*")).hasSize(1).containsExactlyInAnyOrder(root6);
assertThat(filter("subSet.strValue!=*bX")).hasSize(3).containsExactlyInAnyOrder(root2, root3, root6);
assertThat(filter("subSet.strValue!=*bx")).hasSize(3).containsExactlyInAnyOrder(root2, root3, root6);
assertThat(filter("subSet.strValue==*")).hasSize(5).containsExactlyInAnyOrder(root1, root2, root3, root4, root5);
assertThat(filter("subSet.strValue!=*")).hasSize(1).containsExactlyInAnyOrder(root6);
if (getRsqlToSpecBuilder() != LEGACY_G1) {
// G1 treats it like same element shall match - which doesn't match to "has" semantic for sets
assertThat(filter("subSet.strValue==*bx and subSet.strValue==suby")).hasSize(2).containsExactlyInAnyOrder(root4, root5);
}
assertThat(filter("subSet.strValue==*bx and subSet.strValue!=subx")).isEmpty();
assertThat(filter("subSet.strValue==*bx and subSet.strValue!=suby")).hasSize(1).containsExactlyInAnyOrder(root1);
assertThat(filter("subSet.strValue==*bx or subSet.strValue==suby"))
.hasSize(5).containsExactlyInAnyOrder(root1, root2, root3, root4, root5);
if (getRsqlToSpecBuilder() != LEGACY_G1) {
// G1 doesn't get null - has which shall be included as doesn't have
assertThat(filter("subSet.strValue==*bx or subSet.strValue!=subx"))
.hasSize(6).containsExactlyInAnyOrder(root1, root2, root3, root4, root5, root6);
}
// by sub entity int
assertThat(filter("subSet.intValue==0")).hasSize(4).containsExactlyInAnyOrder(root, root3, root4, root5);
assertThat(filter("subSet.intValue==0")).hasSize(4).containsExactlyInAnyOrder(root1, root3, root4, root5);
assertThat(filter("subSet.intValue==2")).isEmpty();
assertThat(filter("subSet.intValue!=0")).hasSize(2).containsExactlyInAnyOrder(root2, root6);
assertThat(filter("subSet.intValue!=2")).hasSize(6);
assertThat(filter("subSet.intValue<1")).hasSize(4).containsExactlyInAnyOrder(root, root3, root4, root5);
assertThat(filter("subSet.intValue<=1")).hasSize(5).containsExactlyInAnyOrder(root, root2, root3, root4, root5);
if (getRsqlToSpecBuilder() == G3) {
// the legacy builders has different (and wrong semantic)
// G1 - has element != x
// G2 - has element != x or has no elements
// accompanying G3 it is (as it should be) semantic of set != x - has no element with value x (including nhas no elements)
assertThat(filter("subSet.intValue!=0")).hasSize(2).containsExactlyInAnyOrder(root2, root6);
}
if (getRsqlToSpecBuilder() != LEGACY_G1) {
// G1 doesn't get null - has which shall be included as doesn't have
assertThat(filter("subSet.intValue!=2")).hasSize(6);
}
assertThat(filter("subSet.intValue<1")).hasSize(4).containsExactlyInAnyOrder(root1, root3, root4, root5);
assertThat(filter("subSet.intValue<=1")).hasSize(5).containsExactlyInAnyOrder(root1, root2, root3, root4, root5);
assertThat(filter("subSet.intValue>0")).hasSize(2).containsExactlyInAnyOrder(root2, root4);
assertThat(filter("subSet.intValue>=0")).hasSize(5).containsExactlyInAnyOrder(root, root2, root3, root4, root5);
assertThat(filter("subSet.intValue=in=0")).hasSize(4).containsExactlyInAnyOrder(root, root3, root4, root5);
assertThat(filter("subSet.intValue=in=(0, 1)")).hasSize(5).containsExactlyInAnyOrder(root, root2, root3, root4, root5);
assertThat(filter("subSet.intValue>=0")).hasSize(5).containsExactlyInAnyOrder(root1, root2, root3, root4, root5);
assertThat(filter("subSet.intValue=in=0")).hasSize(4).containsExactlyInAnyOrder(root1, root3, root4, root5);
assertThat(filter("subSet.intValue=in=(0, 1)")).hasSize(5).containsExactlyInAnyOrder(root1, root2, root3, root4, root5);
assertThat(filter("subSet.intValue=in=(2, 3)")).isEmpty();
assertThat(filter("subSet.intValue=out=0")).hasSize(2).containsExactlyInAnyOrder(root2, root6);
assertThat(filter("subSet.intValue=out=(0, 1)")).hasSize(1).containsExactlyInAnyOrder(root6);
assertThat(filter("subSet.intValue==0 and subSet.intValue==0")).hasSize(4).containsExactlyInAnyOrder(root1, root3, root4, root5);
if (getRsqlToSpecBuilder() == G3) {
assertThat(filter("subSet.intValue==0 and subSet.intValue!=1")).hasSize(3).containsExactlyInAnyOrder(root1, root3, root5);
}
assertThat(filter("subSet.intValue==0 or subSet.intValue==1"))
.hasSize(5).containsExactlyInAnyOrder(root1, root2, root3, root4, root5);
if (getRsqlToSpecBuilder() != LEGACY_G1) {
// G1 doesn't get null - has which shall be included as doesn't have
assertThat(filter("subSet.intValue==0 or subSet.intValue!=1"))
.hasSize(5).containsExactlyInAnyOrder(root1, root3, root4, root5, root6);
}
if (getRsqlToSpecBuilder() != LEGACY_G1) {
// G1 treats it like same element shall match - which doesn't match to "has" semantic for sets
assertThat(filter("subSet.intValue==0 and subSet.strValue==suby")).hasSize(3).containsExactlyInAnyOrder(root3, root4, root5);
}
assertThat(filter("subSet.intValue==0 and subSet.strValue!=subx")).hasSize(1).containsExactlyInAnyOrder(root3);
assertThat(filter("subSet.intValue==0 and subSet.strValue!=suby")).hasSize(1).containsExactlyInAnyOrder(root1);
assertThat(filter("subSet.intValue==0 or subSet.strValue==suby"))
.hasSize(5).containsExactlyInAnyOrder(root1, root2, root3, root4, root5);
if (getRsqlToSpecBuilder() != LEGACY_G1) {
// G1 doesn't get null - has which shall be included as doesn't have
assertThat(filter("subSet.intValue==0 or subSet.strValue!=subx"))
.hasSize(6).containsExactlyInAnyOrder(root1, root2, root3, root4, root5, root6);
}
}
@Test
void pluralSubMapAttribute() {
final Root root = rootRepository.save(new Root().setSubMap(Map.of("x", "rootX", "y", "rootY")));
final Root root2 = rootRepository.save(new Root().setSubMap(Map.of("x", "rootX", "y", "rootX")));
final Root root3 = rootRepository.save(new Root().setSubMap(Map.of("x", "rootY", "y", "rootY")));
final Root root4 = rootRepository.save(new Root().setSubMap(Map.of("x", "rootY", "y", "rootX")));
final Root root5 = rootRepository.save(new Root().setSubMap(Map.of("x", "rootX")));
final Root root1 = rootRepository.save(new Root().setSubMap(Map.of("x", "rootx", "y", "rooty")));
final Root root2 = rootRepository.save(new Root().setSubMap(Map.of("x", "rootx", "y", "rootx")));
final Root root3 = rootRepository.save(new Root().setSubMap(Map.of("x", "rooty", "y", "rooty")));
final Root root4 = rootRepository.save(new Root().setSubMap(Map.of("x", "rooty", "y", "rootx")));
final Root root5 = rootRepository.save(new Root().setSubMap(Map.of("x", "rootx")));
final Root root6 = rootRepository.save(new Root()); // no sub map
assertThat(filter("subMap.x==rootX")).hasSize(3).containsExactlyInAnyOrder(root, root2, root5);
assertThat(filter("subMap.x==rootx")).hasSize(3).containsExactlyInAnyOrder(root1, root2, root5);
assertThat(filter("subMap.x==nostr")).isEmpty();
// TODO / recheck - when missing entity shall it be included or not in != or =out=? - now it's not
assertThat(filter("subMap.x!=rootX")).hasSize(2).containsExactlyInAnyOrder(root3, root4);
assertThat(filter("subMap.x!=nostr")).hasSize(5).containsExactlyInAnyOrder(root, root2, root3, root4, root5);
assertThat(filter("subMap.x<rootY")).hasSize(3).containsExactlyInAnyOrder(root, root2, root5);
assertThat(filter("subMap.x<=rootY")).hasSize(5).containsExactlyInAnyOrder(root, root2, root3, root4, root5);
assertThat(filter("subMap.x>rootX")).hasSize(2).containsExactlyInAnyOrder(root3, root4);
assertThat(filter("subMap.x>=rootX")).hasSize(5).containsExactlyInAnyOrder(root, root2, root3, root4, root5);
assertThat(filter("subMap.x=in=rootX")).hasSize(3).containsExactlyInAnyOrder(root, root2, root5);
assertThat(filter("subMap.x=in=(rootX, rootY)")).hasSize(5).containsExactlyInAnyOrder(root, root2, root3, root4, root5);
assertThat(filter("subMap.x=in=(rootZ, rootT)")).isEmpty();
assertThat(filter("subMap.x=out=rootX")).hasSize(2).containsExactlyInAnyOrder(root3, root4);
assertThat(filter("subMap.x=out=(rootX, rootY)")).isEmpty();
assertThat(filter("subMap.x!=rootx")).hasSize(2).containsExactlyInAnyOrder(root3, root4);
assertThat(filter("subMap.x!=nostr")).hasSize(5).containsExactlyInAnyOrder(root1, root2, root3, root4, root5);
assertThat(filter("subMap.x<rooty")).hasSize(3).containsExactlyInAnyOrder(root1, root2, root5);
assertThat(filter("subMap.x<=rooty")).hasSize(5).containsExactlyInAnyOrder(root1, root2, root3, root4, root5);
assertThat(filter("subMap.x>rootx")).hasSize(2).containsExactlyInAnyOrder(root3, root4);
assertThat(filter("subMap.x>=rootx")).hasSize(5).containsExactlyInAnyOrder(root1, root2, root3, root4, root5);
assertThat(filter("subMap.x=in=rootx")).hasSize(3).containsExactlyInAnyOrder(root1, root2, root5);
assertThat(filter("subMap.x=in=(rootx, rooty)")).hasSize(5).containsExactlyInAnyOrder(root1, root2, root3, root4, root5);
assertThat(filter("subMap.x=in=(rootz, roott)")).isEmpty();
assertThat(filter("subMap.x=out=rootx")).hasSize(2).containsExactlyInAnyOrder(root3, root4);
assertThat(filter("subMap.x=out=(rootx, rooty)")).isEmpty();
// wildcard, like
assertThat(filter("subMap.x==root*")).hasSize(5).containsExactlyInAnyOrder(root, root2, root3, root4, root5);
assertThat(filter("subMap.x==*tX")).hasSize(3).containsExactlyInAnyOrder(root, root2, root5);
assertThat(filter("subMap.x==root*")).hasSize(5).containsExactlyInAnyOrder(root1, root2, root3, root4, root5);
assertThat(filter("subMap.x==*tx")).hasSize(3).containsExactlyInAnyOrder(root1, root2, root5);
assertThat(filter("subMap.x!=root*")).isEmpty();
assertThat(filter("subMap.x!=*tX")).hasSize(2).containsExactlyInAnyOrder(root3, root4);
assertThat(filter("subMap.x!=*tx")).hasSize(2).containsExactlyInAnyOrder(root3, root4);
assertThat(filter("subMap.x==*")).hasSize(5).containsExactlyInAnyOrder(root1, root2, root3, root4, root5);
assertThat(filter("subMap.x!=*")).isEmpty();
if (getRsqlToSpecBuilder() != LEGACY_G1) {
// null checks
assertThat(filter("subMap.x=is=null")).hasSize(1).containsExactlyInAnyOrder(root6);
assertThat(filter("subMap.x=not=null")).hasSize(5).containsExactlyInAnyOrder(root1, root2, root3, root4, root5);
}
assertThat(filter("subMap.x==*tx and subMap.y==rooty")).hasSize(1).containsExactlyInAnyOrder(root1);
assertThat(filter("subMap.x==*tx and subMap.y!=rootx")).hasSize(1).containsExactlyInAnyOrder(root1);
assertThat(filter("subMap.x==*tx or subMap.x==rooty"))
.hasSize(5).containsExactlyInAnyOrder(root1, root2, root3, root4, root5);
assertThat(filter("subMap.x==*tx or subMap.x!=rootx"))
.hasSize(5).containsExactlyInAnyOrder(root1, root2, root3, root4, root5);
}
private List<Root> filter(final String rsql) {
// reference / auto filter (using elements and reflection)
final ReferenceMatcher matcher = ReferenceMatcher.ofRsql(rsql);
final List<Root> refResult = StreamSupport.stream(rootRepository.findAll().spliterator(), false).filter(matcher::match).toList();
final List<Root> result = rootRepository.findAll(builder.specification(RsqlParser.parse(rsql)));
final List<Root> result = rootRepository.findAll(getSpecification(rsql));
// auto check with reference result
try {
assertThat(result).containsExactlyInAnyOrder(refResult.toArray(Root[]::new));
@@ -246,6 +382,14 @@ class SpecificationBuilderTest {
return result;
}
protected Specification<Root> getSpecification(final String rsql) {
return builder.specification(RsqlParser.parse(rsql));
}
private static RsqlConfigHolder.RsqlToSpecBuilder getRsqlToSpecBuilder() {
return RsqlConfigHolder.getInstance().getRsqlToSpecBuilder();
}
@SpringBootConfiguration
static class Config {}
}