actsasflinn-ruby-tokyotyrant 0.1.9 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +22 -4
- data/Rakefile +1 -1
- data/ext/tokyo_tyrant_query.c +9 -0
- data/ext/tokyo_tyrant_table.c +43 -0
- data/spec/tokyo_tyrant_query_spec.rb +10 -0
- data/spec/tokyo_tyrant_table_spec.rb +20 -0
- metadata +1 -1
data/README.rdoc
CHANGED
@@ -4,7 +4,7 @@ This is a c extension for Ruby to access TokyoTyrant databases. It currently su
|
|
4
4
|
|
5
5
|
== Install
|
6
6
|
|
7
|
-
# install tokyocabinet (1.4.
|
7
|
+
# install tokyocabinet (1.4.30) and tokyotyrant (requires 1.1.33)
|
8
8
|
# after installing tc and tt on linux I had to /sbin/ldconfig (as root)
|
9
9
|
gem sources -a http://gems.github.com
|
10
10
|
sudo gem install actsasflinn-ruby-tokyotyrant
|
@@ -135,15 +135,32 @@ This is not in production but the initial benchmarks are very interesting. Resul
|
|
135
135
|
|
136
136
|
# full-text search with all tokens in
|
137
137
|
t.query{ |q| q.condition(:paragraph, :ftsand, 'logos word') }
|
138
|
-
# => ["chapter:1:paragraph:12", "chapter:1:paragraph:14", "chapter:1:paragraph:17", "chapter:1:paragraph:19", "chapter:1:paragraph:24", "chapter:1:paragraph:27", "chapter:1:paragraph:43", "chapter:1:paragraph:53", "
|
138
|
+
# => ["chapter:1:paragraph:12", "chapter:1:paragraph:14", "chapter:1:paragraph:17", "chapter:1:paragraph:19", "chapter:1:paragraph:24", "chapter:1:paragraph:27", "chapter:1:paragraph:43", "chapter:1:paragraph:53", "... lots more ..."]
|
139
139
|
|
140
140
|
# full-text search with at least one token in
|
141
141
|
t.query{ |q| q.condition(:paragraph, :ftsor, 'sermon key') }
|
142
|
-
# => ["chapter:5:paragraph:1", "chapter:9:paragraph:3", "chapter:10:paragraph:1", "chapter:10:paragraph:4", "chapter:10:paragraph:28", "chapter:11:paragraph:3", "chapter:11:paragraph:66", "chapter:11:paragraph:69", "
|
142
|
+
# => ["chapter:5:paragraph:1", "chapter:9:paragraph:3", "chapter:10:paragraph:1", "chapter:10:paragraph:4", "chapter:10:paragraph:28", "chapter:11:paragraph:3", "chapter:11:paragraph:66", "chapter:11:paragraph:69", "... lots more ..."]
|
143
143
|
|
144
144
|
# negated full-text search with at least one token in
|
145
145
|
t.query{ |q| q.condition(:paragraph, '!ftsor', 'the god he and I that said') }
|
146
|
-
# => ["chapter:1:paragraph:95", "chapter:1:paragraph:96", "chapter:1:paragraph:97", "chapter:1:paragraph:98", "chapter:1:paragraph:99", "chapter:2:paragraph:3", "chapter:2:paragraph:5", "chapter:2:paragraph:6", "
|
146
|
+
# => ["chapter:1:paragraph:95", "chapter:1:paragraph:96", "chapter:1:paragraph:97", "chapter:1:paragraph:98", "chapter:1:paragraph:99", "chapter:2:paragraph:3", "chapter:2:paragraph:5", "chapter:2:paragraph:6", "... lots more ..."]
|
147
|
+
|
148
|
+
==== Meta Search (Multi Query)
|
149
|
+
|
150
|
+
query1 = t.prepare_query{ |q| q.condition(:paragraph, :fts, 'rebirth') }
|
151
|
+
query2 = t.prepare_query{ |q| q.condition(:paragraph, :fts, 'logos') }
|
152
|
+
|
153
|
+
# Get the union of two query sets (OR)
|
154
|
+
t.search(:union, query1, query2)
|
155
|
+
# => ["chapter:13:paragraph:4", "chapter:13:paragraph:5", "chapter:13:paragraph:7", "chapter:13:paragraph:19", "chapter:13:paragraph:27", "chapter:13:paragraph:44", "chapter:13:paragraph:57", "... lots more ..."]
|
156
|
+
|
157
|
+
# Get the intersection of two query sets (AND)
|
158
|
+
t.search(:intersection, query1, query2)
|
159
|
+
# => ["chapter:13:paragraph:5", "chapter:13:paragraph:44", "chapter:13:paragraph:69"]
|
160
|
+
|
161
|
+
# Get the difference of two query sets (ANDNOT)
|
162
|
+
t.search(:diff, query1, query2)
|
163
|
+
# => ["chapter:13:paragraph:4", "chapter:13:paragraph:7", "chapter:13:paragraph:19", "chapter:13:paragraph:27", "chapter:13:paragraph:57", "chapter:13:paragraph:125"]
|
147
164
|
|
148
165
|
=== Lua Extension
|
149
166
|
|
@@ -159,3 +176,4 @@ This is not in production but the initial benchmarks are very interesting. Resul
|
|
159
176
|
* Flinn Mueller (actsasflinn) author/maintainer
|
160
177
|
* Justin Reagor (cheapRoc) specs
|
161
178
|
* Seth Yates (sethyates) run method (lua ext)
|
179
|
+
* John Mettraux (jmettraux) inspiration (rufus-tokyo)
|
data/Rakefile
CHANGED
@@ -20,7 +20,7 @@ CLEAN.include('pkg', 'tmp')
|
|
20
20
|
|
21
21
|
gemspec = Gem::Specification.new do |s|
|
22
22
|
s.name = 'ruby-tokyotyrant'
|
23
|
-
s.version = '0.
|
23
|
+
s.version = '0.2.0'
|
24
24
|
s.authors = [ 'Flinn' ]
|
25
25
|
s.email = 'flinn@actsasflinn.com'
|
26
26
|
s.homepage = 'http://github.com/actsasflinn/ruby-tokyotyrant/'
|
data/ext/tokyo_tyrant_query.c
CHANGED
@@ -143,6 +143,14 @@ static VALUE cQuery_set_pkey(VALUE vself, VALUE vpkey) {
|
|
143
143
|
return rb_iv_set(vself, "@pkey", vpkey);
|
144
144
|
}
|
145
145
|
|
146
|
+
static VALUE cQuery_hint(VALUE vself){
|
147
|
+
VALUE vqry;
|
148
|
+
RDBQRY *qry;
|
149
|
+
vqry = rb_iv_get(vself, RDBQRYVNDATA);
|
150
|
+
Data_Get_Struct(vqry, RDBQRY, qry);
|
151
|
+
return rb_str_new2(tcrdbqryhint(qry));
|
152
|
+
}
|
153
|
+
|
146
154
|
void init_query(){
|
147
155
|
rb_define_const(cQuery, "CSTREQ", INT2NUM(RDBQCSTREQ));
|
148
156
|
rb_define_const(cQuery, "CSTRINC", INT2NUM(RDBQCSTRINC));
|
@@ -185,4 +193,5 @@ void init_query(){
|
|
185
193
|
rb_define_method(cQuery, "get", cQuery_get, 0);
|
186
194
|
rb_define_method(cQuery, "set_pkey", cQuery_set_pkey, 1);
|
187
195
|
rb_define_alias(cQuery, "pkey=", "set_pkey");
|
196
|
+
rb_define_method(cQuery, "hint", cQuery_hint, 0);
|
188
197
|
}
|
data/ext/tokyo_tyrant_table.c
CHANGED
@@ -267,6 +267,48 @@ static VALUE cTable_find(VALUE vself){
|
|
267
267
|
return vary;
|
268
268
|
}
|
269
269
|
|
270
|
+
static VALUE cTable_search(int argc, VALUE *argv, VALUE vself){
|
271
|
+
VALUE vqrys, vkeys, vtype, vhash, vcols;
|
272
|
+
TCMAP *recs, *cols;
|
273
|
+
int qsiz, type, j;
|
274
|
+
const char *kbuf;
|
275
|
+
int ksiz, vsiz;
|
276
|
+
TCRDB *db = mTokyoTyrant_getdb(vself);
|
277
|
+
|
278
|
+
// rb_scan_args(argc, argv, "*", &vargs);
|
279
|
+
rb_scan_args(argc, argv, "1*", &vtype, &vqrys);
|
280
|
+
|
281
|
+
// vtype = rb_ary_pop(vargs);
|
282
|
+
qsiz = argc - 1;
|
283
|
+
RDBQRY *qrys[qsiz];
|
284
|
+
|
285
|
+
vtype = StringValueEx(vtype);
|
286
|
+
type = tctdbstrtometasearcytype(RSTRING_PTR(vtype));
|
287
|
+
|
288
|
+
for(j = 0; j < qsiz; j++){
|
289
|
+
VALUE vqry = rb_iv_get(rb_ary_entry(vqrys, j), RDBQRYVNDATA);
|
290
|
+
Data_Get_Struct(vqry, RDBQRY, qrys[j]);
|
291
|
+
}
|
292
|
+
TCLIST *res = tcrdbmetasearch(qrys, qsiz, type);
|
293
|
+
vkeys = listtovary(res);
|
294
|
+
tclistdel(res);
|
295
|
+
|
296
|
+
recs = varytomap(vkeys);
|
297
|
+
if(!tcrdbget3(db, recs)) return Qnil;
|
298
|
+
vhash = rb_hash_new();
|
299
|
+
tcmapiterinit(recs);
|
300
|
+
while((kbuf = tcmapiternext(recs, &ksiz)) != NULL){
|
301
|
+
const char *vbuf = tcmapiterval(kbuf, &vsiz);
|
302
|
+
cols = tcstrsplit4(vbuf, vsiz);
|
303
|
+
vcols = maptovhash(cols);
|
304
|
+
tcmapdel(cols);
|
305
|
+
rb_hash_aset(vhash, StringRaw(kbuf, ksiz), vcols);
|
306
|
+
}
|
307
|
+
tcmapdel(recs);
|
308
|
+
|
309
|
+
return vkeys;
|
310
|
+
}
|
311
|
+
|
270
312
|
void init_table(){
|
271
313
|
rb_define_method(cTable, "mput", cTable_mput, 1);
|
272
314
|
rb_define_alias(cTable, "lput", "mput"); // Rufus Compat
|
@@ -291,4 +333,5 @@ void init_table(){
|
|
291
333
|
rb_define_method(cTable, "prepare_query", cTable_prepare_query, 0);
|
292
334
|
rb_define_method(cTable, "query", cTable_query, 0);
|
293
335
|
rb_define_method(cTable, "find", cTable_find, 0);
|
336
|
+
rb_define_method(cTable, "search", cTable_search, -1);
|
294
337
|
}
|
@@ -120,4 +120,14 @@ describe TokyoTyrant::Query, "with an open database" do
|
|
120
120
|
{'type'=>"Cucumber", 'code'=>"4595", :pk=>"4595", 'variety'=>"Lemon"},
|
121
121
|
{'type'=>"Cucumber", 'code'=>"4596", :pk=>"4596", 'variety'=>"Pickling / Gherkin"}]
|
122
122
|
end
|
123
|
+
|
124
|
+
it "should return a query hint" do
|
125
|
+
@db.set_index(:type, :lexical)
|
126
|
+
q = @db.query
|
127
|
+
q.condition(:type, :streq, 'Cucumber')
|
128
|
+
q.order_by(:code)
|
129
|
+
q.limit(3)
|
130
|
+
q.run
|
131
|
+
q.hint.should == "\nusing an index: \"type\" asc (STREQ)\nresult set size: 5\nsorting the result set: \"code\"\n"
|
132
|
+
end
|
123
133
|
end
|
@@ -267,4 +267,24 @@ describe TokyoTyrant::Table, "with an open database" do
|
|
267
267
|
it "should run lua extensions" do
|
268
268
|
@db.ext('echo', 'hello', 'world').should == "hello\tworld"
|
269
269
|
end
|
270
|
+
|
271
|
+
it "should perform meta search with multiple query objects" do
|
272
|
+
keys = %w[falafel humus couscous tabbouleh tzatziki peanutbutter]
|
273
|
+
values = [{ 'ingredient' => 'chickpeas' },
|
274
|
+
{ 'ingredient' => 'chickpeas' },
|
275
|
+
{ 'ingredient' => 'semolina' },
|
276
|
+
{ 'ingredient' => 'bulgar' },
|
277
|
+
{ 'ingredient' => 'yogurt' },
|
278
|
+
{ 'ingredient' => 'peanuts'}]
|
279
|
+
|
280
|
+
keys.each_with_index{ |k,i| @db[k] = values[i] }
|
281
|
+
|
282
|
+
query1 = @db.prepare_query{ |q| q.condition('ingredient', :streq, 'yogurt').order_by('ingredient') }
|
283
|
+
query2 = @db.prepare_query{ |q| q.condition('ingredient', :streq, 'chickpeas').order_by('ingredient') }
|
284
|
+
query3 = @db.prepare_query{ |q| q.condition('ingredient', :ftsex, 'pea').order_by('ingredient') }
|
285
|
+
|
286
|
+
@db.search(:union, query1, query2).should == ["falafel", "humus", "tzatziki"]
|
287
|
+
@db.search(:diff, query1, query2).should == ["tzatziki"]
|
288
|
+
@db.search(:intersection, query2, query3).should == ["falafel", "humus"]
|
289
|
+
end
|
270
290
|
end
|