diff --git a/fuse-asg/src/main/java/com/yangdb/fuse/asg/strategy/type/RelationPatternRangeAsgStrategy.java b/fuse-asg/src/main/java/com/yangdb/fuse/asg/strategy/type/RelationPatternRangeAsgStrategy.java index 92a71520e..e26ae4721 100644 --- a/fuse-asg/src/main/java/com/yangdb/fuse/asg/strategy/type/RelationPatternRangeAsgStrategy.java +++ b/fuse-asg/src/main/java/com/yangdb/fuse/asg/strategy/type/RelationPatternRangeAsgStrategy.java @@ -9,9 +9,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -21,7 +21,6 @@ */ - import com.yangdb.fuse.asg.strategy.AsgStrategy; import com.yangdb.fuse.model.Range; import com.yangdb.fuse.model.Tagged; @@ -36,6 +35,7 @@ import com.yangdb.fuse.model.query.entity.EndPattern; import com.yangdb.fuse.model.query.properties.BaseProp; import com.yangdb.fuse.model.query.properties.BasePropGroup; +import com.yangdb.fuse.model.query.properties.EPropGroup; import com.yangdb.fuse.model.query.quant.Quant1; import com.yangdb.fuse.model.query.quant.QuantBase; import com.yangdb.fuse.model.query.quant.QuantType; @@ -94,7 +94,7 @@ public void apply(AsgQuery query, AsgStrategyContext context) { quant.get().addNext(quantAsg); } else { // quant of type some exist -> add the inner or patterns after the quant - addRelPattern(counter, query, quant.get(), relPattern,endPattern.get()); + addRelPattern(counter, query, quant.get(), relPattern, endPattern.get()); //remove pattern parent.get().removeNextChild(relPattern); } @@ -103,8 +103,47 @@ public void apply(AsgQuery query, AsgStrategyContext context) { //replace all EndPatterns with its internal real entity AsgQueryUtil.elements(query, EndPattern.class) - .forEach(p-> ((AsgEBase) p).seteBase(p.geteBase().getEndEntity())); + .forEach(p -> replaceEndPattern(counter, p)); + + } + + /** + * replace end pattern with the actual type - including its possible filters + * + * @param counter + * @param p + */ + public void replaceEndPattern(AtomicInteger counter, AsgEBase p) { + if (!p.geteBase().getFilter().isEmpty()) { + //if end pattern is last in query - add quant for the purpose of the end-pattern filters + if (!p.hasNext()) { + addEndPatternFilters(counter, p); + } else { + //if end pattern is not last in query - search for a quant right after + Optional> quantOp = AsgQueryUtil.nextAdjacentDescendant(p, QuantBase.class, 1); + // if quant not present - add one and chain all childs to that quant + if (!quantOp.isPresent()) { + AsgEBase quantAsg = new AsgEBase<>(new Quant1(counter.incrementAndGet(), QuantType.all)); + AsgQueryUtil.replaceParents(quantAsg,p); + p.addNext(quantAsg); + } + // quant present - add end-pattern filter to quant + quantOp = AsgQueryUtil.nextAdjacentDescendant(p, QuantBase.class, 1); + EPropGroup ePropGroup = new EPropGroup(p.geteBase().getFilter()).clone(counter.incrementAndGet()); + quantOp.get().addNext(new AsgEBase<>(ePropGroup)); + } + } + //change endPattern type to actual type + ((AsgEBase) p).seteBase(p.geteBase().getEndEntity()); + } + public QuantBase addEndPatternFilters(AtomicInteger counter, AsgEBase p) { + QuantBase newQuant = new Quant1(counter.incrementAndGet(), QuantType.all); + AsgEBase quantAsg = new AsgEBase<>(newQuant); + EPropGroup ePropGroup = new EPropGroup(p.geteBase().getFilter()).clone(counter.incrementAndGet()); + quantAsg.addNext(new AsgEBase<>(ePropGroup)); + p.addNext(quantAsg); + return newQuant; } /** @@ -117,11 +156,11 @@ public void apply(AsgQuery query, AsgStrategyContext context) { private void addRelPattern(AtomicInteger counter, AsgQuery query, AsgEBase quantAsg, AsgEBase relPattern, AsgEBase endPattern) { Range range = relPattern.geteBase().getLength(); //duplicate the rel pattern according to the range, range should already be validated by the validator - LongStream.rangeClosed(range.getLower() , range.getUpper()) + LongStream.rangeClosed(range.getLower(), range.getUpper()) //this is the Root some quant all pattern premutations will be added to... .forEach(value -> { //if value == 0 remove the RelPattern entirely - if(value==0) { + if (value == 0) { //take the path after the end pattern section (if exists) & add it as no hop pattern to the Quant if (endPattern.hasNext()) { final AsgEBase nextAfterEndPattern = endPattern.getNext().get(0); @@ -145,28 +184,29 @@ private void addRelPattern(AtomicInteger counter, AsgQuery query, AsgEBase addPath(AtomicInteger counter,long value, AsgEBase relPattern, AsgEBase endPattern) { + private AsgEBase addPath(AtomicInteger counter, long value, AsgEBase relPattern, AsgEBase endPattern) { final AtomicReference> current = new AtomicReference<>(); LongStream.rangeClosed(1, value) .forEach(step -> { - AsgEBase node = buildStep(counter,relPattern, endPattern); + AsgEBase node = buildStep(counter, relPattern, endPattern); if (current.get() == null) { current.set(node); } else { //the build step returns a cloned pattern of [rel:Rel]-....->(endPattern:Entity)->... // - if(!(current.get().geteBase() instanceof QuantBase)) { + if (!(current.get().geteBase() instanceof QuantBase)) { final AsgEBase quant = new AsgEBase<>(new Quant1(counter.incrementAndGet(), QuantType.all)); - AsgQueryUtil.addAsNext(quant,current.get()); + AsgQueryUtil.addAsNext(quant, current.get()); current.set(quant); } else { //knowing that the rel pattern has a shape of a line not a tree get the last Descendant - current.set(AsgQueryUtil.nextDescendant(current.get(),EndPattern.class).get()); + current.set(AsgQueryUtil.nextDescendant(current.get(), EndPattern.class).get()); } current.get().addNext(AsgQueryUtil.ancestorRoot(node).get()); current.set(node); @@ -179,11 +219,12 @@ private AsgEBase addPath(AtomicInteger counter,long value, AsgE /** * build a new complete rel->pattern step cloned from existing step + * * @param relPattern * @param endPattern * @return */ - private AsgEBase buildStep(AtomicInteger counter,AsgEBase relPattern, AsgEBase endPattern) { + private AsgEBase buildStep(AtomicInteger counter, AsgEBase relPattern, AsgEBase endPattern) { RelPattern pattern = relPattern.geteBase(); List> belowList = new ArrayList<>(relPattern.getB()); diff --git a/fuse-asg/src/main/java/com/yangdb/fuse/asg/validation/AsgCompositeQueryValidatorStrategy.java b/fuse-asg/src/main/java/com/yangdb/fuse/asg/validation/AsgCompositeQueryValidatorStrategy.java index c523e7cb8..5a78f2976 100644 --- a/fuse-asg/src/main/java/com/yangdb/fuse/asg/validation/AsgCompositeQueryValidatorStrategy.java +++ b/fuse-asg/src/main/java/com/yangdb/fuse/asg/validation/AsgCompositeQueryValidatorStrategy.java @@ -34,7 +34,7 @@ public class AsgCompositeQueryValidatorStrategy implements AsgValidatorStrategy { - public static final String ERROR_1 = "Ontology Contains Cycle "; + public static final String ERROR_1 = "Ontology Composition Error "; @Override public ValidationResult apply(AsgQuery query, AsgStrategyContext context) { diff --git a/fuse-asg/src/main/java/com/yangdb/fuse/asg/validation/AsgEntityPropertiesValidatorStrategy.java b/fuse-asg/src/main/java/com/yangdb/fuse/asg/validation/AsgEntityPropertiesValidatorStrategy.java index f25a29d69..63612dd84 100644 --- a/fuse-asg/src/main/java/com/yangdb/fuse/asg/validation/AsgEntityPropertiesValidatorStrategy.java +++ b/fuse-asg/src/main/java/com/yangdb/fuse/asg/validation/AsgEntityPropertiesValidatorStrategy.java @@ -36,6 +36,7 @@ import com.yangdb.fuse.model.query.properties.EProp; import com.yangdb.fuse.model.query.properties.EPropGroup; import com.yangdb.fuse.model.query.properties.constraint.ConstraintOp; +import com.yangdb.fuse.model.query.quant.Quant1; import com.yangdb.fuse.model.query.quant.QuantBase; import com.yangdb.fuse.model.validation.ValidationResult; import javaslang.collection.Stream; @@ -62,7 +63,7 @@ public ValidationResult apply(AsgQuery query, AsgStrategyContext context) { Stream.ofAll(AsgQueryUtil.elements(query, EProp.class)) .filter(property -> !(property.geteBase() instanceof CalculatedEProp)) .forEach(property -> { - Optional> parent = calculateNextAncestor(property,EEntityBase.class); + Optional> parent = calculateNextAncestor(property,EEntityBase.class,Collections.singletonList(Quant1.class)); if (!parent.isPresent()) { errors.add(ERROR_1 + ":" + property); } else { @@ -72,7 +73,7 @@ public ValidationResult apply(AsgQuery query, AsgStrategyContext context) { Stream.ofAll(AsgQueryUtil.elements(query, EPropGroup.class)) .forEach(group -> { - Optional> parent = calculateNextAncestor(group,EEntityBase.class); + Optional> parent = calculateNextAncestor(group,EEntityBase.class,Collections.singletonList(Quant1.class)); if (!parent.isPresent()) { errors.add(ERROR_1 + group); } else { diff --git a/fuse-asg/src/test/java/com/yangdb/fuse/asg/strategy/type/RelationRangeAsgStrategyWithFiltersInRangTest.java b/fuse-asg/src/test/java/com/yangdb/fuse/asg/strategy/type/RelationRangeAsgStrategyWithFiltersInRangTest.java new file mode 100644 index 000000000..6345eb0b0 --- /dev/null +++ b/fuse-asg/src/test/java/com/yangdb/fuse/asg/strategy/type/RelationRangeAsgStrategyWithFiltersInRangTest.java @@ -0,0 +1,403 @@ +package com.yangdb.fuse.asg.strategy.type; + +import com.yangdb.fuse.asg.validation.AsgQueryValidator; +import com.yangdb.fuse.asg.validation.AsgValidatorStrategyRegistrarImpl; +import com.yangdb.fuse.dispatcher.ontology.OntologyProvider; +import com.yangdb.fuse.model.OntologyTestUtils; +import com.yangdb.fuse.model.Range; +import com.yangdb.fuse.model.asgQuery.AsgEBase; +import com.yangdb.fuse.model.asgQuery.AsgQuery; +import com.yangdb.fuse.model.asgQuery.AsgQueryUtil; +import com.yangdb.fuse.model.asgQuery.AsgStrategyContext; +import com.yangdb.fuse.model.execution.plan.descriptors.AsgQueryDescriptor; +import com.yangdb.fuse.model.ontology.Ontology; +import com.yangdb.fuse.model.query.EBase; +import com.yangdb.fuse.model.query.Rel; +import com.yangdb.fuse.model.query.RelPattern; +import com.yangdb.fuse.model.query.entity.ETyped; +import com.yangdb.fuse.model.query.entity.EUntyped; +import com.yangdb.fuse.model.query.properties.EProp; +import com.yangdb.fuse.model.query.properties.EPropGroup; +import com.yangdb.fuse.model.query.properties.RelProp; +import com.yangdb.fuse.model.query.properties.RelPropGroup; +import com.yangdb.fuse.model.query.properties.constraint.Constraint; +import com.yangdb.fuse.model.query.properties.constraint.ConstraintOp; +import com.yangdb.fuse.model.query.quant.Quant1; +import com.yangdb.fuse.model.query.quant.QuantBase; +import com.yangdb.fuse.model.query.quant.QuantType; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; + +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Optional; + +import static com.yangdb.fuse.model.OntologyTestUtils.*; +import static com.yangdb.fuse.model.Tagged.tagSeq; +import static com.yangdb.fuse.model.asgQuery.AsgQuery.Builder.*; +import static com.yangdb.fuse.model.query.Rel.Direction.R; +import static com.yangdb.fuse.model.query.properties.constraint.Constraint.of; +import static com.yangdb.fuse.model.query.properties.constraint.ConstraintOp.eq; +import static com.yangdb.fuse.model.query.quant.QuantType.all; + +/** + * test Rel pattern asg strategy: + * Expand given relation range pattern into an Or quant with all premutations of requested path length + *

+ * (:E)-[:R | 1..3]->(:E) would be transformed into: + *

+ * (:E)-Quant[OR] + * -[:R]->(:E) + * -[:R]->(:E)-[:R]->(:E) + * -[:R]->(:E)-[:R]->(:E)-[:R]->(:E) + */ +public class RelationRangeAsgStrategyWithFiltersInRangTest { + static Ontology ontology; + static AsgQueryValidator queryValidator; + + @BeforeClass + public static void setUp() throws Exception { + ontology = OntologyTestUtils.createDragonsOntologyShort(); + queryValidator = new AsgQueryValidator(new AsgValidatorStrategyRegistrarImpl(), new OntologyProvider() { + @Override + public Optional get(String id) { + return Optional.of(ontology); + } + + @Override + public Collection getAll() { + return Collections.singleton(ontology); + } + + @Override + public Ontology add(Ontology ontology) { + return ontology; + } + }); + } + + @Test + public void testUntypedToTypedStrategyWithoutQuantsInPath() { + Ontology.Accessor ont = new Ontology.Accessor(ontology); + AsgQuery query = AsgQuery.Builder.start("Q1", "Dragons") + .next(typed(1,OntologyTestUtils.PERSON.type)) + .next(relPattern(2, OntologyTestUtils.KNOW.getrType(), new Range(1, 3), R) + .below(relProp(10, RelProp.of(10, START_DATE.type, of(eq, new Date()))))) + .next(endPattern(new ETyped(3,tagSeq("end"),OntologyTestUtils.PERSON.type,-1), + new EProp(3,GENDER.type,of(eq,"Person")))) + .build(); + + RelationPatternRangeAsgStrategy strategy = new RelationPatternRangeAsgStrategy(); + strategy.apply(query, new AsgStrategyContext(ont)); + + Optional> quant = AsgQueryUtil.elements(query, Quant1.class).stream().filter(q -> q.geteBase().getqType().equals(QuantType.some)).findAny(); + Assert.assertFalse(AsgQueryUtil.element(query, RelPattern.class).isPresent()); + Assert.assertTrue(quant.isPresent()); + Assert.assertEquals(3, quant.get().getNext().size()); + Assert.assertEquals(1, AsgQueryUtil.elements(query, QuantBase.class).stream().filter(e->e.getNext().size()==3).count()); + Assert.assertEquals(3, AsgQueryUtil.elements(query, QuantBase.class).stream().filter(e->e.getNext().size()==2).count()); + Assert.assertEquals(3, AsgQueryUtil.elements(query, QuantBase.class).stream().filter(e->e.getNext().size()==1).count()); + + Assert.assertEquals(5, AsgQueryUtil.count(quant.get().getNext().get(0), EBase.class)); + Assert.assertEquals(10, AsgQueryUtil.count(quant.get().getNext().get(1), EBase.class)); + Assert.assertEquals(15, AsgQueryUtil.count(quant.get().getNext().get(2), EBase.class)); + + Assert.assertEquals(6, AsgQueryUtil.elements(query, Rel.class).stream().filter(r -> r.getB().size() == 1).count()); + Assert.assertEquals(0, AsgQueryUtil.elements(query, RelProp.class).size()); + Assert.assertEquals(6, AsgQueryUtil.elements(query, RelPropGroup.class).size()); + + Assert.assertEquals(0, AsgQueryUtil.elements(query, EProp.class).size()); + Assert.assertEquals(6, AsgQueryUtil.elements(query, EPropGroup.class).size()); + Assert.assertTrue(queryValidator.validate(query).toString(),queryValidator.validate(query).valid()); + Assert.assertNotNull(AsgQueryDescriptor.print(query)); + + } + @Test + public void testUntypedToTypedStrategyWithoutQuantsInPathWithRangeFromZero() { + Ontology.Accessor ont = new Ontology.Accessor(ontology); + AsgQuery query = AsgQuery.Builder.start("Q1", "Dragons") + .next(typed(1,OntologyTestUtils.PERSON.type)) + .next(relPattern(2, KNOW.getrType(), new Range(0, 3), R) + .below(relProp(10, RelProp.of(10, START_DATE.type, of(eq, new Date()))))) + .next(endPattern(new ETyped(3,tagSeq("end"),OntologyTestUtils.PERSON.type,-1), + new EProp(4,FIRST_NAME.type,of(eq,"Person")), + new EProp(5,NAME.type,of(eq,"Other")))) + .build(); + + RelationPatternRangeAsgStrategy strategy = new RelationPatternRangeAsgStrategy(); + strategy.apply(query, new AsgStrategyContext(ont)); + + Optional> quant = AsgQueryUtil.elements(query, Quant1.class).stream().filter(q -> q.geteBase().getqType().equals(QuantType.some)).findAny(); + Assert.assertFalse(AsgQueryUtil.element(query, RelPattern.class).isPresent()); + Assert.assertTrue(quant.isPresent()); + Assert.assertEquals(3, quant.get().getNext().size()); + Assert.assertEquals(1, AsgQueryUtil.elements(query, QuantBase.class).stream().filter(e->e.getNext().size()==3).count()); + Assert.assertEquals(3, AsgQueryUtil.elements(query, QuantBase.class).stream().filter(e->e.getNext().size()==2).count()); + Assert.assertEquals(3, AsgQueryUtil.elements(query, QuantBase.class).stream().filter(e->e.getNext().size()==1).count()); + + Assert.assertEquals(5, AsgQueryUtil.count(quant.get().getNext().get(0), EBase.class)); + Assert.assertEquals(10, AsgQueryUtil.count(quant.get().getNext().get(1), EBase.class)); + Assert.assertEquals(15, AsgQueryUtil.count(quant.get().getNext().get(2), EBase.class)); + + Assert.assertEquals(6, AsgQueryUtil.elements(query, Rel.class).stream().filter(r -> r.getB().size() == 1).count()); + Assert.assertEquals(0, AsgQueryUtil.elements(query, RelProp.class).size()); + Assert.assertEquals(6, AsgQueryUtil.elements(query, RelPropGroup.class).size()); + + Assert.assertEquals(0, AsgQueryUtil.elements(query, EProp.class).size()); + Assert.assertEquals(6, AsgQueryUtil.elements(query, EPropGroup.class).size()); + Assert.assertTrue(queryValidator.validate(query).toString(),queryValidator.validate(query).valid()); + Assert.assertNotNull(AsgQueryDescriptor.print(query)); + + } + + @Test + public void testUntypedToTypedWithPropsStrategyWithoutQuantsInPath() throws Exception { + Ontology.Accessor ont = new Ontology.Accessor(ontology); + AsgQuery query = AsgQuery.Builder.start("Q1", "Dragons") + .next(typed(1,OntologyTestUtils.PERSON.type)) + .next(relPattern(2, OntologyTestUtils.KNOW.getrType(), new Range(1, 3), R) + .below(relProp(10, RelProp.of(10, START_DATE.type, of(eq, new Date()))))) + .next(endPattern(new ETyped(3,tagSeq("end"), OntologyTestUtils.PERSON.type,0,-1), + new EProp(5,NAME.type,of(eq,"Other")))) + .next(eProp(4, OntologyTestUtils.FIRST_NAME.type, of(eq, "abc"))) + .build(); + + RelationPatternRangeAsgStrategy strategy = new RelationPatternRangeAsgStrategy(); + strategy.apply(query, new AsgStrategyContext(ont)); + + Optional> quant = AsgQueryUtil.elements(query, Quant1.class).stream().filter(q -> q.geteBase().getqType().equals(QuantType.some)).findAny(); + Assert.assertFalse(AsgQueryUtil.element(query, RelPattern.class).isPresent()); + Assert.assertTrue(quant.isPresent()); + + Assert.assertEquals(3, quant.get().getNext().size()); + Assert.assertEquals(1, AsgQueryUtil.elements(query, QuantBase.class).stream().filter(e->e.getNext().size()==3).count()); + Assert.assertEquals(6, AsgQueryUtil.elements(query, QuantBase.class).stream().filter(e->e.getNext().size()==2).count()); + Assert.assertEquals(0, AsgQueryUtil.elements(query, QuantBase.class).stream().filter(e->e.getNext().size()==1).count()); + + Assert.assertEquals(6, AsgQueryUtil.count(quant.get().getNext().get(0), EBase.class)); + Assert.assertEquals(11, AsgQueryUtil.count(quant.get().getNext().get(1), EBase.class)); + Assert.assertEquals(16, AsgQueryUtil.count(quant.get().getNext().get(2), EBase.class)); + + Assert.assertEquals(6, AsgQueryUtil.elements(query, Rel.class).stream().filter(r -> r.getB().size() == 1).count()); + Assert.assertEquals(0, AsgQueryUtil.elements(query, RelProp.class).size()); + Assert.assertEquals(6, AsgQueryUtil.elements(query, RelPropGroup.class).size()); + + Assert.assertEquals(3, AsgQueryUtil.elements(query, EProp.class).stream().filter(ep -> ep.geteBase().getCon().getOp().equals(eq)).count()); + Assert.assertEquals(6, AsgQueryUtil.elements(query, EPropGroup.class).size()); + + + Assert.assertTrue(queryValidator.validate(query).toString(),queryValidator.validate(query).valid()); + Assert.assertNotNull(AsgQueryDescriptor.print(query)); + } + @Test + public void testUntypedToTypedWithPropsStrategyWithoutQuantsInPathWithRangeFromZero() throws Exception { + Ontology.Accessor ont = new Ontology.Accessor(ontology); + AsgQuery query = AsgQuery.Builder.start("Q1", "Dragons") + .next(typed(1,OntologyTestUtils.PERSON.type)) + .next(relPattern(2, OntologyTestUtils.KNOW.getrType(), new Range(0, 3), R) + .below(relProp(10, RelProp.of(10, START_DATE.type, of(eq, new Date()))))) + .next(endPattern(new ETyped(3,tagSeq("end"), OntologyTestUtils.PERSON.type,0,-1), + new EProp(5,NAME.type,of(eq,"Other")))) + .next(eProp(4, OntologyTestUtils.FIRST_NAME.type, of(eq, "abc"))) + .build(); + + RelationPatternRangeAsgStrategy strategy = new RelationPatternRangeAsgStrategy(); + strategy.apply(query, new AsgStrategyContext(ont)); + + Optional> quant = AsgQueryUtil.elements(query, Quant1.class).stream().filter(q -> q.geteBase().getqType().equals(QuantType.some)).findAny(); + Assert.assertFalse(AsgQueryUtil.element(query, RelPattern.class).isPresent()); + Assert.assertTrue(quant.isPresent()); + + Assert.assertEquals(4, quant.get().getNext().size()); + + Assert.assertEquals(1, AsgQueryUtil.elements(query, QuantBase.class).stream().filter(e->e.getNext().size()==4).count()); + Assert.assertEquals(6, AsgQueryUtil.elements(query, QuantBase.class).stream().filter(e->e.getNext().size()==2).count()); + Assert.assertEquals(0, AsgQueryUtil.elements(query, QuantBase.class).stream().filter(e->e.getNext().size()==1).count()); + + Assert.assertEquals(1, AsgQueryUtil.count(quant.get().getNext().get(0), EProp.class)); + Assert.assertEquals(6, AsgQueryUtil.count(quant.get().getNext().get(1), EBase.class)); + Assert.assertEquals(11, AsgQueryUtil.count(quant.get().getNext().get(2), EBase.class)); + Assert.assertEquals(16, AsgQueryUtil.count(quant.get().getNext().get(3), EBase.class)); + + Assert.assertEquals(6, AsgQueryUtil.elements(query, Rel.class).stream().filter(r -> r.getB().size() == 1).count()); + Assert.assertEquals(0, AsgQueryUtil.elements(query, RelProp.class).size()); + Assert.assertEquals(6, AsgQueryUtil.elements(query, RelPropGroup.class).size()); + + Assert.assertEquals(4, AsgQueryUtil.elements(query, EProp.class).stream().filter(ep -> ep.geteBase().getCon().getOp().equals(eq)).count()); + Assert.assertEquals(6, AsgQueryUtil.elements(query, EPropGroup.class).size()); + + + Assert.assertTrue(queryValidator.validate(query).toString(),queryValidator.validate(query).valid()); + Assert.assertNotNull(AsgQueryDescriptor.print(query)); + } + + @Test + public void testUntypedToTypedWithSomePropsStrategyWithoutQuantsInPath() throws Exception { + Ontology.Accessor ont = new Ontology.Accessor(ontology); + AsgQuery query = AsgQuery.Builder.start("Q1", "Dragons") + .next(typed(1, OntologyTestUtils.PERSON.type, "source")) + .next(quant1(2, all)) + .in( + eProp(4, OntologyTestUtils.FIRST_NAME.type, of(eq, "abc")), + relPattern(2, OntologyTestUtils.KNOW.getrType(), new Range(1, 3), R) + .next( + endPattern(new ETyped(3,tagSeq("end"), OntologyTestUtils.PERSON.type,0), + new EProp(5,NAME.type,of(eq,"Other"))) + .addNext(eProp(4, OntologyTestUtils.LAST_NAME.type, of(eq, "abc"))))) + + .build(); + RelationPatternRangeAsgStrategy strategy = new RelationPatternRangeAsgStrategy(); + strategy.apply(query, new AsgStrategyContext(ont)); + + Optional> quant = AsgQueryUtil.elements(query, Quant1.class).stream().filter(q -> q.geteBase().getqType().equals(QuantType.some)).findAny(); + Assert.assertFalse(AsgQueryUtil.element(query, RelPattern.class).isPresent()); + Assert.assertTrue(quant.isPresent()); + Assert.assertEquals(3, quant.get().getNext().size()); + Assert.assertEquals(1, AsgQueryUtil.elements(query, QuantBase.class).stream().filter(e->e.getNext().size()==3).count()); + Assert.assertEquals(7, AsgQueryUtil.elements(query, QuantBase.class).stream().filter(e->e.getNext().size()==2).count()); + Assert.assertEquals(0, AsgQueryUtil.elements(query, QuantBase.class).stream().filter(e->e.getNext().size()==1).count()); + + Assert.assertEquals(5, AsgQueryUtil.count(quant.get().getNext().get(0), EBase.class)); + Assert.assertEquals(9, AsgQueryUtil.count(quant.get().getNext().get(1), EBase.class)); + Assert.assertEquals(13, AsgQueryUtil.count(quant.get().getNext().get(2), EBase.class)); + + + Assert.assertEquals(0, AsgQueryUtil.elements(query, RelProp.class).size()); + Assert.assertEquals(0, AsgQueryUtil.elements(query, RelPropGroup.class).size()); + + Assert.assertEquals(1, AsgQueryUtil.elements(query, EProp.class).stream().filter(ep -> ep.geteBase().getpType().equals(OntologyTestUtils.FIRST_NAME.type)).count()); + Assert.assertEquals(3, AsgQueryUtil.elements(query, EProp.class).stream().filter(ep -> ep.geteBase().getpType().equals(OntologyTestUtils.LAST_NAME.type)).count()); + + Assert.assertTrue(queryValidator.validate(query).toString(),queryValidator.validate(query).valid()); + Assert.assertNotNull(AsgQueryDescriptor.print(query)); + } + + @Test + public void testUntypedToTypedWithQuantPropsStrategyWithoutQuantsInPath() throws Exception { + Ontology.Accessor ont = new Ontology.Accessor(ontology); + AsgQuery query = AsgQuery.Builder.start("Q1", "Dragons") + .next(typed(1,OntologyTestUtils.PERSON.type)) + .next(relPattern(2, OntologyTestUtils.KNOW.getrType(), new Range(1, 3), R) + .below(relProp(10, RelProp.of(10, START_DATE.type, of(eq, new Date()))))) + .next(endPattern(new ETyped(3, tagSeq("end"),OntologyTestUtils.PERSON.type,0), + new EProp(5,NAME.type,of(eq,"Other")))) + .next(quant1(4, all)) + .in(ePropGroup(5, + EProp.of(5, OntologyTestUtils.FIRST_NAME.type, Constraint.of(ConstraintOp.like, "Dormir")))) + .build(); + + RelationPatternRangeAsgStrategy strategy = new RelationPatternRangeAsgStrategy(); + strategy.apply(query, new AsgStrategyContext(ont)); + + Optional> quant = AsgQueryUtil.elements(query, Quant1.class).stream().filter(q -> q.geteBase().getqType().equals(QuantType.some)).findAny(); + Assert.assertFalse(AsgQueryUtil.element(query, RelPattern.class).isPresent()); + Assert.assertTrue(quant.isPresent()); + Assert.assertEquals(3, quant.get().getNext().size()); + Assert.assertEquals(1, AsgQueryUtil.elements(query, QuantBase.class).stream().filter(e->e.getNext().size()==3).count()); + Assert.assertEquals(6, AsgQueryUtil.elements(query, QuantBase.class).stream().filter(e->e.getNext().size()==2).count()); + Assert.assertEquals(0, AsgQueryUtil.elements(query, QuantBase.class).stream().filter(e->e.getNext().size()==1).count()); + + Assert.assertEquals(6, AsgQueryUtil.count(quant.get().getNext().get(0), EBase.class)); + Assert.assertEquals(11, AsgQueryUtil.count(quant.get().getNext().get(1), EBase.class)); + Assert.assertEquals(16, AsgQueryUtil.count(quant.get().getNext().get(2), EBase.class)); + + Assert.assertEquals(6, AsgQueryUtil.elements(query, Rel.class).stream().filter(r -> r.getB().size() == 1).count()); + + Assert.assertEquals(9, AsgQueryUtil.elements(query, EPropGroup.class).stream().filter(ep -> ep.geteBase().getProps().size() == 1).count()); + Assert.assertEquals(0, AsgQueryUtil.elements(query, EProp.class).size()); + + Assert.assertTrue(queryValidator.validate(query).toString(),queryValidator.validate(query).valid()); + Assert.assertNotNull(AsgQueryDescriptor.print(query)); + + } + + @Test + @Ignore ( "Fix when both relPattern->relProp && endPattern->filter exist togather") + public void testUntypedToTypedWithQuantPropsAfterRelStrategyWithoutQuantsInPath() throws Exception { + Ontology.Accessor ont = new Ontology.Accessor(ontology); + AsgQuery query = AsgQuery.Builder.start("Q1", "Dragons") + .next(unTyped(1,"P",Collections.singletonList(OntologyTestUtils.PERSON.type))) + .next(relPattern(2, OntologyTestUtils.KNOW.getrType(), new Range(1, 3), R) + .below(relProp(10, RelProp.of(10, START_DATE.type, of(eq, new Date()))))) + .next(quant1(3, all)) + .in(relPropGroup(11, all, RelProp.of(11, END_DATE.type, of(eq, new Date()))), + endPattern(new ETyped(4, tagSeq("end"),OntologyTestUtils.PERSON.type,0), + new EProp(5,NAME.type,of(eq,"Other"))) + .next(quant1(5, all) + .addNext(ePropGroup(12, EProp.of(12, OntologyTestUtils.FIRST_NAME.type, Constraint.of(ConstraintOp.like, "Dormir")))) + ) + ) + .build(); + + RelationPatternRangeAsgStrategy strategy = new RelationPatternRangeAsgStrategy(); + strategy.apply(query, new AsgStrategyContext(ont)); + + Optional> quant = AsgQueryUtil.elements(query, Quant1.class).stream().filter(q -> q.geteBase().getqType().equals(QuantType.some)).findAny(); + Assert.assertFalse(AsgQueryUtil.element(query, RelPattern.class).isPresent()); + Assert.assertTrue(quant.isPresent()); + Assert.assertEquals(3, quant.get().getNext().size()); + Assert.assertEquals(4, AsgQueryUtil.elements(query, QuantBase.class).stream().filter(e->e.getNext().size()==3).count()); + Assert.assertEquals(6, AsgQueryUtil.elements(query, QuantBase.class).stream().filter(e->e.getNext().size()==2).count()); + Assert.assertEquals(0, AsgQueryUtil.elements(query, QuantBase.class).stream().filter(e->e.getNext().size()==1).count()); + + Assert.assertEquals(8, AsgQueryUtil.count(quant.get().getNext().get(0), EBase.class)); + Assert.assertEquals(14, AsgQueryUtil.count(quant.get().getNext().get(1), EBase.class)); + Assert.assertEquals(20, AsgQueryUtil.count(quant.get().getNext().get(2), EBase.class)); + + Assert.assertEquals(6, AsgQueryUtil.elements(query, Rel.class).stream().filter(r -> r.getB().size() == 1).count()); + Assert.assertEquals(6, AsgQueryUtil.elements(query, RelPropGroup.class).stream().filter(ep -> ep.geteBase().getProps().get(0).getpType().equals(START_DATE.type)).count()); + Assert.assertEquals(6, AsgQueryUtil.elements(query, RelPropGroup.class).stream().filter(ep -> ep.geteBase().getProps().get(0).getpType().equals(END_DATE.type)).count()); + + Assert.assertEquals(9, AsgQueryUtil.elements(query, EPropGroup.class).stream().filter(ep -> ep.geteBase().getProps().size() == 1).count()); + + Assert.assertTrue(queryValidator.validate(query).toString(),queryValidator.validate(query).valid()); + Assert.assertNotNull(AsgQueryDescriptor.print(query)); + } + + @Test + public void testUntypedToTypedWithPropsStrategyWithQuantsInPath() { + Ontology.Accessor ont = new Ontology.Accessor(ontology); + AsgQuery query = AsgQuery.Builder.start("Q1", "Dragons") + .next(unTyped(1,"P", Collections.singletonList(OntologyTestUtils.PERSON.type))) + .next(quant1(2, all)) + .in(ePropGroup(3, + EProp.of(3, OntologyTestUtils.FIRST_NAME.type, Constraint.of(ConstraintOp.like, "Dormir")))) + .next(relPattern(2, OntologyTestUtils.KNOW.getrType(), new Range(1, 3), R) + .below(relProp(10, RelProp.of(10, START_DATE.type, of(eq, new Date()))))) + .next(endPattern(new ETyped(3,tagSeq("end"), OntologyTestUtils.PERSON.type,0), + new EProp(5,NAME.type,of(eq,"Other")))) + .next(eProp(4, OntologyTestUtils.NAME.type, of(eq, "abc"))) + .build(); + + RelationPatternRangeAsgStrategy strategy = new RelationPatternRangeAsgStrategy(); + strategy.apply(query, new AsgStrategyContext(ont)); + + Optional> quant = AsgQueryUtil.elements(query, Quant1.class).stream().filter(q -> q.geteBase().getqType().equals(QuantType.some)).findAny(); + Assert.assertFalse(AsgQueryUtil.element(query, RelPattern.class).isPresent()); + Assert.assertTrue(quant.isPresent()); + Assert.assertEquals(3, quant.get().getNext().size()); + Assert.assertEquals(1, AsgQueryUtil.elements(query, QuantBase.class).stream().filter(e->e.getNext().size()==3).count()); + Assert.assertEquals(7, AsgQueryUtil.elements(query, QuantBase.class).stream().filter(e->e.getNext().size()==2).count()); + Assert.assertEquals(0, AsgQueryUtil.elements(query, QuantBase.class).stream().filter(e->e.getNext().size()==1).count()); + + Assert.assertEquals(6, AsgQueryUtil.count(quant.get().getNext().get(0), EBase.class)); + Assert.assertEquals(11, AsgQueryUtil.count(quant.get().getNext().get(1), EBase.class)); + Assert.assertEquals(16, AsgQueryUtil.count(quant.get().getNext().get(2), EBase.class)); + + Assert.assertEquals(6, AsgQueryUtil.elements(query, Rel.class).stream().filter(r -> r.getB().size() == 1).count()); + Assert.assertEquals(6, AsgQueryUtil.elements(query, RelPropGroup.class).size()); + Assert.assertEquals(0, AsgQueryUtil.elements(query, RelProp.class).size()); + + Assert.assertEquals(3, AsgQueryUtil.elements(query, EProp.class).stream().filter(ep -> ep.geteBase().getCon().getOp().equals(eq)).count()); + Assert.assertEquals(7, AsgQueryUtil.elements(query, EPropGroup.class).size()); + + Assert.assertTrue(queryValidator.validate(query).toString(),queryValidator.validate(query).valid()); + Assert.assertNotNull(AsgQueryDescriptor.print(query)); + + } + + +} \ No newline at end of file diff --git a/fuse-model/src/main/java/com/yangdb/fuse/model/asgQuery/AsgQuery.java b/fuse-model/src/main/java/com/yangdb/fuse/model/asgQuery/AsgQuery.java index 6d35a143f..e58ff09fb 100644 --- a/fuse-model/src/main/java/com/yangdb/fuse/model/asgQuery/AsgQuery.java +++ b/fuse-model/src/main/java/com/yangdb/fuse/model/asgQuery/AsgQuery.java @@ -61,10 +61,7 @@ import com.yangdb.fuse.model.query.quant.QuantType; import javaslang.collection.Stream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; +import java.util.*; /** * Created by benishue on 23-Feb-17. @@ -375,6 +372,11 @@ public static AsgEBase relPattern(int eNum, String rType, Range rang return new AsgEBase<>(rel); } + public static AsgEBase> endPattern(T entity,EProp... props) { + EndPattern endPattern = new EndPattern<>(entity, Arrays.asList(props)); + return new AsgEBase<>(endPattern); + } + public static AsgEBase> endPattern(T entity) { EndPattern endPattern = new EndPattern<>(entity); return new AsgEBase<>(endPattern); diff --git a/fuse-model/src/main/java/com/yangdb/fuse/model/asgQuery/AsgQueryUtil.java b/fuse-model/src/main/java/com/yangdb/fuse/model/asgQuery/AsgQueryUtil.java index e550721aa..5442e8fe2 100644 --- a/fuse-model/src/main/java/com/yangdb/fuse/model/asgQuery/AsgQueryUtil.java +++ b/fuse-model/src/main/java/com/yangdb/fuse/model/asgQuery/AsgQueryUtil.java @@ -1118,19 +1118,15 @@ private static List values( return newValues; } - public static Optional> calculateNextAncestor(AsgEBase eProp, Class clazz) { + + public static Optional> calculateNextAncestor(AsgEBase eProp, Class clazz,List> allowedInPath) { final List> path = AsgQueryUtil.pathToAncestor(eProp, clazz); if(path.isEmpty()) return Optional.empty(); + //go over internal path elements make sure they all comply with allowedInPath restriction + if(path.stream().limit(path.size() - 1).skip(1).anyMatch(e -> !allowedInPath.contains(e.geteBase().getClass()))) + return Optional.empty(); return Optional.of((AsgEBase) path.get(path.size()-1)); -/* - Optional> element = Optional.empty(); - if(!path.isEmpty() && path.size()==2) - element = Optional.of((AsgEBase) path.get(1)); - if(!path.isEmpty() && path.size()==3 && QuantBase.class.isAssignableFrom(path.get(1).geteBase().getClass())) - element = Optional.of((AsgEBase) path.get(2)); - return element; -*/ } public static List> path( diff --git a/fuse-model/src/main/java/com/yangdb/fuse/model/query/entity/EndPattern.java b/fuse-model/src/main/java/com/yangdb/fuse/model/query/entity/EndPattern.java index 1ca93c679..0cb9f7e01 100644 --- a/fuse-model/src/main/java/com/yangdb/fuse/model/query/entity/EndPattern.java +++ b/fuse-model/src/main/java/com/yangdb/fuse/model/query/entity/EndPattern.java @@ -46,8 +46,13 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; +import com.yangdb.fuse.model.query.properties.EProp; +import com.yangdb.fuse.model.query.properties.EPropGroup; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; +import java.util.Optional; /** * Created by lior.perry on 16-Feb-17. @@ -55,14 +60,20 @@ @JsonInclude(JsonInclude.Include.NON_EMPTY) public class EndPattern extends EEntityBase { + private List filter = new ArrayList<>(); private T endEntity; //region Constructors public EndPattern() {} public EndPattern(T endEntity) { + this(endEntity,new ArrayList<>()); + } + + public EndPattern(T endEntity, List filter) { super(endEntity.geteNum(),endEntity.geteTag(),endEntity.getNext(),endEntity.getB()); this.endEntity = endEntity; + this.filter = filter; } public T getEndEntity() { @@ -80,14 +91,18 @@ public String geteTag() { return this.endEntity.geteTag(); } + public List getFilter() { + return filter; + } + @Override public EndPattern clone() { - return new EndPattern<>((T) getEndEntity().clone()); + return new EndPattern<>((T) getEndEntity().clone(),getFilter()); } @Override public EndPattern clone(int eNum) { - return new EndPattern<>((T) getEndEntity().clone(eNum)); + return new EndPattern<>((T) getEndEntity().clone(eNum),getFilter()); } @Override diff --git a/fuse-model/src/main/java/com/yangdb/fuse/model/query/properties/BasePropGroup.java b/fuse-model/src/main/java/com/yangdb/fuse/model/query/properties/BasePropGroup.java index bffe91373..eee754e2a 100644 --- a/fuse-model/src/main/java/com/yangdb/fuse/model/query/properties/BasePropGroup.java +++ b/fuse-model/src/main/java/com/yangdb/fuse/model/query/properties/BasePropGroup.java @@ -43,6 +43,8 @@ * */ +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; import com.yangdb.fuse.model.query.EBase; import com.yangdb.fuse.model.query.quant.QuantType; import javaslang.collection.Stream; @@ -54,6 +56,7 @@ /** * Created by moti on 5/17/2017. */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) public abstract class BasePropGroup> extends EBase { //region Constructors public BasePropGroup() { @@ -98,6 +101,7 @@ public BasePropGroup(int eNum, QuantType quantType, Iterable props, Iterable< //region Override Methods @Override + @JsonIgnore public boolean equals(Object o) { if (this == o) { return true; diff --git a/fuse-model/src/main/java/com/yangdb/fuse/model/query/properties/EPropGroup.java b/fuse-model/src/main/java/com/yangdb/fuse/model/query/properties/EPropGroup.java index 7fbfbac70..2f50265f5 100644 --- a/fuse-model/src/main/java/com/yangdb/fuse/model/query/properties/EPropGroup.java +++ b/fuse-model/src/main/java/com/yangdb/fuse/model/query/properties/EPropGroup.java @@ -44,6 +44,8 @@ * */ +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; import com.yangdb.fuse.model.query.quant.QuantType; import javaslang.collection.Stream; import javaslang.control.Option; @@ -60,6 +62,7 @@ /** * Created by benishue on 25-Apr-17. */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) public class EPropGroup extends BasePropGroup { //region Constructors public EPropGroup() { @@ -78,6 +81,10 @@ public EPropGroup(Iterable props) { super(0, props); } + public EPropGroup(List props) { + super(0, props); + } + public EPropGroup(int eNum, EProp... props) { super(eNum, props); } @@ -100,10 +107,12 @@ public EPropGroup(int eNum, QuantType quantType, Iterable props, Iterable //endregion //region Public Methods + @JsonIgnore public List findAll(Predicate propPredicate) { return this.findAll(propPredicate, this); } + @JsonIgnore public void consumeAll(Predicate propPredicate, BiConsumer consumer) { this.consumeAll(propPredicate, this, consumer); } @@ -111,11 +120,13 @@ public void consumeAll(Predicate propPredicate, BiConsumer