activetokyocabinet 0.1.7 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- data/README +93 -33
- data/lib/active_record/connection_adapters/abstract_tokyocabinet_adapter.rb +51 -4
- data/lib/active_record/connection_adapters/tokyocabinet_adapter.rb +2 -4
- data/lib/active_record/connection_adapters/tokyotyrant_adapter.rb +9 -5
- data/lib/active_tokyocabinet/tdb.rb +12 -0
- metadata +2 -2
data/README
CHANGED
@@ -22,67 +22,127 @@ see http://gemcutter.org/gems/activetokyocabinet
|
|
22
22
|
|
23
23
|
== Example
|
24
24
|
=== database.yml
|
25
|
+
|
25
26
|
# TokyoCabinet
|
26
27
|
development:
|
27
|
-
adapter:
|
28
|
-
database:
|
29
|
-
|
28
|
+
adapter: tokyocabinet
|
29
|
+
database: db/casket/
|
30
|
+
# save to `$RAILS_ROOT/db/casket/*.tct'.
|
31
|
+
|
30
32
|
# TokyoTyrant
|
31
33
|
development:
|
32
34
|
adapter: tokyotyrant
|
33
35
|
database:
|
34
|
-
|
35
|
-
|
36
|
+
emps: { host: localhost, port: 1978 }
|
37
|
+
depts: { host: localhost, port: 1979 }
|
36
38
|
|
37
39
|
=== Model
|
38
40
|
|
39
|
-
class
|
41
|
+
class Emp < ActiveRecord::Base
|
40
42
|
include ActiveTokyoCabinet::TDB
|
41
43
|
|
42
44
|
# define schema information.
|
43
45
|
# (string, int, float)
|
44
|
-
string :
|
46
|
+
string :ename
|
45
47
|
int :age
|
48
|
+
string :job
|
49
|
+
float :sal
|
50
|
+
string :hiredate
|
46
51
|
end
|
47
52
|
|
48
|
-
===
|
53
|
+
=== ActiveRecord API
|
49
54
|
|
50
|
-
|
51
|
-
|
52
|
-
|
55
|
+
# see http://api.rubyonrails.org/classes/ActiveRecord/Base.html
|
56
|
+
|
57
|
+
emp = Emp.find(:first,
|
58
|
+
:conditions => ["ename = ? and age > ?", "yamada", 25],
|
59
|
+
:order => 'age desc', :limit => 5, :offset => 3)
|
60
|
+
|
61
|
+
emp.ename = 'yamamoto'
|
62
|
+
emp.age = 30
|
63
|
+
emp.save
|
64
|
+
|
65
|
+
emp_list = Emp.find(101, 102, 103)
|
53
66
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
67
|
+
emp_list.each do |i|
|
68
|
+
i.destroy if i.age > 20
|
69
|
+
end
|
70
|
+
|
71
|
+
new_emp = Emp.new
|
72
|
+
new_emp.ename = 'suzuki'
|
73
|
+
new_emp.age = 27
|
74
|
+
new_emp.save!
|
75
|
+
|
76
|
+
# not available:
|
77
|
+
# - :include, :group
|
78
|
+
# - `OR'
|
79
|
+
# - Subquery
|
80
|
+
# - Include `ID' in search condition
|
81
|
+
# see http://activetokyocabi.rubyforge.org/svn/trunk/lib/active_tokyocabinet/sqlparser.y
|
82
|
+
|
83
|
+
== Expanded operator
|
84
|
+
|
85
|
+
# see http://1978th.net/tokyotyrant/rubydoc/classes/TokyoTyrant/RDBQRY.html
|
61
86
|
|
62
|
-
|
63
|
-
|
64
|
-
:order => 'age desc', :limit => 5, :offset => 3)
|
87
|
+
# query condition: string begins with
|
88
|
+
emp = Emp.find(:all, :conditions => ['ename bw ?', 'J'])
|
65
89
|
|
66
|
-
|
67
|
-
|
68
|
-
hello.save!
|
90
|
+
# query condition: string ends with
|
91
|
+
emp = Emp.find(:all, :conditions => ['ename ew ?', 'ES'])
|
69
92
|
|
70
|
-
|
93
|
+
# query condition: string is included in
|
94
|
+
emp = Emp.find(:all, :conditions => ['ename inc ?', 'LA'])
|
71
95
|
|
72
|
-
|
73
|
-
|
96
|
+
# query condition: string includes all tokens i
|
97
|
+
emp = Emp.find(:all, :conditions => ['job incall (?)', ['ANALYST', 'MANAGER']])
|
74
98
|
|
75
|
-
|
99
|
+
# query condition: string includes at least one token in
|
100
|
+
emp = Emp.find(:all, :conditions => ['job incany (?)', ['ANALYST', 'MANAGER']])
|
76
101
|
|
77
|
-
|
102
|
+
# query condition: string is equal to at least one token in
|
103
|
+
emp = Emp.find(:all, :conditions => ['job in (?)', ['ANALYST', 'MANAGER']])
|
104
|
+
emp = Emp.find(:all, :conditions => ['job anyone (?)', ['ANALYST', 'MANAGER']])
|
78
105
|
|
79
|
-
|
106
|
+
# query condition: string matches regular expressions of
|
107
|
+
emp = Emp.find(:all, :conditions => ['ename regexp ?', '^J[AO].+$'])
|
80
108
|
|
81
|
-
|
109
|
+
# query condition: number is between two tokens of
|
110
|
+
emp = Emp.find(:all, :conditions => ['age between ? and ?', 20, 30])
|
111
|
+
emp = Emp.find(:all, :conditions => ['age bt (?)', [20, 30]])
|
112
|
+
|
113
|
+
# query condition: full-text search with the phrase of
|
114
|
+
emp = Emp.find(:all, :conditions => ['ename fts ?', 'MI'])
|
115
|
+
|
116
|
+
# query condition: full-text search with all tokens in
|
117
|
+
emp = Emp.find(:all, :conditions => ['ename ftsand ?', 'HATSUNE MIKU'])
|
118
|
+
emp = Emp.find(:all, :conditions => ['ename ftsand (?)', ['HATSUNE', 'MIKU']])
|
119
|
+
|
120
|
+
# query condition: full-text search with at least one token in
|
121
|
+
emp = Emp.find(:all, :conditions => ['hiredate ftsor ?', '1983 DEC'])
|
122
|
+
emp = Emp.find(:all, :conditions => ['hiredate ftsor (?)', ['1983', 'DEC']])
|
123
|
+
|
124
|
+
# query condition: full-text search with the compound expression of
|
125
|
+
emp = Emp.find(:all, :conditions => ['ename ftsex ?', 'MIKU || RIN'])
|
126
|
+
emp = Emp.find(:all, :conditions => ['ename ftsex ?', 'HATSUNE && MIKU'])
|
127
|
+
|
128
|
+
== Low layer API
|
82
129
|
|
83
|
-
|
84
|
-
|
130
|
+
Emp.tdbopen do |tdb|
|
131
|
+
pkey = tdb.genuid
|
132
|
+
cols = {"ename" => "tanaka", "age" => "30"}
|
133
|
+
tdb.put(pkey, cols)
|
134
|
+
}
|
135
|
+
|
136
|
+
Emp.proc(:all, :conditions => ['name = ?', 'sugawara'], :limit => 10) do |tdb, pkey, cols|
|
137
|
+
puts "#{pkey}: #{cols.inspect}
|
85
138
|
end
|
139
|
+
# proc method return empty array.
|
140
|
+
|
141
|
+
== Set index
|
142
|
+
|
143
|
+
# see http://1978th.net/tokyotyrant/rubydoc/classes/TokyoTyrant/RDBTBL.html#M000007
|
144
|
+
|
145
|
+
Emp.setindex(:age, :decimal)
|
86
146
|
|
87
147
|
== Related article
|
88
148
|
* http://d.hatena.ne.jp/winebarrel/20100106/p1
|
@@ -1,7 +1,44 @@
|
|
1
|
+
require 'active_record/base'
|
1
2
|
require 'active_record/connection_adapters/abstract_adapter'
|
2
3
|
require 'active_tokyocabinet/tdb'
|
3
4
|
require 'active_tokyocabinet/sqlparser.tab'
|
4
5
|
|
6
|
+
module ActiveRecord
|
7
|
+
class Base
|
8
|
+
class << self
|
9
|
+
def find_by_sql_with_activetokyocabinet(args)
|
10
|
+
retval = nil
|
11
|
+
|
12
|
+
if self.include?(ActiveTokyoCabinet::TDB) and args.kind_of?(Array)
|
13
|
+
sql, proc = args
|
14
|
+
connection.select(sanitize_sql(sql), "#{name} Load", &proc)
|
15
|
+
retval = []
|
16
|
+
else
|
17
|
+
retval = find_by_sql_without_activetokyocabinet(args)
|
18
|
+
end
|
19
|
+
|
20
|
+
return retval
|
21
|
+
end
|
22
|
+
alias_method_chain :find_by_sql, :activetokyocabinet
|
23
|
+
|
24
|
+
def construct_finder_sql_with_activetokyocabinet(options)
|
25
|
+
sql = construct_finder_sql_without_activetokyocabinet(options)
|
26
|
+
|
27
|
+
if self.include?(ActiveTokyoCabinet::TDB) and (proc = options[:activetokyocabinet_proc])
|
28
|
+
sql = [sql, proc]
|
29
|
+
end
|
30
|
+
|
31
|
+
return sql
|
32
|
+
end
|
33
|
+
alias_method_chain :construct_finder_sql, :activetokyocabinet
|
34
|
+
|
35
|
+
unless VALID_FIND_OPTIONS.include?(:activetokyocabinet_proc)
|
36
|
+
VALID_FIND_OPTIONS << :activetokyocabinet_proc
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
5
42
|
module ActiveRecord
|
6
43
|
module ConnectionAdapters
|
7
44
|
class AbstractTokyoCabinetAdapter < AbstractAdapter
|
@@ -20,9 +57,13 @@ module ActiveRecord
|
|
20
57
|
log(sql, name) do
|
21
58
|
parsed_sql = ActiveTokyoCabinet::SQLParser.new(sql).parse
|
22
59
|
|
23
|
-
tdbopen(parsed_sql) do |tdb|
|
60
|
+
tdbopen(parsed_sql[:table], true) do |tdb|
|
24
61
|
if (count = parsed_sql[:count])
|
25
62
|
rows = [{count => rnum(tdb, parsed_sql)}]
|
63
|
+
elsif block_given?
|
64
|
+
rows = search(tdb, parsed_sql) do |_tdb, rkey, rcols|
|
65
|
+
yield([_tdb, rkey, rcols])
|
66
|
+
end
|
26
67
|
else
|
27
68
|
rows = search(tdb, parsed_sql)
|
28
69
|
end
|
@@ -38,7 +79,7 @@ module ActiveRecord
|
|
38
79
|
log(sql, name) do
|
39
80
|
parsed_sql = ActiveTokyoCabinet::SQLParser.new(sql).parse
|
40
81
|
|
41
|
-
tdbopen(parsed_sql) do |tdb|
|
82
|
+
tdbopen(parsed_sql[:table]) do |tdb|
|
42
83
|
pkey = tdb.genuid
|
43
84
|
keys = parsed_sql[:column_list]
|
44
85
|
vals = parsed_sql[:value_list]
|
@@ -64,7 +105,7 @@ module ActiveRecord
|
|
64
105
|
log(sql, name) do
|
65
106
|
parsed_sql = ActiveTokyoCabinet::SQLParser.new(sql).parse
|
66
107
|
|
67
|
-
tdbopen(parsed_sql) do |tdb|
|
108
|
+
tdbopen(parsed_sql[:table]) do |tdb|
|
68
109
|
set_clause_list = parsed_sql[:set_clause_list]
|
69
110
|
|
70
111
|
rkeys(tdb, parsed_sql).each do |rkey|
|
@@ -92,7 +133,7 @@ module ActiveRecord
|
|
92
133
|
log(sql, name) do
|
93
134
|
parsed_sql = ActiveTokyoCabinet::SQLParser.new(sql).parse
|
94
135
|
|
95
|
-
tdbopen(parsed_sql) do |tdb|
|
136
|
+
tdbopen(parsed_sql[:table]) do |tdb|
|
96
137
|
unless query(tdb, parsed_sql).searchout
|
97
138
|
ecode = tdb.ecode
|
98
139
|
raise '%s: %s' % [tdb.errmsg(ecode), sql]
|
@@ -110,6 +151,12 @@ module ActiveRecord
|
|
110
151
|
|
111
152
|
rkeys(tdb, parsed_sql).each do |rkey|
|
112
153
|
rcols = tdb.get(rkey)
|
154
|
+
|
155
|
+
if block_given?
|
156
|
+
yield(tdb, rkey.to_i, rcols)
|
157
|
+
next
|
158
|
+
end
|
159
|
+
|
113
160
|
next if rcols.nil?
|
114
161
|
|
115
162
|
unless select_list.nil? or select_list.empty?
|
@@ -28,11 +28,10 @@ module ActiveRecord
|
|
28
28
|
File.exist?(path)
|
29
29
|
end
|
30
30
|
|
31
|
-
def tdbopen(
|
32
|
-
table_name = parsed_sql[:table]
|
31
|
+
def tdbopen(table_name, readonly = false)
|
33
32
|
path = tdbpath(table_name)
|
34
33
|
|
35
|
-
if File.exist?(path) and
|
34
|
+
if File.exist?(path) and readonly
|
36
35
|
omode = TokyoCabinet::TDB::OREADER
|
37
36
|
else
|
38
37
|
omode = TokyoCabinet::TDB::OWRITER | TokyoCabinet::TDB::OCREAT
|
@@ -54,7 +53,6 @@ module ActiveRecord
|
|
54
53
|
end
|
55
54
|
end
|
56
55
|
end
|
57
|
-
private :tdbopen
|
58
56
|
|
59
57
|
def tdbpath(table_name)
|
60
58
|
File.join(@config[:database], table_name + ".tct")
|
@@ -45,9 +45,7 @@ module ActiveRecord
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
-
def tdbopen(
|
49
|
-
table_name = parsed_sql[:table]
|
50
|
-
|
48
|
+
def tdbopen(table_name, readonly = false)
|
51
49
|
unless table_exists?(table_name)
|
52
50
|
raise "Table does not exist: #{table_name}"
|
53
51
|
end
|
@@ -65,7 +63,6 @@ module ActiveRecord
|
|
65
63
|
yield(tdb)
|
66
64
|
@connection[table_name] = tdb
|
67
65
|
end
|
68
|
-
private :tdbopen
|
69
66
|
|
70
67
|
def search(tdb, parsed_sql)
|
71
68
|
condition = parsed_sql[:condition] || []
|
@@ -82,7 +79,14 @@ module ActiveRecord
|
|
82
79
|
end
|
83
80
|
|
84
81
|
rows = query(tdb, parsed_sql).searchget
|
85
|
-
|
82
|
+
|
83
|
+
if block_given?
|
84
|
+
rows.each do |i|
|
85
|
+
yield(tdb, i[""].to_i, i)
|
86
|
+
end
|
87
|
+
else
|
88
|
+
rows.each {|i| i['id'] = i[""].to_i }
|
89
|
+
end
|
86
90
|
|
87
91
|
return rows
|
88
92
|
end
|
@@ -14,9 +14,21 @@ module ActiveTokyoCabinet
|
|
14
14
|
class_eval "def \#{name}; v = self[:\#{name}]; (v.nil? || v.empty?) ? nil : v.#{conv}; end"
|
15
15
|
end
|
16
16
|
|
17
|
+
def tdbopen(readonly = false)
|
18
|
+
self.connection.tdbopen(self.table_name, readonly) {|tdb| yield(tdb) }
|
19
|
+
end
|
20
|
+
|
17
21
|
def setindex(name, type)
|
18
22
|
self.connection.setindex(self.table_name, name, type)
|
19
23
|
end
|
24
|
+
|
25
|
+
def proc(*args, &block)
|
26
|
+
if block and (options = args.last) and options.kind_of?(Hash)
|
27
|
+
options[:activetokyocabinet_proc] = block
|
28
|
+
end
|
29
|
+
|
30
|
+
self.find(*args)
|
31
|
+
end
|
20
32
|
}
|
21
33
|
end
|
22
34
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activetokyocabinet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- winebarrel
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-01-
|
12
|
+
date: 2010-01-17 00:00:00 +09:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|