-
-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom transforms for creating arbitrarily complex POROs with identity maps #76
Comments
Internal reperesentation of the transform: SPEC = {
identity_idx: 0,
columns: [
:id,
:title,
{
name: :author,
dentity_idx: 0,
columns: [:id, :name]
}
]
} |
Algorithm (without identity map): class Transform
def process(row_hash, spec)
spec[:columns].each do |c|
case c
when Hash
relation_hash = row_hash[c[:name]] = {}
process(relation_hash, c)
else
row_hash[c] = get_next_column_value
end
end
end
end |
With identity map: def prepare_spec(spec)
if spec[:identity_idx]
spec[:id_map] = {}
end
spec[:columns].each do |c|
prepare_spec(c) if c.is_a?(Hash)
end
end
def process(spec)
id = get_column_value(spec[:identity_idx])
if (row = spec[:id_map][id])
return row
end
row = {}
populate_row(row, spec)
row
end
def populate_row(row, spec)
spec[:columns].each_with_index do |c, i|
case c
when Hash
row[c[:name]] = process(c)
else
value = row[c] = get_next_column_value
if i == spec[:identity_idx]
spec[:id_map][value] = row
end
end
end
end |
Working example here: https://gist.github.com/noteflakes/3653ec742d76fe5b6e3557315fe8f503 I'm really pleased with how relatively simple it is. Remains to be seen how useful that can be... There are some additional features to add:
|
Some more possibilities: # custom value conversion
t = Extralite.transform do
stamp(Time) # Time.at(v)
value(Float) # v.to_f
flag(:bool) # (v != 0)
flag(MyClass) # MyClass.new(v)
tag { |v| "(#{v})" } # custom conversion proc
end
# JSON object
t = Extralite.transform do
__set(:json)
end
# Merge JSON
t = Extralite.transform do
id.identity
name
__merge(:json)
end
# return identity map instead of array
t = Extralite.transform do
emit_map
id.identity
name
end
# one_to_many relationship
t = Extralite.transform do
id.identity
title
tags [{
id.identity
name
}]
end
# one_to_many emitted as single values
t = Extralite.transform do
id.identity
title
tags [{
emit_single_value name
id.identity
name
}]
end |
How are transforms used for actually fetching values: t = Extralite.transform do
id.identity
title
tags [{
id.identity
name
}]
end
sql = <<~SQL
select
articles.id, articles.title,
tags.id, tags.name
from
articles
join
tags on tags.article_id = articles.id
SQL
db.query(t, sql) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Right now Extralite supports custom transforms by providing a transform proc that takes the raw values and can return custom objects. A related idea is the ability to turn query results into arbitrarily complex PORO objects, with support for identity maps. This can be especially useful when performing joins where we want to turn the results into a bunch of related objects.
Some preliminary work towards this goal was done in commit bbcf12e.
Example query:
What we want is an identity map expressing relations as nested objects:
The projection transform is expressed using a DSL:
So instead of passing a proc, we pass a transform object. The transform is performed at the C-level, accompanied by identity maps for both
book
andauthor
objects.An important limitation for this is that if identity maps are used, the entire result set must be converted before any rows are returned to the application code.
The text was updated successfully, but these errors were encountered: