Skip to content

Commit b866582

Browse files
authored
Merge pull request #2123 from beyonnex-io/bugfix/rql-exists-in-nested-array
fix ExistsThingPredicateVisitor did not flatten arrays correctly
2 parents 3f8114f + acf2019 commit b866582

File tree

3 files changed

+89
-5
lines changed

3 files changed

+89
-5
lines changed

rql/query/src/main/java/org/eclipse/ditto/rql/query/things/ExistsThingPredicateVisitor.java

+7-3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.eclipse.ditto.placeholders.PlaceholderResolver;
2424
import org.eclipse.ditto.rql.query.expression.ExistsFieldExpression;
2525
import org.eclipse.ditto.rql.query.expression.visitors.ExistsFieldExpressionVisitor;
26+
import org.eclipse.ditto.things.model.Feature;
2627
import org.eclipse.ditto.things.model.Thing;
2728

2829
/**
@@ -85,7 +86,8 @@ public static Predicate<Thing> apply(final ExistsFieldExpression expression,
8586

8687
@Override
8788
public Predicate<Thing> visitAttribute(final String key) {
88-
return thing -> thing.getAttributes().map(attributes -> attributes.getValue(key).isPresent())
89+
return thing -> thing.getAttributes()
90+
.map(attributes -> attributes.getValueFlatteningArrays(key).isPresent())
8991
.orElse(false);
9092
}
9193

@@ -124,15 +126,17 @@ public Predicate<Thing> visitFeatureDesiredProperties(final CharSequence feature
124126
public Predicate<Thing> visitFeatureIdProperty(final String featureId, final String property) {
125127
return thing -> thing.getFeatures()
126128
.flatMap(features -> features.getFeature(featureId))
127-
.map(feature -> feature.getProperty(property).isPresent())
129+
.flatMap(Feature::getProperties)
130+
.map(feature -> feature.getValueFlatteningArrays(property).isPresent())
128131
.orElse(false);
129132
}
130133

131134
@Override
132135
public Predicate<Thing> visitFeatureIdDesiredProperty(final CharSequence featureId, final CharSequence property) {
133136
return thing -> thing.getFeatures()
134137
.flatMap(features -> features.getFeature(featureId.toString()))
135-
.map(feature -> feature.getDesiredProperty(property).isPresent())
138+
.flatMap(Feature::getDesiredProperties)
139+
.map(feature -> feature.getValueFlatteningArrays(property).isPresent())
136140
.orElse(false);
137141
}
138142

rql/query/src/test/java/org/eclipse/ditto/rql/query/things/ThingPredicatePredicateVisitorTest.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ public final class ThingPredicatePredicateVisitorTest {
3535

3636
private static final String KNOWN_PLACEHOLDER_VALUE = "baZingA";
3737

38-
private final static ThingPredicatePredicateVisitor sut = ThingPredicatePredicateVisitor.getInstance();
39-
private final static ThingPredicatePredicateVisitor sutWithPlaceholderResolver =
38+
private static final ThingPredicatePredicateVisitor sut = ThingPredicatePredicateVisitor.getInstance();
39+
private static final ThingPredicatePredicateVisitor sutWithPlaceholderResolver =
4040
ThingPredicatePredicateVisitor.createInstance(PlaceholderFactory.newPlaceholderResolver(
4141
new ThingPredicateTestPlaceholder(), KNOWN_PLACEHOLDER_VALUE));
4242

rql/query/src/test/java/org/eclipse/ditto/rql/query/things/ThingPredicateVisitorTest.java

+80
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import org.eclipse.ditto.base.model.entity.metadata.Metadata;
2323
import org.eclipse.ditto.base.model.exceptions.InvalidRqlExpressionException;
2424
import org.eclipse.ditto.base.model.headers.DittoHeaders;
25+
import org.eclipse.ditto.json.JsonArray;
26+
import org.eclipse.ditto.json.JsonObject;
2527
import org.eclipse.ditto.json.JsonPointer;
2628
import org.eclipse.ditto.json.JsonValue;
2729
import org.eclipse.ditto.placeholders.PlaceholderFactory;
@@ -70,6 +72,23 @@ public final class ThingPredicateVisitorTest {
7072
.setAttribute(JsonPointer.of("aDouble"), JsonValue.of(MATCHING_THING_DOUBLE))
7173
.setAttribute(JsonPointer.of("aBoolean"), JsonValue.of(MATCHING_THING_BOOLEAN))
7274
.setAttribute(JsonPointer.of("aString"), JsonValue.of(MATCHING_THING_STRING))
75+
.setAttribute(JsonPointer.of("anArrayOfObjects"), JsonArray.newBuilder()
76+
.add(JsonObject.newBuilder()
77+
.set("anInteger", MATCHING_THING_INTEGER)
78+
.set("aBoolean", MATCHING_THING_BOOLEAN)
79+
.set("anArrayOfObjects", JsonArray.newBuilder()
80+
.add(
81+
JsonObject.newBuilder()
82+
.set("anInteger", MATCHING_THING_INTEGER)
83+
.set("aBoolean", MATCHING_THING_BOOLEAN)
84+
.build()
85+
)
86+
.build()
87+
)
88+
.build()
89+
)
90+
.build()
91+
)
7392
.setFeature("foo", FeatureProperties.newBuilder()
7493
.set(JsonPointer.of("anInteger"), JsonValue.of(MATCHING_THING_INTEGER))
7594
.set(JsonPointer.of("aLong"), JsonValue.of(MATCHING_THING_LONG))
@@ -453,6 +472,67 @@ public void testFilterAttributeExists() {
453472
.isFalse();
454473
}
455474

475+
@Test
476+
public void testFilterAttributeNestedInArrayExists() {
477+
final Predicate<Thing> thingPredicate = createPredicate("exists(attributes/anArrayOfObjects/aBoolean)");
478+
assertThat(thingPredicate.test(MATCHING_THING))
479+
.as("Filtering 'exists(attributes/anArrayOfObjects/aBoolean)' should be true")
480+
.isTrue();
481+
482+
final Predicate<Thing> negativePredicate = createPredicate("exists(attributes/anArrayOfObjects/missing)");
483+
assertThat(negativePredicate.test(MATCHING_THING))
484+
.as("Filtering 'exists(attributes/anArrayOfObjects/missing)' should be false")
485+
.isFalse();
486+
}
487+
488+
@Test
489+
public void testFilterAttributeNestedInArrayNestedExists() {
490+
final Predicate<Thing> thingPredicate = createPredicate("exists(attributes/anArrayOfObjects/anArrayOfObjects/aBoolean)");
491+
assertThat(thingPredicate.test(MATCHING_THING))
492+
.as("Filtering 'exists(attributes/anArrayOfObjects/anArrayOfObjects/aBoolean)' should be true")
493+
.isTrue();
494+
495+
final Predicate<Thing> negativePredicate = createPredicate("exists(attributes/anArrayOfObjects/anArrayOfObjects/missing)");
496+
assertThat(negativePredicate.test(MATCHING_THING))
497+
.as("Filtering 'exists(attributes/anArrayOfObjects/anArrayOfObjects/missing)' should be false")
498+
.isFalse();
499+
}
500+
501+
@Test
502+
public void testFilterArrayAttributeWithNestedIntegerGe() {
503+
final String filter = "ge(attributes/anArrayOfObjects/anInteger," + (MATCHING_THING_INTEGER - 1) + ")";
504+
final Predicate<Thing> thingPredicate = createPredicate(filter);
505+
assertThat(thingPredicate.test(MATCHING_THING))
506+
.as("Filtering '" + filter + "' should be true")
507+
.isTrue();
508+
}
509+
510+
@Test
511+
public void testFilterArrayAttributeWithArrayNestedIntegerGe() {
512+
final String filter =
513+
"ge(attributes/anArrayOfObjects/anArrayOfObjects/anInteger," + (MATCHING_THING_INTEGER - 1) + ")";
514+
final Predicate<Thing> thingPredicate = createPredicate(filter);
515+
assertThat(thingPredicate.test(MATCHING_THING))
516+
.as("Filtering '" + filter + "' should be true")
517+
.isTrue();
518+
}
519+
520+
@Test
521+
public void testFilterArrayAttributeWithNestedBooleanEq() {
522+
final Predicate<Thing> thingPredicate = createPredicate("eq(attributes/anArrayOfObjects/aBoolean,true)");
523+
assertThat(thingPredicate.test(MATCHING_THING))
524+
.as("Filtering 'exists(attributes/anArrayOfObjects/aBoolean)' should be true")
525+
.isTrue();
526+
}
527+
528+
@Test
529+
public void testFilterArrayAttributeWithArrayNestedBooleanEq() {
530+
final Predicate<Thing> thingPredicate = createPredicate("eq(attributes/anArrayOfObjects/anArrayOfObjects/aBoolean,true)");
531+
assertThat(thingPredicate.test(MATCHING_THING))
532+
.as("Filtering 'exists(attributes/anArrayOfObjects/anArrayOfObjects/aBoolean)' should be true")
533+
.isTrue();
534+
}
535+
456536
@Test
457537
public void testFilterFeatureExists() {
458538
final Predicate<Thing> thingPredicate = createPredicate("exists(features/foo)");

0 commit comments

Comments
 (0)