Closed iehdk closed 3 years ago
Given these models
class Scaffold
include Neo4j::ActiveNode
property :branch_count
property :edge_count
has_many :out, :molecules, type: :HAS_MOLECULE
end
class Molecule
include Neo4j::ActiveNode
property :atom_count
property :formula
has_many :in, :scaffolds, type: :HAS_MOLECULE
end
class Atom
include Neo4j::ActiveNode
property :element
has_many :both, :bonds, rel_class: "HasBondRel"
end
class HasBondRel
include Neo4j::ActiveRel
type :HAS_BOND
from_class "Atom"
to_class "Atom"
property :bond_type
end
I think this query should get you what you want.
Scaffold
.where(branch_count: 1, edge_count: 4)
.molecules.where(atom_count: 5, formula: 'C2N102')
.branch {
atoms.where(element: 'C')
.bonds.where(element: 'C').rel_where(bond_type: 'single')
.bonds.where(element: 'N').rel_where(bond_type: 'single')
}.branch {
atoms.where(element: 'C')
.bonds.where(element: 'O').rel_where(bond_type: 'single')
}.branch {
atoms.where(element: 'C')
.bonds.where(element: 'O').rel_where(bond_type: 'double')
}
See the docs for more info
This being said, personally I find the cypher version more expressive and more readable. Unless you're composing query chains dynamically, I'd just stick with the cypher:
Neo4j::ActiveBase.query("
MATCH (Scaffold {branch_count: 1, edge_count: 4})-[HAS_MOLECULE]->(m:Molecule {atom_count: 5, formula: 'C2N1O2'}),
(m)-[HAS_ATOM]->(a0:Atom),
(a0:Atom {element: 'C'})-[:HAS_BOND {bond_type: 'single'}]-(a1:Atom {element: 'C'}),
(a0:Atom {element: 'C'})-[:HAS_BOND {bond_type: 'single'}]-(a2:Atom {element: 'O'}),
(a0:Atom {element: 'C'})-[:HAS_BOND {bond_type: 'double'}]-(a3:Atom {element: 'O'}),
(a1:Atom {element: 'C'})-[:HAS_BOND {bond_type: 'single'}]-(a4:Atom {element: 'N'})
RETURN m
", {
# any query params go here
}).pluck(:m).first
I do in fact agree that the Cypher version is more readable - and it is way easier to find good help on Cypher queries because of the bigger user group.
I will in fact be constructing queries dynamically, but putting together a cypher string is pretty straight forward. Let me see if I can get this this ActiveBase.query
to work. (For now it goes undefined method
pluck' for #
Before was testing neo4j-core's session.query
, but I would like to get ActiveNode objects returned instead of Neo4j::Core::Node
objects - unless I should go completely neo4j-core (is that recommended?).
Regarding the error you're running into, are you sure neo4jrb is set up properly? I.e. it's connecting to the database, etc.
If you just want to dynamically compose cypher strings, definitely check out neo4j-core. You can get a neo4j-core query object with Neo4j::ActiveBase.new_query
, and use can use it like
query = Neo4j::ActiveBase.new_query
query = query.match("(Scaffold {branch_count: 1, edge_count: 4})-[HAS_MOLECULE]->(m:Molecule {atom_count: 5, formula: 'C2N1O2'})")
.match("(m)-[HAS_ATOM]->(a0:Atom)")
.match("(a0:Atom {element: 'C'})-[:HAS_BOND {bond_type: 'single'}]-(a1:Atom {element: 'C'})")
.match("(a0:Atom {element: 'C'})-[:HAS_BOND {bond_type: 'single'}]-(a2:Atom {element: 'O'})")
.match("(a0:Atom {element: 'C'})-[:HAS_BOND {bond_type: 'double'}]-(a3:Atom {element: 'O'})")
query = query.match("(a1:Atom {element: 'C'})-[:HAS_BOND {bond_type: 'single'}]-(a4:Atom {element: 'N'})")
query.pluck(:m)
The above should return ActiveNode / ActiveRel objects (assuming you've created the models).
One potential "gotcha" with neo4j-core query clause methods is that it will automatically combine clauses. I.e., multiple .match
calls (like above) are, by default, combined into the same MATCH
clause with a comma ,
. In this case that's what you want, but if you wanted separate match clauses you'd need to call .break
on the chain.
I.e. this:
query = Neo4j::ActiveBase.new_query
query = query.match("(Scaffold {branch_count: 1, edge_count: 4})-[HAS_MOLECULE]->(m:Molecule {atom_count: 5, formula: 'C2N1O2'})")
query = query.break.match("(m)-[HAS_ATOM]->(a0:Atom)")
This leads to
MATCH (Scaffold {branch_count: 1, edge_count: 4})-[HAS_MOLECULE]->(m:Molecule {atom_count: 5, formula: 'C2N1O2'})
MATCH (m)-[HAS_ATOM]->(a0:Atom)
Regarding Neo4j-core vs Neo4jrb, it's up to user preference.
I just updated the ActiveNode query in my original answer because I hadn't noticed that your query referred to the a0
node multiple times. Regardless, hopefully you get the idea.
Also, off the top of my head, I don't remember how Neo4jrb translates queries using .branch
. It may use a WHERE
clause, which, while ultimately returning the same results as your original cypher query, wouldn't be as performant (I think).
Right. Thanks for the help. I got the results = Neo4j::ActiveBase.query()
to work (though no pluck-luck, so iterating over the results instead).
CodeTriage referred me here. Seems like this can be closed. If not, how can I help?
Closing this. @iehdk, you can reopen it you still have any issues. Thanks.
Thanks @bobmazanec
How can this cypher expression:
Be translated to #ActiveNode?