Skip to content

Commit

Permalink
Removed the /../ literal for regular expressions. Now a normal string…
Browse files Browse the repository at this point in the history
… literal is used instead
  • Loading branch information
systay authored and jexp committed Aug 22, 2012
1 parent 4001505 commit c94d3bb
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 43 deletions.
4 changes: 4 additions & 0 deletions cypher/CHANGES.txt
@@ -1,3 +1,7 @@
1.8
--------------------
o Removed the /../ literal for regular expressions. Now a normal string literal is used instead

1.8.M07 (2012-08-08)
--------------------
o Added escape characters to string literals
Expand Down
Expand Up @@ -22,7 +22,7 @@ package org.neo4j.cypher.internal.parser.v1_8
import org.neo4j.cypher.internal.commands._


trait Predicates extends Base with ParserPattern {
trait Predicates extends Base with ParserPattern with StringLiteral {
def predicate: Parser[Predicate] = predicateLvl1 ~ rep( ignoreCase("or") ~> predicateLvl1 ) ^^ {
case head ~ rest => rest.foldLeft(head)((a,b) => Or(a,b))
}
Expand Down Expand Up @@ -66,7 +66,7 @@ trait Predicates extends Base with ParserPattern {
expression ~ ">" ~ expression ^^ { case l ~ ">" ~ r => nullable(GreaterThan(l, r),l,r) } |
expression ~ "<=" ~ expression ^^ { case l ~ "<=" ~ r => nullable(LessThanOrEqual(l, r),l,r) } |
expression ~ ">=" ~ expression ^^ { case l ~ ">=" ~ r => nullable(GreaterThanOrEqual(l, r),l,r) } |
expression ~ "=~" ~ regularLiteral ^^ { case a ~ "=~" ~ b => nullable(LiteralRegularExpression(a, b),a,b) } |
expression ~ "=~" ~ stringLit ^^ { case a ~ "=~" ~ b => nullable(LiteralRegularExpression(a, b),a,b) } |
expression ~ "=~" ~ expression ^^ { case a ~ "=~" ~ b => nullable(RegularExpression(a, b),a,b) } |
expression ~> "!" ~> failure("The exclamation symbol is used as a nullable property operator in Cypher. The 'not equal to' operator is <>"))

Expand Down
Expand Up @@ -19,10 +19,10 @@
*/
package org.neo4j.cypher.internal.parser.v1_8

import org.neo4j.cypher.internal.commands.{Expression, Literal}
import org.neo4j.cypher.internal.commands.Literal

trait StringLiteral extends Base {
def stringLit: Parser[Expression] = Parser {
def stringLit: Parser[Literal] = Parser {
case in if in.atEnd => Failure("out of string", in)
case in =>
val start = handleWhiteSpace(in.source, in.offset)
Expand All @@ -35,7 +35,7 @@ trait StringLiteral extends Base {
var ls = string.toList.tail
val sb = new StringBuilder(ls.length)
var idx = start
var result: Option[ParseResult[Expression]] = None
var result: Option[ParseResult[Literal]] = None

while (!ls.isEmpty && result.isEmpty) {
val (pref, suf) = ls span {
Expand Down Expand Up @@ -77,7 +77,7 @@ trait StringLiteral extends Base {
}
}

case class EscapeProduct(result: Option[ParseResult[Expression]])
case class EscapeProduct(result: Option[ParseResult[Literal]])

private def parseEscapeChars(suf: List[Char], in:Input): Either[Char, Failure] = suf match {
case '\\' :: tail => Left('\\')
Expand Down
Expand Up @@ -19,25 +19,22 @@
*/
package org.neo4j.cypher.javacompat;

import java.io.IOException;
import java.io.Writer;
import java.util.Map;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.test.AsciiDocGenerator;
import org.neo4j.test.GraphDescription;
import org.neo4j.test.*;
import org.neo4j.test.GraphDescription.Graph;
import org.neo4j.test.GraphHolder;
import org.neo4j.test.ImpermanentGraphDatabase;
import org.neo4j.test.JavaTestDocsGenerator;
import org.neo4j.test.TestData;
import org.neo4j.visualization.asciidoc.AsciidocHelper;

import static org.neo4j.visualization.asciidoc.AsciidocHelper.*;
import java.io.IOException;
import java.io.Writer;
import java.util.Map;

import static org.neo4j.visualization.asciidoc.AsciidocHelper.createCypherSnippet;
import static org.neo4j.visualization.asciidoc.AsciidocHelper.createQueryResultSnippet;

public class IntroExamplesTest implements GraphHolder
{
Expand Down Expand Up @@ -84,7 +81,7 @@ public void intro_examples() throws Exception
+ data.get().get( "Maria" ).getId()
+ ","
+ data.get().get( "Steve" ).getId()
+ ") MATCH user-[:friend]->follower WHERE follower.name =~ /S.*/ RETURN user, follower.name ";
+ ") MATCH user-[:friend]->follower WHERE follower.name =~ 'S.*' RETURN user, follower.name ";
fw.append( "\n" );
fw.append( createCypherSnippet( query ) );
fw.append( "\nResulting in\n" );
Expand Down
42 changes: 36 additions & 6 deletions cypher/src/test/scala/org/neo4j/cypher/CypherParserTest.scala
Expand Up @@ -277,8 +277,8 @@ class CypherParserTest extends JUnitSuite with Assertions {
returns(ReturnItem(Entity("a"), "a")))
}

@Test def shouldHandleRegularComparison() {
testFrom_1_7(
@Test def shouldHandleRegularComparisonOld() {
test_1_7(
"start a = node(1) where \"Andres\" =~ /And.*/ return a",
Query.
start(NodeById("a", 1)).
Expand All @@ -287,8 +287,18 @@ class CypherParserTest extends JUnitSuite with Assertions {
)
}

@Test def shouldHandleMultipleRegularComparison1_6() {
testFrom_1_7(
@Test def shouldHandleRegularComparison() {
testFrom_1_8(
"start a = node(1) where \"Andres\" =~ 'And.*' return a",
Query.
start(NodeById("a", 1)).
where(LiteralRegularExpression(Literal("Andres"), Literal("And.*"))).
returns(ReturnItem(Entity("a"), "a"))
)
}

@Test def shouldHandleMultipleRegularComparison1_7() {
test_1_7(
"""start a = node(1) where a.name =~ /And.*/ AnD a.name =~ /And.*/ return a""",
Query.
start(NodeById("a", 1)).
Expand All @@ -298,6 +308,16 @@ class CypherParserTest extends JUnitSuite with Assertions {
}

@Test def shouldHandleMultipleRegularComparison() {
testFrom_1_8(
"""start a = node(1) where a.name =~ 'And.*' AnD a.name =~ 'And.*' return a""",
Query.
start(NodeById("a", 1)).
where(And(LiteralRegularExpression(Property("a", "name"), Literal("And.*")), LiteralRegularExpression(Property("a", "name"), Literal("And.*")))).
returns(ReturnItem(Entity("a"), "a"))
)
}

@Test def shouldHandleMultipleRegularComparison1_6() {
test_1_6(
"""start a = node(1) where a.name =~ /And.*/ AnD a.name =~ /And.*/ return a""",
Query.
Expand All @@ -317,8 +337,8 @@ class CypherParserTest extends JUnitSuite with Assertions {
)
}

@Test def shouldHandleEscapedRegexs() {
testFrom_1_7(
@Test def shouldHandleEscapedRegexs1_7() {
test_1_7(
"""start a = node(1) where a.name =~ /And\/.*/ return a""",
Query.
start(NodeById("a", 1)).
Expand All @@ -327,6 +347,16 @@ class CypherParserTest extends JUnitSuite with Assertions {
)
}

@Test def shouldHandleEscapedRegexs() {
testFrom_1_8(
"""start a = node(1) where a.name =~ 'And\\/.*' return a""",
Query.
start(NodeById("a", 1)).
where(LiteralRegularExpression(Property("a", "name"), Literal("And\\/.*"))).
returns(ReturnItem(Entity("a"), "a"))
)
}

@Test def shouldHandleGreaterThanOrEqual() {
testAll(
"start a = node(1) where a.name >= \"andres\" return a",
Expand Down
17 changes: 3 additions & 14 deletions cypher/src/test/scala/org/neo4j/cypher/ExecutionEngineTest.scala
Expand Up @@ -1214,7 +1214,7 @@ return x, p""")

val result = parseAndExecute( """
start a = node(1)
where a.name =~ /And.*/ AND a.name =~ /And.*/
where a.name =~ 'And.*' AND a.name =~ 'And.*'
return a""")

assert(List(a) === result.columnAs[Node]("a").toList)
Expand Down Expand Up @@ -1422,7 +1422,7 @@ order by a.COL1
@Test def shouldAllowStringComparisonsInArray() {
val a = createNode("array" -> Array("Cypher duck", "Gremlin orange", "I like the snow"))

val result = parseAndExecute("start a = node(1) where single(x in a.array where x =~ /.*the.*/) return a")
val result = parseAndExecute("start a = node(1) where single(x in a.array where x =~ '.*the.*') return a")

assert(List(Map("a" -> a)) === result.toList)
}
Expand Down Expand Up @@ -1642,7 +1642,7 @@ RETURN x0.name?
@Test def shouldHandleAllOperatorsWithNull() {
val a = createNode()

val result = parseAndExecute("start a=node(1) where a.x? =~ /.*?blah.*?/ and a.x? = 13 and a.x? <> 13 and a.x? > 13 return a")
val result = parseAndExecute("start a=node(1) where a.x? =~ '.*?blah.*?' and a.x? = 13 and a.x? <> 13 and a.x? > 13 return a")
assert(List(Map("a" -> a)) === result.toList)
}

Expand Down Expand Up @@ -2119,17 +2119,6 @@ RETURN x0.name?
assert(result.toList === List(Map("sum(foo)" -> 8)))
}

@Test
def with_should_not_forget_parameters() {
graph.index().forNodes("test")
val id = "bar"
val result = parseAndExecute("start n=node:test(name={id}) with count(*) as c where c=0 create x={name:{id}} return c, x", "id" -> id).toList

assert(result.size === 1)
assert(result(0)("c").asInstanceOf[Long] === 0)
assert(result(0)("x").asInstanceOf[Node].getProperty("name") === id)
}

@Ignore("This pattern is currently not supported. Revisit when we do support it.")
@Test
def two_double_optional_paths_with_shared_relationships() {
Expand Down
13 changes: 7 additions & 6 deletions cypher/src/test/scala/org/neo4j/cypher/docgen/WhereTest.scala
Expand Up @@ -55,17 +55,18 @@ class WhereTest extends DocumentingTestBase {
@Test def regular_expressions() {
testQuery(
title = "Regular expressions",
text = "You can match on regular expressions by using `=~ /regexp/`, like this:",
queryText = """start n=node(%Andres%, %Tobias%) where n.name =~ /Tob.*/ return n""",
text = "You can match on regular expressions by using `=~ \"regexp\"`, like this:",
queryText = """start n=node(%Andres%, %Tobias%) where n.name =~ 'Tob.*' return n""",
returns = """The "+Tobias+" node will be returned.""",
assertions = (p) => assertEquals(List(node("Tobias")), p.columnAs[Node]("n").toList))
}

@Test def regular_expressions_escaped() {
testQuery(
title = "Escaping in regular expressions",
text = "If you need a forward slash inside of your regular expression, escape it using a backslash (+\\+).",
queryText = """start n=node(%Andres%, %Tobias%) where n.name =~ /Some\/thing/ return n""",
text = "If you need a forward slash inside of your regular expression, escape it. Remember that back slash needs " +
"to be escaped in string literals",
queryText = """start n=node(%Andres%, %Tobias%) where n.name =~ 'Some\\/thing' return n""",
returns = """No nodes match this regular expression.""",
assertions = (p) => assertEquals(List(), p.toList))
}
Expand All @@ -74,7 +75,7 @@ class WhereTest extends DocumentingTestBase {
testQuery(
title = "Case insensitive regular expressions",
text = "By pre-pending a regular expression with `(?i)`, the whole expression becomes case insensitive.",
queryText = """start n=node(%Andres%, %Tobias%) where n.name =~ /(?i)ANDR.*/ return n""",
queryText = """start n=node(%Andres%, %Tobias%) where n.name =~ '(?i)ANDR.*' return n""",
returns = """The node with name "+Andres+" is returned.""",
assertions = (p) => assertEquals(List(Map("n" -> node("Andres"))), p.toList))
}
Expand Down Expand Up @@ -113,7 +114,7 @@ class WhereTest extends DocumentingTestBase {
text = "You can put the exact relationship type in the `MATCH` pattern, but sometimes you want to be able to do more " +
"advanced filtering on the type. You can use the special property `TYPE` to compare the type with something else. " +
"In this example, the query does a regular expression comparison with the name of the relationship type.",
queryText = """start n=node(%Andres%) match (n)-[r]->() where type(r) =~ /K.*/ return r""",
queryText = """start n=node(%Andres%) match (n)-[r]->() where type(r) =~ 'K.*' return r""",
returns = """This returns relationships that has a type whose name starts with K.""",
assertions = (p) => assertEquals("KNOWS", p.columnAs[Relationship]("r").toList.head.getType.name()))
}
Expand Down

0 comments on commit c94d3bb

Please sign in to comment.