diff --git a/src/main/antlr4/org/s1ck/gdl/GDL.g4 b/src/main/antlr4/org/s1ck/gdl/GDL.g4 index 00066a6..cb17c08 100644 --- a/src/main/antlr4/org/s1ck/gdl/GDL.g4 +++ b/src/main/antlr4/org/s1ck/gdl/GDL.g4 @@ -153,6 +153,7 @@ interval intervalSelector : Identifier '.' IntervalConst + | IntervalConst ; intervalFromStamps @@ -179,6 +180,7 @@ timeLiteral timeSelector : Identifier '.' TimeProp + | TimeProp ; intervalFunc diff --git a/src/main/java/org/s1ck/gdl/GDLLoader.java b/src/main/java/org/s1ck/gdl/GDLLoader.java index cb30159..2d18bab 100644 --- a/src/main/java/org/s1ck/gdl/GDLLoader.java +++ b/src/main/java/org/s1ck/gdl/GDLLoader.java @@ -312,9 +312,7 @@ public void exitGraph(GDLParser.GraphContext ctx) { */ @Override public void exitQuery(GDLParser.QueryContext ctx) { - if(predicates!=null) { - predicates = Predicate.unfoldTemporalComparisons(predicates); - } + postprocessPredicates(); for(Vertex v : vertices) { addPredicates(Predicate.fromGraphElement(v, getDefaultVertexLabel())); } @@ -323,6 +321,24 @@ public void exitQuery(GDLParser.QueryContext ctx) { } } + /** + * Reformulates the predicates when leaving the query. First, complex temporal expressions + * like {@code MIN(t1,t2) vars = new ArrayList<>(); + vars.addAll(userEdgeCache.keySet()); + vars.addAll(userVertexCache.keySet()); + vars.addAll(autoEdgeCache.keySet()); + vars.addAll(autoVertexCache.keySet()); + predicates = Predicate.translateGlobalPredicates(predicates, vars, true); + } + } + /** * Called when parser enters a vertex context. * @@ -537,7 +553,8 @@ private TimePoint[] buildIntervall(GDLParser.IntervalContext ctx) { if (ctx.intervalSelector()!=null){ GDLParser.IntervalSelectorContext selector = ctx.intervalSelector(); // throws exception, if variable invalid - String var = resolveIdentifier(selector.Identifier().getText()); + String var = selector.Identifier()!=null ? + resolveIdentifier(selector.Identifier().getText()) : TimeSelector.GLOBAL_SELECTOR; String intId = selector.IntervalConst().getText(); TimePoint from = new TimeSelector(var, intId+"_from"); TimePoint to = new TimeSelector(var, intId+"_to"); @@ -629,7 +646,8 @@ private TimePoint buildTimePoint(GDLParser.TimePointContext ctx) { else if (ctx.timeSelector()!=null){ GDLParser.TimeSelectorContext ts = ctx.timeSelector(); // checks whether ID is even there (is a vertex or edge) and returns its variable - String var = resolveIdentifier(ts.Identifier().getText()); + String var = ts.Identifier()!=null ? + resolveIdentifier(ts.Identifier().getText()) : TimeSelector.GLOBAL_SELECTOR; String field = ts.TimeProp().getText(); return new TimeSelector(var, field); } @@ -665,7 +683,7 @@ private Predicate createDefaultAsOf(){ vars.addAll(userVertexCache.keySet()); vars.addAll(autoEdgeCache.keySet()); vars.addAll(autoVertexCache.keySet()); - if(vars==null){ + if(vars.isEmpty()){ return null; } else{ diff --git a/src/main/java/org/s1ck/gdl/model/comparables/time/PlusTimePoint.java b/src/main/java/org/s1ck/gdl/model/comparables/time/PlusTimePoint.java index 3ed8c3c..9e5625f 100644 --- a/src/main/java/org/s1ck/gdl/model/comparables/time/PlusTimePoint.java +++ b/src/main/java/org/s1ck/gdl/model/comparables/time/PlusTimePoint.java @@ -1,8 +1,15 @@ package org.s1ck.gdl.model.comparables.time; +import org.s1ck.gdl.model.comparables.ComparableExpression; import org.s1ck.gdl.model.comparables.time.util.TimeConstant; +import org.s1ck.gdl.model.predicates.Predicate; +import org.s1ck.gdl.model.predicates.expressions.Comparison; +import org.s1ck.gdl.utils.Comparator; import java.util.ArrayList; +import java.util.List; + +import static org.s1ck.gdl.utils.Comparator.EQ; /** * Represents an addition of a constant to a given TimePoint @@ -78,4 +85,9 @@ public boolean equals(Object o) { } + @Override + public Predicate unfoldGlobal(Comparator comp, ComparableExpression rhs, List variables) { + //TODO implement, if necessary. Practically the same as in TimeSelector + return null; + } } diff --git a/src/main/java/org/s1ck/gdl/model/comparables/time/TimeAtom.java b/src/main/java/org/s1ck/gdl/model/comparables/time/TimeAtom.java index 0d33fd2..9900952 100644 --- a/src/main/java/org/s1ck/gdl/model/comparables/time/TimeAtom.java +++ b/src/main/java/org/s1ck/gdl/model/comparables/time/TimeAtom.java @@ -1,14 +1,29 @@ package org.s1ck.gdl.model.comparables.time; +import org.s1ck.gdl.model.comparables.ComparableExpression; import org.s1ck.gdl.model.predicates.Predicate; import org.s1ck.gdl.model.predicates.expressions.Comparison; import org.s1ck.gdl.utils.Comparator; +import java.util.List; + /** * Base class for atoms in a {@link TimeTerm}, e.g. simple timestamps */ public abstract class TimeAtom extends TimePoint { + + /** + * Translates a simple comparison described by {@code this comp rhs} to a equivalent + * comparison that does not contain global time selectors / intervals anymore. + * @param comp the comparator of the comparison + * @param rhs the right hand side of the comparison + * @param variables all the variables in the query + * @return equivalent containing only local selectors/intervals + */ + public abstract Predicate unfoldGlobal(Comparator comp, ComparableExpression rhs, List variables); + + @Override public Predicate unfoldComparison(Comparator comparator, TimePoint arg){ // nothing to unfold here diff --git a/src/main/java/org/s1ck/gdl/model/comparables/time/TimeLiteral.java b/src/main/java/org/s1ck/gdl/model/comparables/time/TimeLiteral.java index ba1f56a..ee44d86 100644 --- a/src/main/java/org/s1ck/gdl/model/comparables/time/TimeLiteral.java +++ b/src/main/java/org/s1ck/gdl/model/comparables/time/TimeLiteral.java @@ -1,9 +1,12 @@ package org.s1ck.gdl.model.comparables.time; +import org.s1ck.gdl.model.comparables.ComparableExpression; +import org.s1ck.gdl.model.predicates.Predicate; import org.s1ck.gdl.model.predicates.expressions.Comparison; import java.time.*; import java.util.ArrayList; +import java.util.List; import org.s1ck.gdl.utils.Comparator; @@ -170,4 +173,8 @@ private String preprocessDateString(String date){ return date; } + @Override + public Predicate unfoldGlobal(Comparator comp, ComparableExpression rhs, List variables) { + return new Comparison(this, comp, rhs); + } } diff --git a/src/main/java/org/s1ck/gdl/model/comparables/time/TimeSelector.java b/src/main/java/org/s1ck/gdl/model/comparables/time/TimeSelector.java index ea390f7..1bc7904 100644 --- a/src/main/java/org/s1ck/gdl/model/comparables/time/TimeSelector.java +++ b/src/main/java/org/s1ck/gdl/model/comparables/time/TimeSelector.java @@ -2,10 +2,15 @@ import org.s1ck.gdl.model.comparables.ComparableExpression; import org.s1ck.gdl.model.predicates.Predicate; +import org.s1ck.gdl.model.predicates.booleans.And; +import org.s1ck.gdl.model.predicates.booleans.Or; import org.s1ck.gdl.model.predicates.expressions.Comparison; import org.s1ck.gdl.utils.Comparator; import java.util.ArrayList; +import java.util.List; + +import static org.s1ck.gdl.utils.Comparator.*; /** * Represents a timestamp selection of a graph variable, e.g. v.VAL_FROM selects the VAL_FROM value of a graph element v @@ -22,6 +27,11 @@ public class TimeSelector extends TimeAtom{ */ private TimeField timeProp; + /** + * Variable name that indicates a global time selector, referring to the whole pattern + */ + public final static String GLOBAL_SELECTOR = "___global"; + /** * * All time properties defined by TPGM @@ -43,6 +53,24 @@ public TimeSelector(String variable, TimeField field){ timeProp = field; } + /** + * Initializes a global TimeSelector given a time property (VAL_FROM, VAL_TO, TX_FROM, TX_TO) + * @param field the time property as defined by the TPGM + */ + public TimeSelector(TimeField field){ + this(GLOBAL_SELECTOR, field); + } + + /** + * Initializes a global TimeSelector given the string representation of a time property + * (VAL_FROM, VAL_TO, TX_FROM, TX_TO) + * @param field the time property as defined by the TPGM. Must be one of "val_from", "val_to", "tx_from", "tx_to" + * (cases irrelevant) + */ + public TimeSelector(String field){ + this(GLOBAL_SELECTOR, field); + } + /** * Initializes a TimeSelector given a variable and the string representation of a time property * (VAL_FROM, VAL_TO, TX_FROM, TX_TO) @@ -94,9 +122,181 @@ public boolean containsSelectorType(TimeSelector.TimeField type){ return timeProp.equals(type); } + @Override + public Predicate unfoldGlobal(Comparator comp, ComparableExpression rhs, List variables) { + if(!variable.equals(GLOBAL_SELECTOR)){ + return new Comparison(this, comp, rhs); + } + if(comp.equals(EQ)){ + return unfoldGlobalEQ(rhs, variables); + } + else if(comp.equals(Comparator.NEQ)){ + return unfoldGlobalNEQ(rhs, variables); + } + else if(comp.equals(Comparator.LT)){ + return unfoldGlobalLT(rhs, variables); + } + else if(comp.equals(Comparator.LTE)){ + return unfoldGlobalLTE(rhs, variables); + } + else if(comp.equals(Comparator.GT)){ + return unfoldGlobalGT(rhs, variables); + } + else if(comp.equals(Comparator.GTE)){ + return unfoldGlobalGTE(rhs, variables); + } + return null; + } + + /** + * Translates a comparison {@code (this == rhs)} into an equivalent predicate that does not contain + * global time selectors/intervals anymore + * @param rhs the right hand side of the comparison to translate + * @param variables all query variables + * @return translated comparison + */ + private Predicate unfoldGlobalEQ(ComparableExpression rhs, List variables){ + // exists var: var.from==rhs + Predicate exists = existsVariable(EQ, rhs, variables); + + if(timeProp.equals(TimeField.TX_FROM) || timeProp.equals(TimeField.VAL_FROM)){ + //globalfrom==rhs <=> (exists var: var.from==rhs) AND (forall var: var.from<=rhs) + return new And(exists,forAllVariables(LTE, rhs, variables)); + } + + else{ + //globalto == rhs <=> (exists var: var.to ==rhs) AND (forall var: var.to>=rhs) + return new And(exists,forAllVariables(GTE, rhs, variables)); + } + } + + /** + * Translates a comparison {@code (this != rhs)} into an equivalent predicate that does not contain + * global time selectors/intervals anymore. + * @param rhs the right hand side of the comparison to translate + * @param variables all query variables + * @return translated comparison + */ + private Predicate unfoldGlobalNEQ(ComparableExpression rhs, List variables){ + return forAllVariables(NEQ, rhs, variables); + } + + /** + * Translates a comparison {@code (this < rhs)} into an equivalent predicate that does not contain + * global time selectors/intervals anymore. + * @param rhs the right hand side of the comparison to translate + * @param variables all query variables + * @return translated comparison + */ + private Predicate unfoldGlobalLT(ComparableExpression rhs, List variables){ + if(timeProp.equals(TimeField.TX_FROM) || timeProp.equals(TimeField.VAL_FROM)){ + // globalfrom < rhs <=> forall var: var.from < rhs + return forAllVariables(LT, rhs, variables); + } + else{ + //globalto < rhs <=> exists var: var.to < rhs + return existsVariable(LT, rhs, variables); + } + } + + /** + * Translates a comparison {@code (this <= rhs)} into an equivalent predicate that does not contain + * global time selectors/intervals anymore. + * @param rhs the right hand side of the comparison to translate + * @param variables all query variables + * @return translated comparison + */ + private Predicate unfoldGlobalLTE(ComparableExpression rhs, List variables){ + if(timeProp.equals(TimeField.TX_FROM) || timeProp.equals(TimeField.VAL_FROM)){ + // globalfrom <= rhs <=> forall var: var.from <= rhs + return forAllVariables(LTE, rhs, variables); + } + else{ + //globalto <= rhs <=> exists var: var.to <= rhs + return existsVariable(LTE, rhs, variables); + } + } + + /** + * Translates a comparison {@code (this > rhs)} into an equivalent predicate that does not contain + * global time selectors/intervals anymore. + * @param rhs the right hand side of the comparison to translate + * @param variables all query variables + * @return translated comparison + */ + private Predicate unfoldGlobalGT(ComparableExpression rhs, List variables){ + if(timeProp.equals(TimeField.TX_FROM) || timeProp.equals(TimeField.VAL_FROM)){ + // globalfrom > rhs <=> exists var: var.from > rhs + return existsVariable(GT, rhs, variables); + } + else{ + //globalto > rhs <=> forall var: var.to > rhs + return forAllVariables(GT, rhs, variables); + } + } + + /** + * Translates a comparison {@code (this >= rhs)} into an equivalent predicate that does not contain + * global time selectors/intervals anymore. + * @param rhs the right hand side of the comparison to translate + * @param variables all query variables + * @return translated comparison + */ + private Predicate unfoldGlobalGTE(ComparableExpression rhs, List variables){ + if(timeProp.equals(TimeField.TX_FROM) || timeProp.equals(TimeField.VAL_FROM)){ + // globalfrom >= rhs <=> exists var: var.from >= rhs + return existsVariable(GTE, rhs, variables); + } + else{ + //globalto >= rhs <=> forall var: var.to >= rhs + return forAllVariables(GTE, rhs, variables); + } + } + + /** + * Returns a predicate equivalent to {@code exists v in variables s.t. (v comp rhs) holds} + * @param comp the comparator + * @param rhs the rhs in the comparison + * @param variables the query variables to "iterate" over (the domain) + * @return predicate equivalent to {@code exists v in variables s.t. (v comp rhs) holds} + */ + private Predicate existsVariable(Comparator comp, ComparableExpression rhs, List variables){ + Comparison c0 = new Comparison(new TimeSelector(variables.get(0),timeProp), comp, rhs); + if(variables.size()==1){ + return c0; + } + Or exists = new Or(c0, new Comparison(new TimeSelector(variables.get(1),timeProp), comp, rhs)); + for(int i=2; i variables){ + Comparison c0 = new Comparison(new TimeSelector(variables.get(0),timeProp), comp, rhs); + if(variables.size()==1){ + return c0; + } + + And forall = new And(c0, new Comparison(new TimeSelector(variables.get(1),timeProp), comp, rhs)); + for(int i=2; i(b)}, the global predicate {@code tx_to >= 2020-04-28)} would be + * reduced to {@code a.tx_to>=2020-04-28 AND b.tx_to>=2020-04-28} + * while {@code tx_to <= 2020-04-28)} is reduced to + * {@code @code a.tx_to<=2020-04-28 OR b.tx_to<=2020-04-28} + * @param predicate the predicate whose global time predicates should be translated + * @param unfoldedComparisons indicates whether complex time predicates were already reduced to + * simple comparisons. If so, this flag should be set to {@code true} + * to avoid unnecessary work. + * @return equivalent predicate consisting only of primitive local time value comparisons + */ + static Predicate translateGlobalPredicates(Predicate predicate, ArrayList vars, boolean unfoldedComparisons){ + if(!unfoldedComparisons){ + predicate = Predicate.unfoldTemporalComparisons(predicate); + } + predicate = predicate.unfoldGlobalLeft(vars); + predicate = predicate.switchSides(); + return predicate.unfoldGlobalLeft(vars); + } + /** * Returns the predicates arguments * @@ -139,4 +161,12 @@ static Predicate unfoldTemporalComparisons(Predicate p){ */ boolean isTemporal(); + /** + * "Translates" a predicate possibly containing global time predicates to a predicate + * containing only local time comparisons. Only unfolds the left side(!!!) + * @param variables the variables in the whole query + * @return translated predicate (only left side "translated"!!!) + */ + Predicate unfoldGlobalLeft(List variables); + } diff --git a/src/main/java/org/s1ck/gdl/model/predicates/booleans/And.java b/src/main/java/org/s1ck/gdl/model/predicates/booleans/And.java index e4325d7..45f5e26 100644 --- a/src/main/java/org/s1ck/gdl/model/predicates/booleans/And.java +++ b/src/main/java/org/s1ck/gdl/model/predicates/booleans/And.java @@ -19,6 +19,7 @@ import org.s1ck.gdl.model.comparables.time.TimeSelector; import org.s1ck.gdl.model.predicates.Predicate; +import java.util.List; import java.util.Set; public class And implements Predicate { @@ -71,6 +72,11 @@ public boolean isTemporal(){ return lhs.isTemporal() || rhs.isTemporal(); } + @Override + public Predicate unfoldGlobalLeft(List variables) { + return new And(lhs.unfoldGlobalLeft(variables), rhs.unfoldGlobalLeft(variables)); + } + @Override public String toString() { return String.format("(%s AND %s)", lhs, rhs); diff --git a/src/main/java/org/s1ck/gdl/model/predicates/booleans/Not.java b/src/main/java/org/s1ck/gdl/model/predicates/booleans/Not.java index be842be..182b3c7 100644 --- a/src/main/java/org/s1ck/gdl/model/predicates/booleans/Not.java +++ b/src/main/java/org/s1ck/gdl/model/predicates/booleans/Not.java @@ -19,6 +19,7 @@ import org.s1ck.gdl.model.comparables.time.TimeSelector; import org.s1ck.gdl.model.predicates.Predicate; +import java.util.List; import java.util.Set; public class Not implements Predicate { @@ -64,6 +65,11 @@ public boolean isTemporal(){ return expression.isTemporal(); } + @Override + public Predicate unfoldGlobalLeft(List variables) { + return new Not(expression.unfoldGlobalLeft(variables)); + } + @Override public String toString() { return String.format("(NOT %s)", expression); diff --git a/src/main/java/org/s1ck/gdl/model/predicates/booleans/Or.java b/src/main/java/org/s1ck/gdl/model/predicates/booleans/Or.java index 9f1fdbc..8b84799 100644 --- a/src/main/java/org/s1ck/gdl/model/predicates/booleans/Or.java +++ b/src/main/java/org/s1ck/gdl/model/predicates/booleans/Or.java @@ -19,6 +19,7 @@ import org.s1ck.gdl.model.comparables.time.TimeSelector; import org.s1ck.gdl.model.predicates.Predicate; +import java.util.List; import java.util.Set; public class Or implements Predicate { @@ -71,6 +72,11 @@ public boolean isTemporal(){ return lhs.isTemporal() || rhs.isTemporal(); } + @Override + public Predicate unfoldGlobalLeft(List variables) { + return new Or(lhs.unfoldGlobalLeft(variables), rhs.unfoldGlobalLeft(variables)); + } + @Override public String toString() { return String.format("(%s OR %s)", lhs, rhs); diff --git a/src/main/java/org/s1ck/gdl/model/predicates/booleans/Xor.java b/src/main/java/org/s1ck/gdl/model/predicates/booleans/Xor.java index fd16587..4c69e68 100644 --- a/src/main/java/org/s1ck/gdl/model/predicates/booleans/Xor.java +++ b/src/main/java/org/s1ck/gdl/model/predicates/booleans/Xor.java @@ -19,6 +19,7 @@ import org.s1ck.gdl.model.comparables.time.TimeSelector; import org.s1ck.gdl.model.predicates.Predicate; +import java.util.List; import java.util.Set; public class Xor implements Predicate { @@ -71,6 +72,11 @@ public boolean isTemporal(){ return lhs.isTemporal() || rhs.isTemporal(); } + @Override + public Predicate unfoldGlobalLeft(List variables) { + return new Xor(lhs.unfoldGlobalLeft(variables), rhs.unfoldGlobalLeft(variables)); + } + @Override public String toString() { return String.format("(%s XOR %s)", lhs, rhs); diff --git a/src/main/java/org/s1ck/gdl/model/predicates/expressions/Comparison.java b/src/main/java/org/s1ck/gdl/model/predicates/expressions/Comparison.java index 7091a61..d222376 100644 --- a/src/main/java/org/s1ck/gdl/model/predicates/expressions/Comparison.java +++ b/src/main/java/org/s1ck/gdl/model/predicates/expressions/Comparison.java @@ -16,6 +16,7 @@ package org.s1ck.gdl.model.predicates.expressions; +import org.s1ck.gdl.model.comparables.time.TimeAtom; import org.s1ck.gdl.model.comparables.time.TimePoint; import org.s1ck.gdl.model.comparables.time.TimeSelector; import org.s1ck.gdl.model.predicates.Predicate; @@ -23,6 +24,7 @@ import org.s1ck.gdl.utils.Comparator; import java.util.HashSet; +import java.util.List; import java.util.Set; /** @@ -84,6 +86,17 @@ public boolean isTemporal(){ return lhs instanceof TimePoint || rhs instanceof TimePoint; } + @Override + public Predicate unfoldGlobalLeft(List variables) { + // check whether there is something to do + if(!isTemporal() || !(lhs instanceof TimeAtom)){ + return this; + } + + return (((TimeAtom)lhs).unfoldGlobal(comparator, rhs, variables)); + + } + @Override public Predicate unfoldTemporalComparisonsLeft(){ if (!isTemporal()){ diff --git a/src/test/java/org/s1ck/gdl/GDLLoaderTemporalTest.java b/src/test/java/org/s1ck/gdl/GDLLoaderTemporalTest.java index 1049b3b..cb22113 100644 --- a/src/test/java/org/s1ck/gdl/GDLLoaderTemporalTest.java +++ b/src/test/java/org/s1ck/gdl/GDLLoaderTemporalTest.java @@ -11,6 +11,7 @@ import org.s1ck.gdl.model.comparables.time.TimeSelector; import org.s1ck.gdl.model.predicates.Predicate; import org.s1ck.gdl.model.predicates.booleans.And; +import org.s1ck.gdl.model.predicates.booleans.Or; import org.s1ck.gdl.model.predicates.expressions.Comparison; import org.s1ck.gdl.utils.Comparator; @@ -24,9 +25,6 @@ public void simpleTimestampFunctionsTest(){ GDLLoader loader = getLoaderFromGDLString( "MATCH (alice)-[e1:knows {since : 2014}]->(bob) (alice)-[e2:knows {since : 2013}]->(eve) " + "WHERE (e1.val_from.before(2017-01-01) AND e2.tx_to.after(2018-12-23T15:55:23)) OR e1.knows>2010"); - System.out.println("Edges: "+loader.getEdgeCache()); - System.out.println("Vertices: "+loader.getVertexCache()); - System.out.println("Predicates: "+loader.getPredicates()); assertTrue(predicateContainedIn( new Comparison(new TimeSelector("e1", "val_from"), Comparator.LT, new TimeLiteral("2017-01-01")), loader.getPredicates().get().switchSides())); @@ -42,7 +40,7 @@ public void simpleTimestampFunctionsTest(){ public void periodLiteralTest(){ GDLLoader loader = getLoaderFromGDLString("MATCH (a)-->(b) " + "WHERE a.tx.overlaps(Interval(1970-01-01,1970-01-02))"); - assertEquals(loader.getPredicates().get(), + assertPredicateEquals(loader.getPredicates().get(), new And( new And( new Comparison( @@ -89,7 +87,7 @@ public void afterTest(){ new TimeSelector("a", "tx_to")) ); - assertEquals(result, expected); + assertPredicateEquals(result, expected); } @Test @@ -109,7 +107,7 @@ public void fromToTest(){ new TimeSelector("a", "tx_to") ) ); - assertEquals(result, expected); + assertPredicateEquals(result, expected); } @Test @@ -128,8 +126,8 @@ public void betweenTest(){ Comparator.GT, new TimeSelector("b", TimeSelector.TimeField.TX_FROM) ) - ).switchSides(); - assertEquals(result, expected); + ); + assertPredicateEquals(result, expected); } @Test @@ -141,8 +139,8 @@ public void precedesTest(){ new TimeSelector("a", TimeSelector.TimeField.TX_TO), Comparator.LTE, new TimeSelector("b", TimeSelector.TimeField.VAL_FROM) - ).switchSides(); - assertEquals(result.toString(), expected.toString()); + ); + assertPredicateEquals(result, expected); // timestamp as caller loader = getLoaderFromGDLString("MATCH (a)-->(b) " + @@ -152,8 +150,8 @@ public void precedesTest(){ new TimeSelector("a", TimeSelector.TimeField.TX_TO), Comparator.LTE, new TimeSelector("b", TimeSelector.TimeField.VAL_FROM) - ).switchSides(); - assertEquals(result, expected); + ); + assertPredicateEquals(result, expected); } @Test @@ -165,8 +163,8 @@ public void succeedsTest(){ new TimeSelector("a", TimeSelector.TimeField.VAL_FROM), Comparator.GTE, new TimeSelector("b", TimeSelector.TimeField.TX_TO) - ).switchSides(); - assertEquals(result, expected); + ); + assertPredicateEquals(result, expected); // timestamp as caller loader = getLoaderFromGDLString("MATCH (a)-->(b) " + @@ -176,14 +174,14 @@ public void succeedsTest(){ new TimeSelector("a", TimeSelector.TimeField.TX_TO), Comparator.GTE, new TimeSelector("b", TimeSelector.TimeField.VAL_TO) - ).switchSides(); - assertEquals(result, expected); + ); + assertPredicateEquals(result, expected); } @Test public void asOfTest(){ GDLLoader loader = getLoaderFromGDLString("MATCH (a)-[e]->(b) " + - "WHERE e.asOf(1970-01-01"); + "WHERE e.asOf(1970-01-01)"); Predicate result = loader.getPredicates().get(); Predicate expected = new And( new Comparison( @@ -196,8 +194,91 @@ public void asOfTest(){ Comparator.GTE, new TimeLiteral("1970-01-01") ) - ).switchSides(); - assertEquals(result, expected); + ); + assertPredicateEquals(result, expected); + } + + @Test + public void globalPredicateTest(){ + TimeLiteral l1 = new TimeLiteral("1970-01-01"); + TimeLiteral l2 = new TimeLiteral("2020-05-01"); + + TimeSelector aFrom = new TimeSelector("a", TimeSelector.TimeField.TX_FROM); + TimeSelector aTo = new TimeSelector("a", TimeSelector.TimeField.TX_TO); + TimeSelector bFrom = new TimeSelector("b", TimeSelector.TimeField.TX_FROM); + TimeSelector bTo = new TimeSelector("b", TimeSelector.TimeField.TX_TO); + TimeSelector eFrom = new TimeSelector("e", TimeSelector.TimeField.TX_FROM); + TimeSelector eTo = new TimeSelector("e", TimeSelector.TimeField.TX_TO); + // only lhs global + GDLLoader loader = getLoaderFromGDLString("MATCH (a)-[e]->(b) " + + "WHERE tx.between(1970-01-01, 2020-05-01)"); + Predicate result1 = loader.getPredicates().get(); + Predicate expected1 = new And( + // all froms <= l2 + new And( + new And( + new Comparison(eFrom, Comparator.LTE, l2), + new Comparison(aFrom, Comparator.LTE, l2) + ), + new Comparison(bFrom, Comparator.LTE, l2) + ), + new And( + new And( + new Comparison(eTo, Comparator.GT, l1), + new Comparison(aTo, Comparator.GT, l1) + ), + new Comparison(bTo, Comparator.GT, l1) + ) + ); + assertPredicateEquals(expected1, result1); + + //only rhs global + loader = getLoaderFromGDLString("MATCH (a)-[e]->(b) " + + "WHERE 1970-01-01.precedes(tx)"); + Predicate result2 = loader.getPredicates().get(); + Or expected2 = new Or( + new Or( + new Comparison(l1, Comparator.LTE, eFrom), + new Comparison(l1, Comparator.LTE, aFrom) + ), + new Comparison(l1, Comparator.LTE, bFrom) + ); + assertPredicateEquals(expected2, result2); + + //both sides global + + loader = getLoaderFromGDLString("MATCH (a)-[e]->(b) " + + "WHERE val_to.after(tx_from)"); + TimeSelector aValTo = new TimeSelector("a", TimeSelector.TimeField.VAL_TO); + TimeSelector bValTo = new TimeSelector("b", TimeSelector.TimeField.VAL_TO); + TimeSelector eValTo = new TimeSelector("e", TimeSelector.TimeField.VAL_TO); + Predicate result3 = loader.getPredicates().get(); + Predicate expected3 = new And( + new And( + new And( + new And( + new Comparison(eValTo, Comparator.GT, eFrom), + new Comparison(aValTo, Comparator.GT, eFrom) + ), + new Comparison(bValTo, Comparator.GT, eFrom) + ), + new And( + new And( + new Comparison(eValTo, Comparator.GT, aFrom), + new Comparison(aValTo, Comparator.GT, aFrom) + ), + new Comparison(bValTo, Comparator.GT, aFrom) + ) + ), + new And( + new And( + new Comparison(eValTo, Comparator.GT, bFrom), + new Comparison(aValTo, Comparator.GT, bFrom) + ), + new Comparison(bValTo, Comparator.GT, bFrom) + ) + ); + assertPredicateEquals(expected3, result3); } @@ -206,6 +287,15 @@ public void graphTimeStampTest(){ } + /** + * Does not fail iff {@code result==expected} or {@code result.switchSides()==expected} + * @param result predicate to compare + * @param expected predicate to compare + */ + private void assertPredicateEquals(Predicate result, Predicate expected){ + assertTrue(result.equals(expected) || result.switchSides().equals(expected)); + } + // checks if pred 1 is contained in pred 2 @@ -213,7 +303,7 @@ private boolean predicateContainedIn(Predicate p1, Predicate p2){ if(p1 instanceof Comparison){ if(p2 instanceof Comparison){ // TODO better equals method? - return p1.toString().equals(p2.toString()); + return p1.equals(p2) || p1.switchSides().equals(p2); } else{ Predicate lhs = p2.getArguments()[0]; @@ -229,7 +319,7 @@ private boolean predicateContainedIn(Predicate p1, Predicate p2){ return false; } else if(p1.toString().length() == p2.toString().length()){ - return p1.toString().equals(p2.toString()); + return p1.equals(p2) || p1.switchSides().equals(p2); } else{ Predicate lhs = p2.getArguments()[0]; diff --git a/src/test/java/org/s1ck/gdl/comparables/time/TimeLiteralTest.java b/src/test/java/org/s1ck/gdl/comparables/time/TimeLiteralTest.java index a5186fe..c84d893 100644 --- a/src/test/java/org/s1ck/gdl/comparables/time/TimeLiteralTest.java +++ b/src/test/java/org/s1ck/gdl/comparables/time/TimeLiteralTest.java @@ -11,6 +11,7 @@ import java.util.Date; import static org.junit.Assert.*; +import static org.s1ck.gdl.utils.Comparator.NEQ; public class TimeLiteralTest { @@ -65,14 +66,14 @@ public void unfoldPredicateTest(){ TimeSelector s = new TimeSelector("x", TimeSelector.TimeField.VAL_TO); //expected values Comparison cEq = new Comparison(literal, Comparator.EQ, s); - Comparison cNeq = new Comparison(literal, Comparator.NEQ, s); + Comparison cNeq = new Comparison(literal, NEQ, s); Comparison cGt = new Comparison(literal, Comparator.GT, s); Comparison cGte = new Comparison(literal, Comparator.GTE, s); Comparison cLt = new Comparison(literal, Comparator.LT, s); Comparison cLte = new Comparison(literal, Comparator.LTE, s); assertEquals(literal.unfoldComparison(Comparator.EQ, s), cEq); - assertEquals(literal.unfoldComparison(Comparator.NEQ, s), cNeq); + assertEquals(literal.unfoldComparison(NEQ, s), cNeq); assertEquals(literal.unfoldComparison(Comparator.GT, s), cGt); assertEquals(literal.unfoldComparison(Comparator.GTE, s), cGte); assertEquals(literal.unfoldComparison(Comparator.LT, s), cLt); @@ -85,4 +86,12 @@ public void containsTxToTest(){ TimeLiteral literal = new TimeLiteral("1970-02-01T15:23:05"); assertFalse(literal.containsSelectorType(TimeSelector.TimeField.TX_TO)); } + + @Test + public void unfoldGlobalTest(){ + TimeLiteral literal = new TimeLiteral(); + TimeSelector s = new TimeSelector("a", TimeSelector.TimeField.TX_TO); + Comparison c = new Comparison(literal, NEQ, s); + assertEquals(c.unfoldTemporalComparisonsLeft(), c); + } } diff --git a/src/test/java/org/s1ck/gdl/comparables/time/TimeSelectorTest.java b/src/test/java/org/s1ck/gdl/comparables/time/TimeSelectorTest.java index 661b3cb..962dcb9 100644 --- a/src/test/java/org/s1ck/gdl/comparables/time/TimeSelectorTest.java +++ b/src/test/java/org/s1ck/gdl/comparables/time/TimeSelectorTest.java @@ -1,10 +1,17 @@ package org.s1ck.gdl.comparables.time; import org.junit.Test; +import org.s1ck.gdl.model.comparables.time.TimeLiteral; import org.s1ck.gdl.model.comparables.time.TimeSelector; +import org.s1ck.gdl.model.predicates.Predicate; +import org.s1ck.gdl.model.predicates.booleans.And; +import org.s1ck.gdl.model.predicates.booleans.Or; import org.s1ck.gdl.model.predicates.expressions.Comparison; import org.s1ck.gdl.utils.Comparator; +import java.util.ArrayList; + import static org.junit.Assert.*; +import static org.s1ck.gdl.utils.Comparator.*; public class TimeSelectorTest { @@ -24,14 +31,14 @@ public void unfoldPredicateTest(){ TimeSelector selector = new TimeSelector("var", TimeSelector.TimeField.VAL_TO); TimeSelector s2 = new TimeSelector("x", TimeSelector.TimeField.VAL_TO); //expected values - Comparison cEq = new Comparison(selector, Comparator.EQ, s2); + Comparison cEq = new Comparison(selector, EQ, s2); Comparison cNeq = new Comparison(selector, Comparator.NEQ, s2); Comparison cGt = new Comparison(selector, Comparator.GT, s2); Comparison cGte = new Comparison(selector, Comparator.GTE, s2); Comparison cLt = new Comparison(selector, Comparator.LT, s2); Comparison cLte = new Comparison(selector, Comparator.LTE, s2); - assertEquals(selector.unfoldComparison(Comparator.EQ, s2), cEq); + assertEquals(selector.unfoldComparison(EQ, s2), cEq); assertEquals(selector.unfoldComparison(Comparator.NEQ, s2), cNeq); assertEquals(selector.unfoldComparison(Comparator.GT, s2), cGt); assertEquals(selector.unfoldComparison(Comparator.GTE, s2), cGte); @@ -40,6 +47,167 @@ public void unfoldPredicateTest(){ } + @Test + public void unfoldGlobalTest(){ + TimeSelector globalFrom = new TimeSelector(TimeSelector.TimeField.VAL_FROM); + TimeSelector globalTo = new TimeSelector(TimeSelector.TimeField.TX_TO); + ArrayList variables = new ArrayList<>(); + variables.add("a"); + variables.add("b"); + variables.add("c"); + TimeSelector aFrom = new TimeSelector("a", TimeSelector.TimeField.VAL_FROM); + TimeSelector aTo = new TimeSelector("a", TimeSelector.TimeField.TX_TO); + TimeSelector bFrom = new TimeSelector("b", TimeSelector.TimeField.VAL_FROM); + TimeSelector bTo = new TimeSelector("b", TimeSelector.TimeField.TX_TO); + TimeSelector cFrom = new TimeSelector("c", TimeSelector.TimeField.VAL_FROM); + TimeSelector cTo = new TimeSelector("c", TimeSelector.TimeField.TX_TO); + TimeLiteral rhs = new TimeLiteral(); + + //expected values + And expectedEqFrom = new And( + new Or( + new Or( + new Comparison(aFrom, EQ, rhs), + new Comparison(bFrom, EQ, rhs) + ), + new Comparison(cFrom, EQ, rhs) + ), + new And( + new And( + new Comparison(aFrom, LTE, rhs), + new Comparison(bFrom, LTE, rhs) + ), + new Comparison(cFrom, LTE, rhs) + ) + ); + And expectedEqTo = new And( + new Or( + new Or( + new Comparison(aTo, EQ, rhs), + new Comparison(bTo, EQ, rhs) + ), + new Comparison(cTo, EQ, rhs) + ), + new And( + new And( + new Comparison(aTo, GTE, rhs), + new Comparison(bTo, GTE, rhs) + ), + new Comparison(cTo, GTE, rhs) + ) + ); + + And expectedNeqFrom = new And( + new And( + new Comparison(aFrom, NEQ, rhs), + new Comparison(bFrom, NEQ, rhs) + ), + new Comparison(cFrom, NEQ, rhs) + ); + + And expectedNeqTo = new And( + new And( + new Comparison(aTo, NEQ, rhs), + new Comparison(bTo, NEQ, rhs) + ), + new Comparison(cTo, NEQ, rhs) + ); + + Or expectedGtFrom = new Or( + new Or( + new Comparison(aFrom, GT, rhs), + new Comparison(bFrom, GT, rhs) + ), + new Comparison(cFrom, GT, rhs) + ); + + And expectedGtTo = new And( + new And( + new Comparison(aTo, GT, rhs), + new Comparison(bTo, GT, rhs) + ), + new Comparison(cTo, GT, rhs) + ); + + Or expectedGteFrom = new Or( + new Or( + new Comparison(aFrom, GTE, rhs), + new Comparison(bFrom, GTE, rhs) + ), + new Comparison(cFrom, GTE, rhs) + ); + + And expectedGteTo = new And( + new And( + new Comparison(aTo, GTE, rhs), + new Comparison(bTo, GTE, rhs) + ), + new Comparison(cTo, GTE, rhs) + ); + + And expectedLtFrom = new And( + new And( + new Comparison(aFrom, LT, rhs), + new Comparison(bFrom, LT, rhs) + ), + new Comparison(cFrom, LT, rhs) + ); + + Or expectedLtTo = new Or( + new Or( + new Comparison(aTo, LT, rhs), + new Comparison(bTo, LT, rhs) + ), + new Comparison(cTo, LT, rhs) + ); + + And expectedLteFrom = new And( + new And( + new Comparison(aFrom, LTE, rhs), + new Comparison(bFrom, LTE, rhs) + ), + new Comparison(cFrom, LTE, rhs) + ); + + Or expectedLteTo = new Or( + new Or( + new Comparison(aTo, LTE, rhs), + new Comparison(bTo, LTE, rhs) + ), + new Comparison(cTo, LTE, rhs) + ); + + + + Predicate resultEqFrom = globalFrom.unfoldGlobal(EQ, rhs, variables); + Predicate resultEqTo = globalTo.unfoldGlobal(EQ, rhs, variables); + Predicate resultNeqFrom = globalFrom.unfoldGlobal(NEQ, rhs, variables); + Predicate resultNeqTo = globalTo.unfoldGlobal(NEQ, rhs, variables); + Predicate resultGtFrom = globalFrom.unfoldGlobal(GT, rhs, variables); + Predicate resultGtTo = globalTo.unfoldGlobal(GT, rhs, variables); + Predicate resultGteFrom = globalFrom.unfoldGlobal(GTE, rhs, variables); + Predicate resultGteTo = globalTo.unfoldGlobal(GTE, rhs, variables); + Predicate resultLtFrom = globalFrom.unfoldGlobal(LT, rhs, variables); + Predicate resultLtTo = globalTo.unfoldGlobal(LT, rhs, variables); + Predicate resultLteFrom = globalFrom.unfoldGlobal(LTE, rhs, variables); + Predicate resultLteTo = globalTo.unfoldGlobal(LTE, rhs, variables); + + + assertEquals(expectedEqFrom, resultEqFrom); + assertEquals(expectedEqTo, resultEqTo); + assertEquals(expectedNeqFrom, resultNeqFrom); + assertEquals(expectedNeqTo, resultNeqTo); + assertEquals(expectedGtFrom, resultGtFrom); + assertEquals(expectedGtTo, resultGtTo); + assertEquals(expectedGteFrom, resultGteFrom); + assertEquals(expectedGteTo, resultGteTo); + assertEquals(expectedLtFrom, resultLtFrom); + assertEquals(expectedLtTo, resultLtTo); + assertEquals(expectedLteFrom, resultLteFrom); + assertEquals(expectedLteTo, resultLteTo); + + } + @Test public void containsTxToTest(){ TimeSelector valF = new TimeSelector("a", "val_from");