A ‘table’ a table database.
http://alpha.mixi.co.jp/blog/?p=290 http://tokyocabinet.sourceforge.net/spex-en.html#tctdbapi
A short example :
require 'rubygems'
require 'rufus/tokyo/cabinet/table'
t = Rufus::Tokyo::Table.new('table.tdb', :create, :write)
# '.tdb' suffix is a must
t['pk0'] = { 'name' => 'alfred', 'age' => '22' }
t['pk1'] = { 'name' => 'bob', 'age' => '18' }
t['pk2'] = { 'name' => 'charly', 'age' => '45' }
t['pk3'] = { 'name' => 'doug', 'age' => '77' }
t['pk4'] = { 'name' => 'ephrem', 'age' => '32' }
p t.query { |q|
q.add_condition 'age', :numge, '32'
q.order_by 'age'
q.limit 2
}
# => [ {"name"=>"ephrem", :pk=>"pk4", "age"=>"32"},
# {"name"=>"charly", :pk=>"pk2", "age"=>"45"} ]
t.close
Methods
public class
public instance
- []=
- clear
- close
- delete
- delete_keys_with_prefix
- do_query
- generate_unique_id
- genuid
- keys
- lget
- lib
- path
- pointer
- prepare_query
- query
- query_delete
- set_index
- size
- tranabort
- tranbegin
- trancommit
protected instance
Included modules
Constants
| INDEX_TYPES | = | { :lexical => 0, :decimal => 1, :token => 2, :qgram => 3, :opt => 9998, :optimized => 9998, :void => 9999, :remove => 9999, :keep => 1 << 24 } |
Public class methods
Creates a Table instance (creates or opens it depending on the args)
For example,
t = Rufus::Tokyo::Table.new('table.tdb')
# '.tdb' suffix is a must
will create the table.tdb (or simply open it if already present) and make sure we have write access to it.
parameters
Parameters can be set in the path or via the optional params hash (like in Rufus::Tokyo::Cabinet)
* :mode a set of chars ('r'ead, 'w'rite, 'c'reate, 't'runcate,
'e' non locking, 'f' non blocking lock), default is 'wc'
* :opts a set of chars ('l'arge, 'd'eflate, 'b'zip2, 't'cbs)
(usually empty or something like 'ld' or 'lb')
* :bnum number of elements of the bucket array
* :apow size of record alignment by power of 2 (defaults to 4)
* :fpow maximum number of elements of the free block pool by
power of 2 (defaults to 10)
* :mutex when set to true, makes sure only 1 thread at a time
accesses the table (well, Ruby, global thread lock, ...)
* :rcnum specifies the maximum number of records to be cached.
If it is not more than 0, the record cache is disabled.
It is disabled by default.
* :lcnum specifies the maximum number of leaf nodes to be cached.
If it is not more than 0, the default value is specified.
The default value is 2048.
* :ncnum specifies the maximum number of non-leaf nodes to be
cached. If it is not more than 0, the default value is
specified. The default value is 512.
* :xmsiz specifies the size of the extra mapped memory. If it is
not more than 0, the extra mapped memory is disabled.
The default size is 67108864.
* :dfunit unit step number. If it is not more than 0,
the auto defragmentation is disabled. (Since TC 1.4.21)
Some examples :
t = Rufus::Tokyo::Table.new('table.tdb')
t = Rufus::Tokyo::Table.new('table.tdb#mode=r')
t = Rufus::Tokyo::Table.new('table.tdb', :mode => 'r')
t = Rufus::Tokyo::Table.new('table.tdb#opts=ld#mode=r')
t = Rufus::Tokyo::Table.new('table.tdb', :opts => 'ld', :mode => 'r')
# File lib/rufus/tokyo/cabinet/table.rb, line 125 125: def initialize (path, params={}) 126: 127: conf = determine_conf(path, params, :table) 128: 129: @db = lib.tctdbnew 130: 131: # 132: # tune table 133: 134: libcall(:tctdbsetmutex) if conf[:mutex] 135: 136: libcall(:tctdbtune, conf[:bnum], conf[:apow], conf[:fpow], conf[:opts]) 137: 138: # TODO : set indexes here... well, there is already #set_index 139: #conf[:indexes]... 140: 141: libcall(:tctdbsetcache, conf[:rcnum], conf[:lcnum], conf[:ncnum]) 142: 143: libcall(:tctdbsetxmsiz, conf[:xmsiz]) 144: 145: libcall(:tctdbsetdfunit, conf[:dfunit]) \ 146: if lib.respond_to?(:tctdbsetdfunit) # TC >= 1.4.21 147: 148: # 149: # open table 150: 151: @path = conf[:path] 152: 153: libcall(:tctdbopen, @path, conf[:mode]) 154: end
Public instance methods
Inserts a record in the table db
table['pk0'] = [ 'name', 'fred', 'age', '45' ]
table['pk1'] = { 'name' => 'jeff', 'age' => '46' }
Accepts both a hash or an array (expects the array to be of the form [ key, value, key, value, … ] else it will raise an ArgumentError)
Raises an error in case of failure.
# File lib/rufus/tokyo/cabinet/table.rb, line 240 240: def []= (pk, h_or_a) 241: 242: pk = pk.to_s 243: h_or_a = Rufus::Tokyo.h_or_a_to_s(h_or_a) 244: 245: m = Rufus::Tokyo::Map[h_or_a] 246: 247: r = lib.tab_put(@db, pk, Rufus::Tokyo.blen(pk), m.pointer) 248: 249: m.free 250: 251: (r == 1) || raise_error # raising potential error after freeing map 252: 253: h_or_a 254: end
Removes all records in this table database
# File lib/rufus/tokyo/cabinet/table.rb, line 274 274: def clear 275: 276: libcall(:tab_vanish) 277: end
Closes the table (and frees the datastructure allocated for it), returns true in case of success.
# File lib/rufus/tokyo/cabinet/table.rb, line 173 173: def close 174: 175: result = lib.tab_close(@db) 176: lib.tab_del(@db) 177: 178: (result == 1) 179: end
Removes an entry in the table
(might raise an error if the delete itself failed, but returns nil if there was no entry for the given key)
# File lib/rufus/tokyo/cabinet/table.rb, line 261 261: def delete (k) 262: 263: k = k.to_s 264: 265: v = self[k] 266: return nil unless v 267: libcall(:tab_out, k, Rufus::Tokyo.blen(k)) 268: 269: v 270: end
Deletes all the entries whose key begin with the given prefix.
# File lib/rufus/tokyo/cabinet/table.rb, line 333 333: def delete_keys_with_prefix (prefix) 334: 335: # TODO : use ...searchout 336: 337: ks = lib.tab_fwmkeys(@db, prefix, Rufus::Tokyo.blen(prefix), -1) 338: # -1 for no limit 339: 340: begin 341: ks = Rufus::Tokyo::List.new(ks) 342: ks.each { |k| self.delete(k) } 343: ensure 344: ks && ks.free 345: end 346: end
Prepares and runs a query, returns a ResultSet instance (takes care of freeing the query structure)
# File lib/rufus/tokyo/cabinet/table.rb, line 377 377: def do_query (&block) 378: 379: q = prepare_query(&block) 380: rs = q.run 381: 382: return rs 383: 384: ensure 385: q && q.free 386: end
Generates a unique id (in the context of this Table instance)
# File lib/rufus/tokyo/cabinet/table.rb, line 183 183: def generate_unique_id 184: 185: lib.tab_genuid(@db) 186: end
Returns an array of all the primary keys in the table
With no options given, this method will return all the keys (strings) in a Ruby array.
:prefix --> returns only the keys who match a given string prefix :limit --> returns a limited number of keys :native --> returns an instance of Rufus::Tokyo::List instead of a Ruby Hash, you have to call #free on that List when done with it ! Else you're exposing yourself to a memory leak.
# File lib/rufus/tokyo/cabinet/table.rb, line 292 292: def keys (options={}) 293: 294: outlen = nil 295: 296: if pre = options[:prefix] 297: 298: l = lib.tab_fwmkeys( 299: @db, pre, Rufus::Tokyo.blen(pre), options[:limit] || -1) 300: 301: l = Rufus::Tokyo::List.new(l) 302: 303: options[:native] ? l : l.release 304: 305: else 306: 307: limit = options[:limit] || -1 308: limit = nil if limit < 1 309: 310: l = options[:native] ? Rufus::Tokyo::List.new : [] 311: 312: lib.tab_iterinit(@db) 313: 314: outlen = FFI::MemoryPointer.new(:int) 315: 316: loop do 317: break if limit and l.size >= limit 318: out = lib.tab_iternext(@db, outlen) 319: break if out.address == 0 320: l << out.get_bytes(0, outlen.get_int(0)) 321: end 322: 323: l 324: end 325: 326: ensure 327: 328: outlen && outlen.free 329: end
No ‘misc’ methods for the table library, so this lget is equivalent to calling get for each key. Hoping later versions of TC will provide a mget method.
# File lib/rufus/tokyo/cabinet/table.rb, line 352 352: def lget (keys) 353: 354: keys.inject({}) { |h, k| k = k.to_s; v = self[k]; h[k] = v if v; h } 355: end
Using the cabinet lib
# File lib/rufus/tokyo/cabinet/table.rb, line 158 158: def lib 159: 160: CabinetLib 161: end
Returns the path to the table.
# File lib/rufus/tokyo/cabinet/table.rb, line 165 165: def path 166: 167: @path 168: end
# File lib/rufus/tokyo/cabinet/table.rb, line 444 444: def pointer 445: @db 446: end
Prepares a query instance (block is optional)
# File lib/rufus/tokyo/cabinet/table.rb, line 366 366: def prepare_query (&block) 367: 368: q = TableQuery.new(self) 369: block.call(q) if block 370: 371: q 372: end
Prepares and runs a query, returns an array of hashes (all Ruby) (takes care of freeing the query and the result set structures)
# File lib/rufus/tokyo/cabinet/table.rb, line 391 391: def query (&block) 392: 393: rs = do_query(&block) 394: a = rs.to_a 395: 396: return a 397: 398: ensure 399: rs && rs.free 400: end
Prepares a query and then runs it and deletes all the results.
# File lib/rufus/tokyo/cabinet/table.rb, line 404 404: def query_delete (&block) 405: 406: q = prepare_query(&block) 407: rs = q.delete 408: 409: return rs 410: 411: ensure 412: q && q.free 413: end
Sets an index on a column of the table.
Types maybe be :lexical or :decimal.
Recently (TC 1.4.26 and 1.4.27) inverted indexes have been added, they are :token and :qgram. There is an :opt index as well.
Sorry couldn’t find any good doc about those inverted indexes apart from :
http://alpha.mixi.co.jp/blog/?p=1147 http://www.excite-webtl.jp/world/english/web/?wb_url=http%3A%2F%2Falpha.mixi.co.jp%2Fblog%2F%3Fp%3D1147&wb_lp=JAEN&wb_dis=2&wb_submit=+%96%7C+%96%F3+
Use :keep to “add” and :remove (or :void) to “remove” an index.
If column_name is :pk or “”, the index will be set on the primary key.
Returns true in case of success.
# File lib/rufus/tokyo/cabinet/table.rb, line 220 220: def set_index (column_name, *types) 221: 222: column_name = column_name == :pk ? '' : column_name.to_s 223: 224: i = types.inject(0) { |i, t| i = i | INDEX_TYPES[t]; i } 225: 226: (lib.tab_setindex(@db, column_name, i) == 1) 227: end
Returns the number of records in this table db
# File lib/rufus/tokyo/cabinet/table.rb, line 359 359: def size 360: 361: lib.tab_rnum(@db) 362: end
Warning : this method is low-level, you probably only need to use transaction and a block.
Direct call for ‘transaction abort’.
# File lib/rufus/tokyo/cabinet/table.rb, line 438 438: def tranabort 439: libcall(:tctdbtranabort) 440: end
Warning : this method is low-level, you probably only need to use transaction and a block.
Direct call for ‘transaction begin’.
# File lib/rufus/tokyo/cabinet/table.rb, line 420 420: def tranbegin 421: libcall(:tctdbtranbegin) 422: end
Warning : this method is low-level, you probably only need to use transaction and a block.
Direct call for ‘transaction commit’.
# File lib/rufus/tokyo/cabinet/table.rb, line 429 429: def trancommit 430: libcall(:tctdbtrancommit) 431: end
Protected instance methods
Returns the value (as a Ruby Hash) else nil
(the actual #[] method is provided by HashMethods)
# File lib/rufus/tokyo/cabinet/table.rb, line 454 454: def get (k) 455: 456: m = lib.tab_get(@db, k, Rufus::Tokyo.blen(k)) 457: 458: return nil if m.address == 0 459: 460: Map.to_h(m) # which frees the map 461: end
# File lib/rufus/tokyo/cabinet/table.rb, line 463 463: def libcall (lib_method, *args) 464: 465: (lib.send(lib_method, @db, *args) == 1) or raise_error 466: # stack level too deep with JRuby 1.1.6 :( 467: 468: #(eval(%{ lib.#{lib_method}(@db, *args) }) == 1) or raise_error 469: # works with JRuby 1.1.6 470: end
Obviously something got wrong, let’s ask the db about it and raise a TokyoError
# File lib/rufus/tokyo/cabinet/table.rb, line 475 475: def raise_error 476: 477: err_code = lib.tab_ecode(@db) 478: err_msg = lib.tab_errmsg(err_code) 479: 480: raise TokyoError.new("(err #{err_code}) #{err_msg}") 481: end