0

I'm working on a Rails application that tracks transactions between addresses, using Neo4j as the database. When I try to run this query:

Neo4j::Session.query("MATCH (sender:Address {address: 'abc123'})-[transaction:SENT_TO]->(receiver:Address) RETURN sender, transaction, receiver").to_a

I get back an array, as expected, but the transaction item returns as a CypherRelationship pointer rather than a struct of its properties like it does for the sender and receiver addresses:

=> [#<struct sender=#<Address uuid: nil, address: "abc123", user_tags: nil>, transaction=CypherRelationship 30, receiver=#<Address uuid: nil, address: "xyz321", user_tags: nil>>]

If I try to get one of the transaction properties by doing something like transaction.props['amount'] it causes a new Cypher query to run. Since there are multiple properties I want to get back on each transaction, and a large number of transactions that will be returned, I would like for the initial query to simply return the properties of the relationship as a struct within the array.

This works how I want it to when I run the query directly in Neo4j, so I suspect it's an issue with the neo4j-core gem (or my use of it)... any ideas?

Thanks for your help!

btw09
  • 1
  • 1

1 Answers1

0

The Neo4j and Neo4j-core gems provide object representations of nodes and relationships, but which object you'll wind up with depends entirely on the classes defined within your app. When a node or relationship is returned, the Neo4j gem attempts to locate a model that is responsible for it based on labels or rel type. When one is found, it returns an instance of that class; when one is not found, it returns an instance of one of the basic classes provided by Neo4j-core: CypherNode or CypherRelationship.

In your case, you're not getting structs of properties, you're getting structs of nodes of type Address, since you have that model defined, and CypherRelationship, so you don't have a model defined for that type of relationship. Instances of your models have convenience methods for getting and setting properties but instances of CypherNode and CypherRelationship do not, so call props and you'll have access to the relationship's properties.

You might want to take a look at the CypherRelationship code. https://github.com/neo4jrb/neo4j-core/blob/master/lib/neo4j-server/cypher_relationship.rb is the bulk of it but it inherits from https://github.com/neo4jrb/neo4j-core/blob/master/lib/neo4j/relationship.rb, which is mostly an abstract class but it also provides a few other methods. It has a pretty basic public interface, mostly convenience methods for working with the relationships. CypherNode is very similar: https://github.com/neo4jrb/neo4j-core/blob/master/lib/neo4j-server/cypher_node.rb.

In general, my recommendation is to define models using Neo4j::ActiveRel for any relationships that you plan on working with as objects in Ruby. You can find the docs for it at http://neo4jrb.readthedocs.io/en/v7.0.2/ActiveRel.html.

Two other notes:

  • You will receive Structs instead of instances of CypherNode/CypherRelationship or your model classes if your query is instructed to return properties instead of complete objects.

  • If you use the Cypher DSL instead of passing the query in, you can use the pluck method and receive Arrays of objects instead of structs, if you'd like. return(symbols).to_a will work, too.


Neo4j::Session.current.query
      .match("(sender:Address {address: {addr} })-[transaction:SENT_TO]->(receiver:Address)")
      .params(addr: 'abc123')
      .pluck(:sender, :transaction, :receiver)
subvertallchris
  • 5,282
  • 2
  • 25
  • 43