diff --git a/src/main/antlr4/org/s1ck/gdl/GDL.g4 b/src/main/antlr4/org/s1ck/gdl/GDL.g4 index f85aed3..ab4bac6 100644 --- a/src/main/antlr4/org/s1ck/gdl/GDL.g4 +++ b/src/main/antlr4/org/s1ck/gdl/GDL.g4 @@ -197,6 +197,9 @@ intervalFunc | precedesOperator | succeedsOperator | containsOperator + | immediatelyPrecedesOperator + | immediatelySucceedsOperator + | equalsOperator ; overlapsIntervallOperator : 'overlaps(' interval ')' @@ -224,6 +227,18 @@ containsOperator | 'contains(' timePoint ')' ; +immediatelyPrecedesOperator + : 'immediatelyPrecedes(' interval ')' + ; + +immediatelySucceedsOperator + : 'immediatelySucceeds(' interval ')' + ; + +equalsOperator + : 'equals(' interval ')' + ; + stampFunc : beforePointOperator | afterPointOperator diff --git a/src/main/java/org/s1ck/gdl/GDLLoader.java b/src/main/java/org/s1ck/gdl/GDLLoader.java index 7f426bb..acfca34 100644 --- a/src/main/java/org/s1ck/gdl/GDLLoader.java +++ b/src/main/java/org/s1ck/gdl/GDLLoader.java @@ -36,8 +36,7 @@ import java.util.*; import java.util.stream.Collectors; -import static org.s1ck.gdl.utils.Comparator.GTE; -import static org.s1ck.gdl.utils.Comparator.LTE; +import static org.s1ck.gdl.utils.Comparator.*; class GDLLoader extends GDLBaseListener { @@ -476,27 +475,34 @@ private Predicate buildIntervalFunction(GDLParser.IntvFContext ctx) { * {@code from} and {@code to}. */ private Predicate createIntervalPredicates(TimePoint from, TimePoint to, GDLParser.IntervalFuncContext intervalFunc) { - Predicate predicate = null; if(intervalFunc.overlapsIntervallOperator()!=null){ - predicate = createOverlapsPredicates(from, to, intervalFunc.overlapsIntervallOperator()); + return createOverlapsPredicates(from, to, intervalFunc.overlapsIntervallOperator()); } else if(intervalFunc.fromToOperator()!=null){ - predicate = createFromToPredicates(from, to, intervalFunc.fromToOperator()); + return createFromToPredicates(from, to, intervalFunc.fromToOperator()); } else if(intervalFunc.betweenOperator()!=null){ - predicate = createBetweenPredicates(from, to, intervalFunc.betweenOperator()); + return createBetweenPredicates(from, to, intervalFunc.betweenOperator()); } else if(intervalFunc.precedesOperator()!=null){ - predicate = createPrecedesPredicates(to, intervalFunc.precedesOperator()); + return createPrecedesPredicates(to, intervalFunc.precedesOperator()); } else if(intervalFunc.succeedsOperator()!=null){ - predicate = createSucceedsPredicates(from, intervalFunc.succeedsOperator()); + return createSucceedsPredicates(from, intervalFunc.succeedsOperator()); } else if(intervalFunc.containsOperator()!=null){ - predicate = createContainsPredicates(from, to, intervalFunc.containsOperator()); + return createContainsPredicates(from, to, intervalFunc.containsOperator()); } - // additional constraints added during interval processing? - return predicate; + else if(intervalFunc.immediatelyPrecedesOperator()!=null){ + return createImmediatelyPrecedesPredicates(to, intervalFunc.immediatelyPrecedesOperator()); + } + else if(intervalFunc.immediatelySucceedsOperator()!=null){ + return createImmediatelySucceedsPredicates(from, intervalFunc.immediatelySucceedsOperator()); + } + else if(intervalFunc.equalsOperator()!=null){ + return createEqualsPredicates(from, to, intervalFunc.equalsOperator()); + } + return null; } /** @@ -552,14 +558,20 @@ private Predicate createBetweenPredicates(TimePoint from, TimePoint to, GDLParse * Creates a predicate a.precedes(b) = a <= b. * Function is used for interval and timestamp function {@code precedes}, as they both * only compare two time stamps - * @param point the time stamp of the caller to compare + * @param to the time stamp of the caller to compare * @param ctx the context containing the value to be compared * @return precedes predicate */ - private Predicate createPrecedesPredicates(TimePoint point, GDLParser.PrecedesOperatorContext ctx){ + private Predicate createPrecedesPredicates(TimePoint to, GDLParser.PrecedesOperatorContext ctx){ TimePoint[] arg = buildIntervall(ctx.interval()); TimePoint arg_from = arg[0]; - return new Comparison(point, LTE, arg_from); + return new Comparison(to, LTE, arg_from); + } + + private Predicate createImmediatelyPrecedesPredicates(TimePoint to, GDLParser.ImmediatelyPrecedesOperatorContext ctx){ + TimePoint[] arg = buildIntervall(ctx.interval()); + TimePoint arg_from = arg[0]; + return new Comparison(to, EQ, arg_from); } /** @@ -576,6 +588,13 @@ private Predicate createSucceedsPredicates(TimePoint point, GDLParser.SucceedsOp return new Comparison(point, GTE, arg_to); } + private Predicate createImmediatelySucceedsPredicates(TimePoint from, + GDLParser.ImmediatelySucceedsOperatorContext ctx){ + TimePoint[] arg = buildIntervall(ctx.interval()); + TimePoint arg_to = arg[1]; + return new Comparison(from, EQ, arg_to); + } + /** * Creates a predicate a.contains(b) = a.from<=b.from AND a.to>=b.to * @param from from value of the calling interval @@ -602,6 +621,16 @@ private Predicate createContainsPredicates(TimePoint from, TimePoint to, GDLPars } } + private Predicate createEqualsPredicates(TimePoint from, TimePoint to, GDLParser.EqualsOperatorContext ctx){ + TimePoint[] arg = buildIntervall(ctx.interval()); + TimePoint arg_from = arg[0]; + TimePoint arg_to = arg[1]; + return new And( + new Comparison(from, EQ, arg_from), + new Comparison(to, EQ, arg_to) + ); + } + /** * Creates an array {@code {from, to}} representing an intervall. * @param ctx context from which to derive {@code from} and {@code to} diff --git a/src/test/java/org/s1ck/gdl/GDLLoaderTemporalTest.java b/src/test/java/org/s1ck/gdl/GDLLoaderTemporalTest.java index e3458be..69bbcb2 100644 --- a/src/test/java/org/s1ck/gdl/GDLLoaderTemporalTest.java +++ b/src/test/java/org/s1ck/gdl/GDLLoaderTemporalTest.java @@ -454,6 +454,37 @@ public void comparisonTest(){ assertPredicateEquals(loader.getPredicates().get(), expected); } + @Test + public void immediatelyPrecedesTest(){ + GDLLoader loader = getLoaderFromGDLString("MATCH (a)-[e]->(b) " + + "WHERE a.tx.immediatelyPrecedes(e.val)"); + Predicate expected = new Comparison( + new TimeSelector("a", TX_TO), EQ, new TimeSelector("e", VAL_FROM) + ); + assertPredicateEquals(loader.getPredicates().get(), expected); + } + + @Test + public void immediatelySucceedsTest(){ + GDLLoader loader = getLoaderFromGDLString("MATCH (a)-[e]->(b) " + + "WHERE a.tx.immediatelySucceeds(e.val)"); + Predicate expected = new Comparison( + new TimeSelector("a", TX_FROM), EQ, new TimeSelector("e", VAL_TO) + ); + assertPredicateEquals(loader.getPredicates().get(), expected); + } + + @Test + public void equalsTest(){ + GDLLoader loader = getLoaderFromGDLString("MATCH (a)-[e]->(b) " + + "WHERE a.tx.equals(e.val)"); + Predicate expected = new And( + new Comparison(new TimeSelector("a", TX_FROM), EQ, new TimeSelector("e", VAL_FROM)), + new Comparison(new TimeSelector("a", TX_TO), EQ, new TimeSelector("e", VAL_TO)) + ); + assertPredicateEquals(loader.getPredicates().get(), expected); + } + /** * Does not fail iff {@code result==expected} or {@code result.switchSides()==expected} * @param result predicate to compare