Class: Extralite::Transform
- Inherits:
-
Object
- Object
- Extralite::Transform
- Defined in:
- ext/extralite/transform.c,
lib/extralite.rb,
ext/extralite/transform.c
Overview
This class represents a specification for retrieving structured data as the result of a query. When querying data from multiple tables using joins, transforms allow you to retrieve the results as an object graph containing the different entities.
For example, a posts table and a tags table may be joined to represent a many-to-many relationship:
select
posts.id, posts.content,
tags.id, tags.name
from posts
left outer join posts_tags on posts_tags.post_id = posts.id
left outer join tags on posts_tags.tag_id = tags.id
Normally, the resulting data will be represented as an array of hashes, of the form:
[
{ posts_id: 1, posts_content: "foo", tags_id: 1, tags_name: "blah" },
{ posts_id: 2, posts_content: "bar", tags_id: 1, tags_name: "blah" },
{ posts_id: 2, posts_content: "bar", tags_id: 2, tags_name: "bleh" },
{ posts_id: 3, posts_content: "baz", tags_id: 2, tags_name: "blah" },
...
]
Those results, while containing all the information that we requested, also contain a lot of duplication: the same post entity may appear in multiple rows, and the same tag entity may be repeated for different posts.
With a properly configured transform, we can convert those flat rows with duplicate data into an object graph that represents the different entities (posts and tags), such that each post entity will also include the corresponding tags. Furthermore, we can eliminate duplication by using identity maps to create each entity only once, and reuse it if it repeats (such as in the case of tags):
[
{ id: 1, content: "foo", tags: [{id: 1, name: "blah"}] },
{ id: 2, content: "bar", tags: [{id: 1, name: "blah"}, {id: 2, name: "bleh"}] },
{ id: 3, content: "baz", tags: [{id: 2, name: "bleh"}] }
]
The transform is expressed using the transform DSL:
transform = Extralite::Transform.new do
{
id: integer.identity,
content: text,
tags: [{
id: integer.identity,
name: text
}]
}
end
To use the transform we can feed it into Database#query:
db.query(transform, sql) #=> [...]
A transform may also be used with a prepared query:
q = db.prepare(transform, sql)
q.to_a #=> [...]
Transforms can also be used for type coercion. If the type is 'auto' or not specified, the returned value will reflect the native type of the value in the database. The following types are supported:
- auto: native database type
- integer: 64-bit integer
- float: floating point number
- text: text/string value
- bool: a boolean (after coercion to integer)
- json: parsed JSON representation
- proc: a custom proc for converting a value
To use the proc type, specify the proc as the type, e.g.:
Extralite::Transform.new do
{
stamp: ->(s) { Time.at(s) },
value: float
}
end
Defined Under Namespace
Classes: DSLContext
Instance Method Summary collapse
-
#initialize(spec) ⇒ void
constructor
call-seq: Extralite::Transform.new(literal_spec) -> transform Extralite::Transform.new { dsl_spec } -> transform.
- #orig_initialize ⇒ Object
-
#to_h ⇒ Hash
Returns the transform spec in literal form.
Constructor Details
#initialize(spec) ⇒ void
call-seq: Extralite::Transform.new(literal_spec) -> transform Extralite::Transform.new { dsl_spec } -> transform
Initializes a new transform with with the given spec. The spec may be expressed as a hash containing the columns and any nested entities, or as a block with the transform DSL.
# literal transform spec
transform = Extralite::Transform.new(
columns: {
id: { type: :integer, identity: true },
content: { type: :text },
author: {
type: :relation,
columns: {
id: { type: :integer, identity: true },
name: { type: :text },
}
}
}
)
# DSL spec
transform = Extralite::Transform.new do
{
id: integer.identity,
content: text,
author: {
id: integer.identity,
name: text
}
}
end
360 361 362 363 364 365 |
# File 'lib/extralite.rb', line 360 def initialize(spec = nil, &block) return orig_initialize(spec) if spec raise "No spec given" if !block orig_initialize(dsl_to_spec(block)) end |
Instance Method Details
#orig_initialize ⇒ Object
320 |
# File 'lib/extralite.rb', line 320 alias_method :orig_initialize, :initialize |
#to_h ⇒ Hash
Returns the transform spec in literal form.
383 384 385 386 |
# File 'ext/extralite/transform.c', line 383
VALUE Transform_to_h(VALUE self) {
Transform_t *t = self_to_transform(self);
return transform_node_to_obj(t->root);
}
|