Class: Extralite::Transform

Inherits:
Object
  • Object
show all
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

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

Parameters:

  • spec (Hash, nil)

    literal spec

  • block (Proc, nil)

    DSL spec



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_initializeObject



320
# File 'lib/extralite.rb', line 320

alias_method :orig_initialize, :initialize

#to_hHash

Returns the transform spec in literal form.

Returns:

  • (Hash)

    literal transform spec



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);
}