hyper_record 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,57 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ # Like a regular database, each table in Hypertable has a fixed list
4
+ # of columns. However, Hypertable allows flexible schemas through the
5
+ # use of column qualifiers. Suppose a table is defined to have a single
6
+ # column called misc.
7
+ #
8
+ # CREATE TABLE pages (
9
+ # 'misc'
10
+ # )
11
+ #
12
+ # In Hypertable, each traditional database column is referred to as
13
+ # a column family. Each column family can have a theoretically infinite
14
+ # number of qualified instances. An instance of a qualified column
15
+ # is referred to using the column_family:qualifer notation. e.g.,
16
+ #
17
+ # misc:red
18
+ # misc:green
19
+ # misc:blue
20
+ #
21
+ # These qualified column instances do not need to be declared as part
22
+ # of the table schema. The table schema itself does not provide
23
+ # an indication of whether a column family has been used with qualifiers.
24
+ # As a results, we must explicitly declare intent to use a column family
25
+ # in a qualified manner in our class definition. The resulting AR
26
+ # object models the column family as a Hash.
27
+ #
28
+ # class Page < ActiveRecord::HyperBase
29
+ # qualified_column :misc
30
+ # end
31
+ #
32
+ # p = Page.new
33
+ # p.ROW = 'page_1'
34
+ # p.misc['url'] = 'http://www.zvents.com/'
35
+ # p.misc['hits'] = 127
36
+ # p.save
37
+
38
+ class QualifiedColumn < Column
39
+ attr_accessor :qualifiers
40
+
41
+ def initialize(name, default, sql_type = nil, null = true)
42
+ @qualifiers ||= []
43
+ super
44
+ end
45
+
46
+ def klass
47
+ Hash
48
+ end
49
+
50
+ def default
51
+ h = Hash.new
52
+ h.default = @default
53
+ h
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,107 @@
1
+ # Since Hypertable does not support join within queries, the association
2
+ # information is written to each table using qualified columns instead of
3
+ # using a separate JOIN table (as is the case for has_and_belongs_to_many
4
+ # associations in a regular RDBMS).
5
+ #
6
+ # For instance, assume that you have two models (Book and Author) in a
7
+ # has_and_belongs_to_many association. The HABTM association in a traditional
8
+ # RDBMS requires a join table (authors_books) to record the associations
9
+ # between objects. In Hypertable, instead of using a separate join table,
10
+ # each table has a column dedicated to recording the associations.
11
+ # Specifically, the books table has an author_id column and the authors
12
+ # table has a book_id column.
13
+ #
14
+ # Column qualifiers are used so we can record as many associated objects are
15
+ # necessary. If an Author with the row key charles_dickens is added to a
16
+ # Book with the row key tale_of_two_cities, then a cell called
17
+ # author_id:charles_dickens is added to the Book object and a cell
18
+ # called book_id:tale_of_two_cities is added to the Author object. The
19
+ # value of the cells is inconsequential - their presence alone indicates
20
+ # an association between the objects.
21
+ #
22
+ # When an object in an HABTM association is destroyed, the corresponding
23
+ # entries in the associated table are removed (warning: don't use delete
24
+ # because it will leave behind stale association information)
25
+
26
+ module ActiveRecord
27
+ module Associations
28
+ module HyperHasAndBelongsToManyAssociationExtension
29
+ def self.included(base)
30
+ base.class_eval do
31
+ alias_method :find_without_hypertable, :find
32
+ alias_method :find, :find_with_hypertable
33
+
34
+ alias_method :delete_records_without_hypertable, :delete_records
35
+ alias_method :delete_records, :delete_records_with_hypertable
36
+
37
+ alias_method :insert_record_without_hypertable, :insert_record
38
+ alias_method :insert_record, :insert_record_with_hypertable
39
+
40
+ alias_method :create_record_without_hypertable, :create_record
41
+ alias_method :create_record, :create_record_with_hypertable
42
+ end
43
+ end
44
+
45
+ def find_with_hypertable(*args)
46
+ if @reflection.klass <= ActiveRecord::HyperBase
47
+ associated_object_ids = @owner.send(@reflection.association_foreign_key).keys
48
+ @reflection.klass.find(associated_object_ids)
49
+ else
50
+ find_without_hypertable(*args)
51
+ end
52
+ end
53
+
54
+ # Record the association in the assocation columns.
55
+ def insert_record_with_hypertable(record, force=true)
56
+ if @reflection.klass <= ActiveRecord::HyperBase
57
+ @owner.send(@reflection.association_foreign_key)[record.ROW] = 1
58
+ @owner.write_cells([@owner.connection.cell_native_array(@owner.ROW, @reflection.association_foreign_key, record.ROW, "1")])
59
+ record.send(@reflection.primary_key_name)[@owner.ROW] = 1
60
+ record.write_cells([@owner.connection.cell_native_array(record.ROW, @reflection.primary_key_name, @owner.ROW, "1")])
61
+ else
62
+ insert_record_without_hypertable(record, force)
63
+ end
64
+ end
65
+
66
+ # Remove the association from the assocation columns.
67
+ def delete_records_with_hypertable(records)
68
+ if @reflection.klass <= ActiveRecord::HyperBase
69
+ cells_to_delete_by_table = Hash.new{|h,k| h[k] = []}
70
+
71
+ records.each {|r|
72
+ # remove association cells from in memory object
73
+ @owner.send(@reflection.association_foreign_key).delete(r.ROW)
74
+ r.send(@reflection.primary_key_name).delete(@owner.ROW)
75
+
76
+ # make list of cells that need to be removed from hypertable
77
+ cells_to_delete_by_table[@owner.class.table_name] << @owner.connection.cell_native_array(@owner.ROW, @reflection.association_foreign_key, r.ROW)
78
+ cells_to_delete_by_table[r.class.table_name] << @owner.connection.cell_native_array(r.ROW, @reflection.primary_key_name, @owner.ROW)
79
+ }
80
+
81
+ for table in cells_to_delete_by_table.keys
82
+ @owner.delete_cells(cells_to_delete_by_table[table], table)
83
+ end
84
+ else
85
+ delete_records_without_hypertable(records)
86
+ end
87
+ end
88
+
89
+ private
90
+ def create_record_with_hypertable(attributes)
91
+ if @reflection.klass <= ActiveRecord::HyperBase
92
+ r = @reflection.klass.create(attributes)
93
+ insert_record_with_hypertable(r)
94
+ r
95
+ else
96
+ create_record_without_hypertable(attributes) {|record|
97
+ insert_record_without_hypertable(record, true)
98
+ }
99
+ end
100
+ end
101
+ end
102
+
103
+ class HasAndBelongsToManyAssociation
104
+ include HyperHasAndBelongsToManyAssociationExtension
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,87 @@
1
+ module ActiveRecord
2
+ module Associations
3
+ module HyperHasManyAssociationExtension
4
+ def self.included(base)
5
+ base.class_eval do
6
+ alias_method :find_without_hypertable, :find
7
+ alias_method :find, :find_with_hypertable
8
+
9
+ alias_method :delete_records_without_hypertable, :delete_records
10
+ alias_method :delete_records, :delete_records_with_hypertable
11
+
12
+ alias_method :insert_record_without_hypertable, :insert_record
13
+ alias_method :insert_record, :insert_record_with_hypertable
14
+
15
+ alias_method :create_record_without_hypertable, :create_record
16
+ alias_method :create_record, :create_record_with_hypertable
17
+ end
18
+ end
19
+
20
+ def find_with_hypertable(*args)
21
+ if @reflection.klass <= ActiveRecord::HyperBase
22
+ associated_object_ids = @owner.send(@reflection.association_foreign_key).keys
23
+ @reflection.klass.find(associated_object_ids)
24
+ else
25
+ find_without_hypertable(*args)
26
+ end
27
+ end
28
+
29
+ def insert_record_with_hypertable(record)
30
+ if @reflection.klass <= ActiveRecord::HyperBase
31
+ raise "missing ROW key on record" if record.ROW.blank?
32
+ @owner.send(@reflection.association_foreign_key)[record.ROW] = 1
33
+ @owner.write_cells([@owner.connection.cell_native_array(
34
+ @owner.ROW,
35
+ @reflection.association_foreign_key,
36
+ record.ROW,
37
+ "1"
38
+ )])
39
+ record.send("#{@reflection.primary_key_name}=", @owner.ROW)
40
+ record.save
41
+ self.reset
42
+ else
43
+ insert_record_without_hypertable(record)
44
+ end
45
+ end
46
+
47
+ def delete_records_with_hypertable(records)
48
+ if @reflection.klass <= ActiveRecord::HyperBase
49
+ cells_to_delete = []
50
+ records.each {|r|
51
+ # remove association cells from in memory object
52
+ r.send("#{@reflection.primary_key_name}=", nil)
53
+ r.save
54
+
55
+ # make list of cells that need to be removed from hypertable
56
+ cells_to_delete << @owner.connection.cell_native_array(
57
+ @owner.ROW,
58
+ @reflection.association_foreign_key,
59
+ r.ROW
60
+ )
61
+ }
62
+
63
+ @owner.delete_cells(cells_to_delete, @owner.class.table_name)
64
+ else
65
+ delete_records_without_hypertable(records)
66
+ end
67
+ end
68
+
69
+ private
70
+ def create_record_with_hypertable(attributes)
71
+ if @reflection.klass <= ActiveRecord::HyperBase
72
+ r = @reflection.klass.create(attributes)
73
+ insert_record_with_hypertable(r)
74
+ r
75
+ else
76
+ create_record_without_hypertable(attributes) {|record|
77
+ insert_record_without_hypertable(record)
78
+ }
79
+ end
80
+ end
81
+ end
82
+
83
+ class HasManyAssociation
84
+ include HyperHasManyAssociationExtension
85
+ end
86
+ end
87
+ end