With the neo4j
shard, you can pass types to a query and it will yield a tuple of those types to a block to be destructured into individual arguments:
query = "MATCH (user:User)-[:MEMBER_OF]->(group) RETURN user, group"
txn.exec_cast query, {User, Group} do |(user, group)|
# ...
end
But if you have a really complicated query that returns a lot of things (not super uncommon in graph queries), I want to be able to retrieve the returned values by name, so I’m trying to wrap the tuple in a Result
type that will still allow tuple-like destructuring (for simpler queries) but also allow referencing by name. For example:
query = "MATCH (user:User)-[:MEMBER_OF]->(group) RETURN user, group"
txn.exec_cast query, {User, Group} do |result|
result["user"] # User(@id=... name=...)
result["group"] # Group(@id=... name=...)
end
The problem I’m coming across is that the type of result[0]
(using numeric literal, not a variable) in this case is User | Group
, not User
. Here’s the code for Result
(I’ve tried a few things, but this is where it stands right now):
struct Result(*T)
getter values
def initialize(@names : Array(String), @values : T)
end
def size
@values.size
end
@[AlwaysInline]
def [](index : Int)
index += {{T.size}} if index < 0
{% for i in 0...T.size %}
return @values[{{i}}] if {{i}} == index
{% end %}
raise IndexError.new
end
@[AlwaysInline]
def [](name : String)
{% for i in 0...T.size %}
return @values[{{i}}] if @names[{{i}}] == name
{% end %}
raise KeyError.new("No RETURN binding named #{name}, only #{@names.join(',')}")
end
end
result = Result(User, Group).new(%w[user group], {user, group})
u, g = result
typeof(u) # => (Group | User)
I’m having a lot of trouble making it work like Tuple
(despite trying to use almost identical code). It makes me wonder, does the compiler have special handling for tuples to make this work?