diff --git a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/query/Condition.java b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/query/Condition.java index 09e223e4ca..e65179df55 100644 --- a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/query/Condition.java +++ b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/query/Condition.java @@ -195,7 +195,7 @@ private static boolean equals(final Object first, * * @param first is actual value, might be Number/Date or String, It is * probably that the `first` is serialized to String. - * @param second is value in query condition, must be Number/Date + * @param second is value in query condition, must be Number/Date/Boolean * @return the value 0 if first is numerically equal to second; * a value less than 0 if first is numerically less than * second; and a value greater than 0 if first is @@ -208,6 +208,8 @@ private static int compare(final Object first, final Object second) { (Number) second); } else if (second instanceof Date) { return compareDate(first, (Date) second); + } else if (second instanceof Boolean) { + return compareBoolean(first, (Boolean) second); } throw new IllegalArgumentException(String.format( @@ -230,6 +232,20 @@ private static int compareDate(Object first, Date second) { second, second.getClass().getSimpleName())); } + private static int compareBoolean(Object first, Boolean second) { + if (first == null) { + first = false; + } + if (first instanceof Boolean) { + return Boolean.compare((Boolean) first, second); + } + + throw new IllegalArgumentException(String.format( + "Can't compare between %s(%s) and %s(%s)", + first, first.getClass().getSimpleName(), + second, second.getClass().getSimpleName())); + } + private void checkBaseType(Object value, Class clazz) { if (!clazz.isInstance(value)) { String valueClass = value == null ? "null" : diff --git a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/query/ConditionQueryFlatten.java b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/query/ConditionQueryFlatten.java index 83af41b008..3208c0549c 100644 --- a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/query/ConditionQueryFlatten.java +++ b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/query/ConditionQueryFlatten.java @@ -265,7 +265,7 @@ private static List flattenRelations(ConditionQuery query) { cq.query(nonRelations); return ImmutableList.of(cq); } - return ImmutableList.of(query); + return ImmutableList.of(); } private static Relations optimizeRelations(Relations relations) { @@ -427,9 +427,26 @@ private static boolean validRange(Relation low, Relation high) { if (low == null || high == null) { return true; } - return compare(low, high) < 0 || compare(low, high) == 0 && - low.relation() == Condition.RelationType.GTE && - high.relation() == Condition.RelationType.LTE; + int compared = compare(low, high); + if (compared > 0) { + return false; + } + if (compared == 0) { + return low.relation() == Condition.RelationType.GTE && + high.relation() == Condition.RelationType.LTE; + } + return !emptyBooleanRange(low, high); + } + + private static boolean emptyBooleanRange(Relation low, Relation high) { + if (!(low.value() instanceof Boolean) || + !(high.value() instanceof Boolean)) { + return false; + } + return Boolean.FALSE.equals(low.value()) && + Boolean.TRUE.equals(high.value()) && + low.relation() == Condition.RelationType.GT && + high.relation() == Condition.RelationType.LT; } private static boolean validEq(Relation eq, Relation low, Relation high) { @@ -507,6 +524,9 @@ private static int compare(Relation first, Relation second) { return NumericUtil.compareNumber(firstValue, (Number) secondValue); } else if (firstValue instanceof Date && secondValue instanceof Date) { return ((Date) firstValue).compareTo((Date) secondValue); + } else if (firstValue instanceof Boolean && + secondValue instanceof Boolean) { + return Boolean.compare((Boolean) firstValue, (Boolean) secondValue); } else { throw new IllegalArgumentException(String.format("Can't compare between %s and %s", first, second)); diff --git a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/traversal/optimize/TraversalUtil.java b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/traversal/optimize/TraversalUtil.java index 11a5c0cee4..c61504063e 100644 --- a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/traversal/optimize/TraversalUtil.java +++ b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/traversal/optimize/TraversalUtil.java @@ -46,6 +46,7 @@ import org.apache.hugegraph.structure.HugeElement; import org.apache.hugegraph.structure.HugeProperty; import org.apache.hugegraph.type.HugeType; +import org.apache.hugegraph.type.define.DataType; import org.apache.hugegraph.type.define.Directions; import org.apache.hugegraph.type.define.HugeKeys; import org.apache.hugegraph.util.CollectionUtil; @@ -419,9 +420,9 @@ public static Condition convOr(HugeGraph graph, return cond; } - private static Condition.Relation convCompare2Relation(HugeGraph graph, - HugeType type, - HasContainer has) { + private static Condition convCompare2Relation(HugeGraph graph, + HugeType type, + HasContainer has) { assert type.isGraph(); BiPredicate bp = has.getPredicate().getBiPredicate(); assert bp instanceof Compare; @@ -459,9 +460,9 @@ private static Condition.Relation convCompare2SyspropRelation(HugeGraph graph, } } - private static Condition.Relation convCompare2UserpropRelation(HugeGraph graph, - HugeType type, - HasContainer has) { + private static Condition convCompare2UserpropRelation(HugeGraph graph, + HugeType type, + HasContainer has) { BiPredicate bp = has.getPredicate().getBiPredicate(); assert bp instanceof Compare; @@ -469,6 +470,11 @@ private static Condition.Relation convCompare2UserpropRelation(HugeGraph graph, PropertyKey pkey = graph.propertyKey(key); Id pkeyId = pkey.id(); Object value = validPropertyValue(has.getValue(), pkey); + if (pkey.dataType() == DataType.BOOLEAN && + value instanceof Boolean) { + return convCompare2BooleanUserpropRelation((Compare) bp, pkeyId, + (Boolean) value); + } switch ((Compare) bp) { case eq: @@ -488,6 +494,36 @@ private static Condition.Relation convCompare2UserpropRelation(HugeGraph graph, } } + private static Condition convCompare2BooleanUserpropRelation(Compare compare, + Id key, + Boolean value) { + switch (compare) { + case eq: + return Condition.eq(key, value); + case neq: + return Condition.neq(key, value); + case gt: + return value ? impossibleBooleanCondition(key) : + Condition.eq(key, true); + case gte: + return value ? Condition.eq(key, true) : + Condition.in(key, ImmutableList.of(false, true)); + case lt: + return value ? Condition.eq(key, false) : + impossibleBooleanCondition(key); + case lte: + return value ? Condition.in(key, ImmutableList.of(false, true)) : + Condition.eq(key, false); + default: + throw new AssertionError(compare); + } + } + + private static Condition impossibleBooleanCondition(Id key) { + return Condition.and(Condition.eq(key, false), + Condition.eq(key, true)); + } + private static Condition convRelationType2Relation(HugeGraph graph, HugeType type, HasContainer has) { diff --git a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/EdgeCoreTest.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/EdgeCoreTest.java index 265d408742..efa3ded95f 100644 --- a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/EdgeCoreTest.java +++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/EdgeCoreTest.java @@ -7117,6 +7117,95 @@ public void testQueryEdgeWithNullablePropertyInCompositeIndex() { Assert.assertEquals(1, (int) el.get(0).value("id")); } + @Test + public void testQueryEdgeByBooleanRangePredicate() { + HugeGraph graph = graph(); + initStrikeIndex(); + graph.schema().indexLabel("strikeByArrested").onE("strike").secondary() + .by("arrested").create(); + + Vertex louise = graph.addVertex(T.label, "person", "name", "Louise", + "city", "Beijing", "age", 21); + Vertex sean = graph.addVertex(T.label, "person", "name", "Sean", + "city", "Beijing", "age", 23); + long current = System.currentTimeMillis(); + louise.addEdge("strike", sean, "id", 1, + "timestamp", current, "place", "park", + "tool", "shovel", "reason", "jeer", + "arrested", false); + louise.addEdge("strike", sean, "id", 2, + "timestamp", current + 1, "place", "street", + "tool", "shovel", "reason", "jeer", + "arrested", true); + + List hasLtEdges = graph.traversal().E() + .has("arrested", P.lt(true)) + .toList(); + Assert.assertEquals(1, hasLtEdges.size()); + Assert.assertEquals(1, (int) hasLtEdges.get(0).value("id")); + + List whereEdges = graph.traversal().E() + .where(__.has("arrested", P.lt(true))) + .toList(); + Assert.assertEquals(1, whereEdges.size()); + Assert.assertEquals(1, (int) whereEdges.get(0).value("id")); + + List matchEdges = graph.traversal().E() + .match(__.as("start") + .where(__.has("arrested", + P.lt(true))) + .as("matched")) + .select("matched") + .toList(); + Assert.assertEquals(1, matchEdges.size()); + Assert.assertEquals(1, (int) matchEdges.get(0).value("id")); + + List hasLteFalseEdges = graph.traversal().E() + .has("arrested", P.lte(false)) + .toList(); + Assert.assertEquals(1, hasLteFalseEdges.size()); + Assert.assertEquals(1, (int) hasLteFalseEdges.get(0).value("id")); + + List hasGtFalseEdges = graph.traversal().E() + .has("arrested", P.gt(false)) + .toList(); + Assert.assertEquals(1, hasGtFalseEdges.size()); + Assert.assertEquals(2, (int) hasGtFalseEdges.get(0).value("id")); + + List hasGteTrueEdges = graph.traversal().E() + .has("arrested", P.gte(true)) + .toList(); + Assert.assertEquals(1, hasGteTrueEdges.size()); + Assert.assertEquals(2, (int) hasGteTrueEdges.get(0).value("id")); + + List hasGteFalseEdges = graph.traversal().E() + .has("arrested", P.gte(false)) + .toList(); + Assert.assertEquals(2, hasGteFalseEdges.size()); + Set gteFalseIds = new HashSet<>(); + for (Edge edge : hasGteFalseEdges) { + gteFalseIds.add(edge.value("id")); + } + Assert.assertEquals(ImmutableSet.of(1, 2), gteFalseIds); + + List hasLteTrueEdges = graph.traversal().E() + .has("arrested", P.lte(true)) + .toList(); + Assert.assertEquals(2, hasLteTrueEdges.size()); + Set lteTrueIds = new HashSet<>(); + for (Edge edge : hasLteTrueEdges) { + lteTrueIds.add(edge.value("id")); + } + Assert.assertEquals(ImmutableSet.of(1, 2), lteTrueIds); + + Assert.assertEquals(0, graph.traversal().E() + .has("arrested", P.lt(false)) + .toList().size()); + Assert.assertEquals(0, graph.traversal().E() + .has("arrested", P.gt(true)) + .toList().size()); + } + @Test public void testQueryEdgeByPage() { Assume.assumeTrue("Not support paging", diff --git a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/core/ConditionQueryFlattenTest.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/core/ConditionQueryFlattenTest.java index 627dcb29aa..dbe5506480 100644 --- a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/core/ConditionQueryFlattenTest.java +++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/core/ConditionQueryFlattenTest.java @@ -256,5 +256,48 @@ public void testFlattenWithNotIn() { Collection actual = queries.iterator().next().conditions(); Assert.assertEquals(expect, actual); } + + @Test + public void testFlattenWithBooleanRangeUpperBound() { + Id key = IdGenerator.of("c1"); + + ConditionQuery query = new ConditionQuery(HugeType.VERTEX); + query.query(Condition.lt(key, true)); + query.query(Condition.lt(key, false)); + + List queries = ConditionQueryFlatten.flatten(query); + Assert.assertEquals(1, queries.size()); + + Collection actual = queries.iterator().next().conditions(); + Assert.assertEquals(ImmutableList.of(Condition.lt(key, false)), actual); + } + + @Test + public void testFlattenWithBooleanRangeWindow() { + Id key = IdGenerator.of("c1"); + + ConditionQuery query = new ConditionQuery(HugeType.VERTEX); + query.query(Condition.gte(key, false)); + query.query(Condition.lt(key, true)); + + List queries = ConditionQueryFlatten.flatten(query); + Assert.assertEquals(1, queries.size()); + + Collection actual = queries.iterator().next().conditions(); + Assert.assertEquals(ImmutableList.of(Condition.gte(key, false), + Condition.lt(key, true)), actual); + } + + @Test + public void testFlattenWithConflictingBooleanRange() { + Id key = IdGenerator.of("c1"); + + ConditionQuery query = new ConditionQuery(HugeType.VERTEX); + query.query(Condition.gt(key, false)); + query.query(Condition.lt(key, true)); + + List queries = ConditionQueryFlatten.flatten(query); + Assert.assertEquals(0, queries.size()); + } } diff --git a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/core/ConditionTest.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/core/ConditionTest.java index e333c7a98b..485c3d6c23 100644 --- a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/core/ConditionTest.java +++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/core/ConditionTest.java @@ -293,6 +293,32 @@ public void testConditionLte() { }); } + @Test + public void testConditionBooleanRange() { + Condition lt = Condition.lt(HugeKeys.ID, true); + Assert.assertTrue(lt.test(false)); + Assert.assertFalse(lt.test(true)); + + Condition lte = Condition.lte(HugeKeys.ID, false); + Assert.assertTrue(lte.test(false)); + Assert.assertFalse(lte.test(true)); + + Condition gt = Condition.gt(HugeKeys.ID, false); + Assert.assertTrue(gt.test(true)); + Assert.assertFalse(gt.test(false)); + + Condition gte = Condition.gte(HugeKeys.ID, true); + Assert.assertTrue(gte.test(true)); + Assert.assertFalse(gte.test(false)); + + Assert.assertThrows(IllegalArgumentException.class, () -> { + Condition.lt(HugeKeys.ID, true).test(1); + }, e -> { + String err = "Can't compare between 1(Integer) and true(Boolean)"; + Assert.assertEquals(err, e.getMessage()); + }); + } + @Test public void testConditionNeq() { Condition c1 = Condition.neq(HugeKeys.ID, 123); diff --git a/hugegraph-struct/src/main/java/org/apache/hugegraph/query/Condition.java b/hugegraph-struct/src/main/java/org/apache/hugegraph/query/Condition.java index 5c7d3e221c..0b97c95c8a 100644 --- a/hugegraph-struct/src/main/java/org/apache/hugegraph/query/Condition.java +++ b/hugegraph-struct/src/main/java/org/apache/hugegraph/query/Condition.java @@ -437,7 +437,7 @@ private static boolean equals(final Object first, * * @param first is actual value, might be Number/Date or String, It is * probably that the `first` is serialized to String. - * @param second is value in query condition, must be Number/Date + * @param second is value in query condition, must be Number/Date/Boolean * @return the value 0 if first is numerically equal to second; * a value less than 0 if first is numerically less than * second; and a value greater than 0 if first is @@ -450,6 +450,8 @@ private static int compare(final Object first, final Object second) { (Number) second); } else if (second instanceof Date) { return compareDate(first, (Date) second); + } else if (second instanceof Boolean) { + return compareBoolean(first, (Boolean) second); } throw new IllegalArgumentException(String.format( @@ -472,6 +474,20 @@ private static int compareDate(Object first, Date second) { second, second.getClass().getSimpleName())); } + private static int compareBoolean(Object first, Boolean second) { + if (first == null) { + first = false; + } + if (first instanceof Boolean) { + return Boolean.compare((Boolean) first, second); + } + + throw new IllegalArgumentException(String.format( + "Can't compare between %s(%s) and %s(%s)", + first, first.getClass().getSimpleName(), + second, second.getClass().getSimpleName())); + } + public static List tokenize(String str) { final ArrayList tokens = new ArrayList<>(); int previous = 0;