Search code examples
neo4jcypher

How can I find the index of an object which contains an array as a property from a collection in Cypher?


I am trying to find the index of an item in a collection of custom objects (so not a collection of nodes). I am using apoc.coll.indexOf(collection, object), but it is returning -1 instead of the actual index.

The queries below show the problem. The first query creates two test nodes, each with an array of booleans called someArray. The second query matches all nodes with the given label, collects them into a variable called collection, and lastly tries to get the index of an object with the same someArray field as an empty array. I expected it to return either 0 or 1 (depending on how the collection is sorted), but it returns -1.

CREATE (test:Test { someArray: [true, false, true] })
CREATE (test2:Test { someArray: [] })
MATCH (n:Test)

WITH COLLECT({
    someArray: n.someArray
}) as collection

RETURN apoc.coll.indexOf(collection, { someArray: [] }) // Returns -1

How do I find the correct index in this case? If this is intended behaviour (for example, comparing arrays by reference) can you point me to the documentation page?

(this is Neo4j 4.4)


Solution

  • This seems to be caused by a weird bug in apoc.coll.indexOf.

    This query:

    MATCH (n:Test)
    WITH COLLECT({
        someArray: n.someArray
    }) as collection
    RETURN collection
    

    returns this:

    [{someArray: [true, false, true]}, {someArray: []}]
    

    If you hardcode that list into this query (I tested on neo4j 5.5):

    RETURN apoc.coll.indexOf([{someArray: [true, false, true]}, {someArray: []}], {someArray: []})
    

    the result is 1.

    So apoc.coll.indexOf unexpectedly treats collection differently than the "same" hardcoded list.

    You should create a neo4j issue about this odd behavior.

    [UPDATE]

    Workaround (where the test value is passed in as the $val parameter):

    MATCH (n:Test)
    WITH COLLECT(
      {someArray: n.someArray}
    ) AS collection
    RETURN [i IN RANGE(0, SIZE(collection)-1) WHERE collection[i] = $val | i] AS result
    

    result is a possibly-empty list of matching indexes. This query is similar to @CharchitKapoor's, but we return all matching indexes.