Skip to content
Browse files

Unnamed pattern elements are now excluded from the identifier uniquen…

…ess rule
  • Loading branch information...
1 parent 8758f96 commit 99dfca2d80f70da792458d6e1a301b22b3511347 @systay committed Aug 29, 2012
View
55 cypher/src/main/scala/org/neo4j/cypher/internal/pipes/matching/History.scala
@@ -22,7 +22,7 @@ package org.neo4j.cypher.internal.pipes.matching
import collection.Map
import collection.Set
import collection.mutable.{Map => MutableMap}
-import org.neo4j.graphdb.{Relationship, Path, Node}
+import org.neo4j.graphdb.{PropertyContainer, Relationship, Path, Node}
import scala.Option
/**
@@ -31,66 +31,81 @@ import scala.Option
*
* It's also used to emit the subgraph when the whole pattern has been matched (that's the toMap method)
*/
-abstract class History {
- val seen : Set[MatchingPair]
+trait History {
+ def seen: Set[MatchingPair]
def filter(relationships: Set[PatternRelationship]): Set[PatternRelationship] = relationships.filterNot(r => matches(r))
def filter(relationships: Seq[GraphRelationship]): Seq[GraphRelationship] = relationships.filterNot(gr => gr match {
- case SingleGraphRelationship(r) => matches(r)
+ case SingleGraphRelationship(r) => matches(r)
case VariableLengthGraphRelationship(p) => matches(p)
})
- def matches(p : Any) : Boolean
+ def matches(p: Any): Boolean
def add(pair: MatchingPair): History
- val toMap: Map[String, Any]
+ def toMap: Map[String, Any]
+
+ def contains(p: MatchingPair): Boolean
+
+ def containsElementWithDifferentName(name:String, element: PropertyContainer): Boolean
- def contains(p : MatchingPair) : Boolean
override def toString: String = "History(%s)".format(seen.mkString("[", "], [", "]"))
}
-class InitialHistory(source : Map[String,Any]) extends History {
+case class InitialHistory(source: Map[String, Any]) extends History {
lazy val seen = Set[MatchingPair]()
def matches(p: Any) = false
- def contains(p : MatchingPair) = false
+ def contains(p: MatchingPair) = false
- def add(pair: MatchingPair) = new AddedHistory(this,pair)
+ def add(pair: MatchingPair) = new AddedHistory(this, pair)
val toMap = source
+
+ def containsElementWithDifferentName(name: String, element: PropertyContainer): Boolean = source.exists {
+ case (k, v: PropertyContainer) => name != k && v == element
+ case (k, SingleGraphRelationship(r)) => (name != k && r == element)
+ case _ => false
+ }
}
-class AddedHistory(val parent : History, val pair : MatchingPair) extends History {
+case class AddedHistory(parent: History, pair: MatchingPair) extends History {
lazy val seen = parent.seen + pair
def matches(p: Any) = pair.matches(p) || parent.matches(p)
- def contains(p : MatchingPair) = pair == p || parent.contains(p)
+ def contains(p: MatchingPair) = pair == p || parent.contains(p)
+
+ def containsElementWithDifferentName(name:String, element: PropertyContainer): Boolean = (pair.patternElement.key,pair.entity) match {
+ case (k, v: Node) => (name != k && v == element) || parent.containsElementWithDifferentName(name, element)
+ case (k, v: SingleGraphRelationship) => (name != k && v.rel == element) || parent.containsElementWithDifferentName(name, element)
+ case _ => parent.containsElementWithDifferentName(name, element)
+ }
- def add(pair: MatchingPair) = if (contains(pair)) this else new AddedHistory(this,pair)
+ def add(pair: MatchingPair) = if (contains(pair)) this else new AddedHistory(this, pair)
lazy val toMap = {
parent.toMap ++ toSeq(pair)
}
- def toSeq(p: MatchingPair) : Seq[(String,Any)] = {
+ def toSeq(p: MatchingPair): Seq[(String, Any)] = {
p match {
- case MatchingPair(pe: PatternNode, entity: Node) => Seq(pe.key -> entity)
- case MatchingPair(pe: PatternRelationship, entity: SingleGraphRelationship) => Seq(pe.key -> entity.rel)
- case MatchingPair(pe: VariableLengthPatternRelationship, null) => Seq(pe.key -> null) ++ pe.relIterable.map( _ -> null)
- case MatchingPair(pe: PatternRelationship, null) => Seq(pe.key -> null)
+ case MatchingPair(pe: PatternNode, entity: Node) => Seq(pe.key -> entity)
+ case MatchingPair(pe: PatternRelationship, entity: SingleGraphRelationship) => Seq(pe.key -> entity.rel)
+ case MatchingPair(pe: VariableLengthPatternRelationship, null) => Seq(pe.key -> null) ++ pe.relIterable.map(_ -> null)
+ case MatchingPair(pe: PatternRelationship, null) => Seq(pe.key -> null)
case MatchingPair(pe: VariableLengthPatternRelationship, entity: VariableLengthGraphRelationship) => {
relationshipIterable(pe, entity) match {
case Some(aPair) => Seq(pe.key -> entity.path, aPair)
- case None => Seq(pe.key -> entity.path)
+ case None => Seq(pe.key -> entity.path)
}
}
}
}
- private def relationshipIterable(pe: VariableLengthPatternRelationship, entity: VariableLengthGraphRelationship):Option[(String, Any)] = pe.relIterable.map(_->entity.relationships)
+ private def relationshipIterable(pe: VariableLengthPatternRelationship, entity: VariableLengthGraphRelationship): Option[(String, Any)] = pe.relIterable.map(_ -> entity.relationships)
}
View
8 cypher/src/main/scala/org/neo4j/cypher/internal/pipes/matching/MatchingContext.scala
@@ -54,11 +54,11 @@ class MatchingContext(boundIdentifiers: SymbolTable,
}
private def decideWhichMatcherToUse(): MatcherBuilder = {
- if(SimplePatternMatcherBuilder.canHandle(patternGraph)) {
- new SimplePatternMatcherBuilder(patternGraph, predicates, symbols)
- } else {
+// if(SimplePatternMatcherBuilder.canHandle(patternGraph)) {
+// new SimplePatternMatcherBuilder(patternGraph, predicates, symbols)
+// } else {
new PatterMatchingBuilder(patternGraph, predicates)
- }
+// }
}
}
View
54 cypher/src/main/scala/org/neo4j/cypher/internal/pipes/matching/PatternMatcher.scala
@@ -19,7 +19,7 @@
*/
package org.neo4j.cypher.internal.pipes.matching
-import org.neo4j.graphdb.Node
+import org.neo4j.graphdb.{PropertyContainer, Node}
import org.neo4j.cypher.internal.commands.{True, Predicate}
import collection.Map
@@ -46,11 +46,11 @@ class PatternMatcher(bindings: Map[String, MatchingPair], predicates: Seq[Predic
return false
}
- val newHistory = history.add(current)
- if (!isMatchSoFar(newHistory)) {
+ if (!isMatchSoFar(history,current)) {
debug("failed subgraph because of predicate")
return false
}
+ val newHistory = history.add(current)
val notYetVisited: List[PatternRelationship] = getPatternRelationshipsNotYetVisited(current.patternNode, history)
@@ -80,20 +80,21 @@ class PatternMatcher(bindings: Map[String, MatchingPair], predicates: Seq[Predic
false
} else {
- val newHistory = history.add(current)
+ if (isMatchSoFar(history, current)) {
+ val newHistory = history.add(current)
+
+ currentRel.predicate match {
+ case True() =>
+ case p => if (!p.isMatch(newHistory.toMap)) return false
+ }
- currentRel.predicate match {
- case True() =>
- case p => if(!p.isMatch(newHistory.toMap)) return false
- }
- if (isMatchSoFar(newHistory)) {
val nextNode = rel.getOtherNode(gNode)
val nextPair = MatchingPair(nextPNode, nextNode)
remaining.find(_.patternElement.key == nextPNode.key) match {
- case None => traverseNode(remaining ++ Set(nextPair), newHistory, yielder)
+ case None => traverseNode(remaining ++ Set(nextPair), newHistory, yielder)
case Some(x) => if (x.entity == nextNode)
traverseNode(remaining ++ Set(nextPair), newHistory, yielder)
else {
@@ -130,7 +131,7 @@ class PatternMatcher(bindings: Map[String, MatchingPair], predicates: Seq[Predic
val relationships = currentNode.getGraphRelationships(currentRel)
val step1 = history.filter(relationships)
- val notVisitedRelationships: Seq[GraphRelationship] = step1.
+ val notVisitedRelationships: Seq[GraphRelationship] = relationships.
filter(x => alreadyPinned(currentRel, x))
val nextPNode = currentRel.getOtherNode(pNode)
@@ -154,10 +155,33 @@ class PatternMatcher(bindings: Map[String, MatchingPair], predicates: Seq[Predic
false
}
- private def isMatchSoFar(history: History): Boolean = {
- val m = history.toMap
- val predicate = predicates.filter(predicate=> !predicate.containsIsNull && predicate.dependencies.map(_.name).forall(m contains))
- predicate.forall(_.isMatch(m))
+ private def isMatchSoFar(history: History, next:MatchingPair): Boolean = {
+ lazy val m = history.toMap
+ val key = next.patternElement.key
+
+ val predicate = predicates.filter(predicate=> {
+
+ lazy val containsNull = predicate.containsIsNull
+ lazy val dependenciesMet = predicate.dependencies.map(_.name).forall(m contains)
+ lazy val currentDependency = predicate.dependencies.exists(_.name == key)
+
+ !containsNull && currentDependency && dependenciesMet
+ })
+
+ val a: Boolean = !key.startsWith(" UNNAMED")
+ val b: Boolean = next.entity.isInstanceOf[PropertyContainer] || next.entity.isInstanceOf[SingleGraphRelationship]
+
+ val elementAlreadyExists = if (a&&b) {
+ val propContainer: PropertyContainer = next.entity match {
+ case n: Node => n
+ case SingleGraphRelationship(r) => r
+ }
+ history.containsElementWithDifferentName(key, propContainer)
+ } else {
+ false
+ }
+
+ !elementAlreadyExists && predicate.forall(_.isMatch(m))
}
private def traverseNextNodeOrYield[U](remaining: Set[MatchingPair], history: History, yielder: Map[String, Any] => U): Boolean = {
View
14 cypher/src/test/scala/org/neo4j/cypher/ExecutionEngineTest.scala
@@ -2248,4 +2248,18 @@ RETURN x0.name?
assert(result.toList === List(3,4,5))
}
+ @Test
+ def unnamed_nodes_can_point_to_nodes_already_in_context() {
+ val a = createNode()
+ relate(refNode, a)
+ relate(refNode, a)
+
+
+ val result = parseAndExecute("DECLARE x,r1,r2 NON-UNIQUE START n=node(0) MATCH n-[]->()<-[r2]-x RETURN count(*)")
+
+ parseAndExecute("NON UNIQUE x START n=node:person(name='A') MATCH (n)-[:LIKES]->(x) RETURN x.name")
+
+ println(result.dumpToString())
+ }
+
}

0 comments on commit 99dfca2

Please sign in to comment.
Something went wrong with that request. Please try again.