Skip to content

Commit

Permalink
Fix bug in node comparison
Browse files Browse the repository at this point in the history
Fix a bug where comparison of nodes was incorrect because of labels appearing in different order.
  • Loading branch information
loveleif authored Sep 27, 2022
1 parent 946eccb commit 1018021
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,26 @@
*/
package org.opencypher.tools.tck.values

import scala.annotation.tailrec
import org.opencypher.tools.tck.values.OrderingCompatibility.Implicits.seqOrdering

object CypherValue {
def apply(s: String, orderedLists: Boolean = true): CypherValue = {
val parser = new CypherValueParser(orderedLists)
parser.parse(s)
}

implicit val ordering: Ordering[CypherValue] = new Ordering[CypherValue] {
private val stringSetOrdering: Ordering[Set[String]] = Ordering.by(set => set.toSeq.sorted)

@tailrec
override def compare(x: CypherValue, y: CypherValue): Int = {
val stringOrdering = implicitly[Ordering[String]]
stringOrdering.compare(x.toString, y.toString)
(x, y) match {
case (CypherNode(lsA, psA), CypherNode(lsB, psB)) =>
val labelOrder = stringSetOrdering.compare(lsA, lsB)
if (labelOrder != 0) labelOrder else compare(psA, psB)
case (a, b) => a.toString.compareTo(b.toString)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2015-2022 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Attribution Notice under the terms of the Apache License 2.0
*
* This work was created by the collective efforts of the openCypher community.
* Without limiting the terms of Section 6, any Derivative Work that is not
* approved by the public consensus process of the openCypher Implementers Group
* should not be described as “Cypher” (and Cypher® is a registered trademark of
* Neo4j Inc.) or as "openCypher". Extensions by implementers or prototypes or
* proposals for change that have been documented or implemented should only be
* described as "implementation extensions to Cypher" or as "proposed changes to
* Cypher that are not yet approved by the openCypher community".
*/
package org.opencypher.tools.tck.values

import scala.language.higherKinds
import scala.math.Ordering.Implicits.seqDerivedOrdering

object OrderingCompatibility {
trait Implicits {
implicit def seqOrdering[CC[X] <: scala.collection.Seq[X], T](implicit ord: Ordering[T]): Ordering[CC[T]] = scala.math.Ordering.Implicits.seqDerivedOrdering
}
object Implicits extends Implicits { }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2015-2022 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Attribution Notice under the terms of the Apache License 2.0
*
* This work was created by the collective efforts of the openCypher community.
* Without limiting the terms of Section 6, any Derivative Work that is not
* approved by the public consensus process of the openCypher Implementers Group
* should not be described as “Cypher” (and Cypher® is a registered trademark of
* Neo4j Inc.) or as "openCypher". Extensions by implementers or prototypes or
* proposals for change that have been documented or implemented should only be
* described as "implementation extensions to Cypher" or as "proposed changes to
* Cypher that are not yet approved by the openCypher community".
*/
package org.opencypher.tools.tck.values

import scala.math.Ordering.Implicits.seqOrdering

object OrderingCompatibility {
trait Implicits {
implicit def seqOrdering[CC[X] <: scala.collection.Seq[X], T](implicit ord: Ordering[T]): Ordering[CC[T]] = scala.math.Ordering.Implicits.seqOrdering
}
object Implicits extends Implicits { }
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ package org.opencypher.tools.tck.values
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers

import scala.collection.SortedSet

class CypherValueTest extends AnyFunSuite with Matchers {

test("list comparisons") {
Expand Down Expand Up @@ -107,4 +109,60 @@ class CypherValueTest extends AnyFunSuite with Matchers {
l2 should equal(l1)
}

test("node comparison with labelled nodes") {
CypherNode(Set("A", "B")) should equal(CypherNode(Set("B", "A")))
CypherNode(Set("A", "C")) should not equal(CypherNode(Set("A", "B")))
}

test("list comparison with labelled nodes") {
val nodeList1 = List(
CypherNode(scala.collection.immutable.SortedSet("A", "D")),
CypherNode(scala.collection.immutable.SortedSet("B", "C"))
)
val nodeList2 = List(
CypherNode(scala.collection.immutable.SortedSet("D", "A")(Ordering.String.reverse)),
CypherNode(scala.collection.immutable.SortedSet("C", "B")(Ordering.String.reverse))
)

CypherOrderedList(nodeList1) should equal(CypherOrderedList(nodeList2))
CypherOrderedList(nodeList2) should equal(CypherOrderedList(nodeList1))
CypherOrderedList(nodeList1) should equal(CypherUnorderedList(nodeList2))
CypherOrderedList(nodeList2) should equal(CypherUnorderedList(nodeList1))
CypherOrderedList(nodeList1.reverse) should equal(CypherUnorderedList(nodeList2))
CypherOrderedList(nodeList1) should equal(CypherUnorderedList(nodeList2.reverse))
CypherOrderedList(nodeList1.reverse) should equal(CypherUnorderedList(nodeList1))
CypherOrderedList(nodeList1) should equal(CypherUnorderedList(nodeList1.reverse))

CypherOrderedList(nodeList1.reverse) should not equal(CypherOrderedList(nodeList2))
CypherOrderedList(nodeList1) should not equal(CypherOrderedList(nodeList2.reverse))
CypherOrderedList(nodeList1.reverse) should not equal(CypherOrderedList(nodeList1))
CypherOrderedList(nodeList1) should not equal(CypherOrderedList(nodeList1.reverse))

CypherUnorderedList(nodeList1) should equal(CypherUnorderedList(nodeList2))
CypherUnorderedList(nodeList2) should equal(CypherUnorderedList(nodeList1))
CypherUnorderedList(nodeList1) should equal(CypherOrderedList(nodeList2))
CypherUnorderedList(nodeList2) should equal(CypherOrderedList(nodeList1))
CypherUnorderedList(nodeList1.reverse) should equal(CypherUnorderedList(nodeList2))
CypherUnorderedList(nodeList1) should equal(CypherUnorderedList(nodeList2.reverse))
CypherUnorderedList(nodeList1.reverse) should equal(CypherOrderedList(nodeList2))
CypherUnorderedList(nodeList1) should equal(CypherOrderedList(nodeList2.reverse))
CypherUnorderedList(nodeList1.reverse) should equal(CypherUnorderedList(nodeList1))
CypherUnorderedList(nodeList1) should equal(CypherUnorderedList(nodeList1.reverse))
CypherUnorderedList(nodeList1.reverse) should equal(CypherOrderedList(nodeList1))
CypherUnorderedList(nodeList1) should equal(CypherOrderedList(nodeList1.reverse))
}

test("list of lists comparison with labelled nodes") {
CypherValue("[[(:A:D), (:B:C)], [(:AA:DD), (:BB:CC)]]", orderedLists = false) should
equal(CypherValue("[[(:D:A), (:C:B)], [(:DD:AA), (:CC:BB)]]", orderedLists = false))

CypherValue("[[(:A:D), (:B:C)], [(:AA:DD), (:BB:CC)]]", orderedLists = true) should
equal(CypherValue("[[(:D:A), (:C:B)], [(:DD:AA), (:CC:BB)]]", orderedLists = true))

CypherValue("[[(:AA:DD), (:BB:CC)], [(:A:D), (:B:C)]]", orderedLists = false) should
equal(CypherValue("[[(:D:A), (:C:B)], [(:DD:AA), (:CC:BB)]]", orderedLists = false))

CypherValue("[[(:AA:DD), (:BB:CC)], [(:A:D), (:B:C)]]", orderedLists = true) should
not equal(CypherValue("[[(:D:A), (:C:B)], [(:DD:AA), (:CC:BB)]]", orderedLists = true))
}
}

0 comments on commit 1018021

Please sign in to comment.