1#!/usr/bin/env ruby 2# 3# Copyright(C) 2006 Brazil 4# All rights reserved. 5# This is free software with ABSOLUTELY NO WARRANTY. 6# 7# You can redistribute it and/or modify it under the terms of 8# the GNU General Public License version 2. 9 10require 'senna_api' 11# todo: load 'senna/senna_api_dl' if 'senna_api' is unavailable 12 13module Senna 14 INDEX_NORMALIZE = 0x0001 15 INDEX_SPLIT_ALPHA = 0x0002 16 INDEX_SPLIT_DIGIT = 0x0004 17 INDEX_SPLIT_SYMBOL = 0x0008 18 INDEX_MORPH_ANALYSE = 0x0000 19 INDEX_NGRAM = 0x0010 20 INDEX_DELIMITED = 0x0020 21 INDEX_ENABLE_SUFFIX_SEARCH = 0x0100 22 INDEX_DISABLE_SUFFIX_SEARCH = 0x0200 23 INDEX_WITH_VACUUM = 0x8000 24 25 SYM_MAX_KEY_SIZE = 0x2000 26 SYM_WITH_SIS = 0x80000000 27 28 SNIP_NORMALIZE = 0x0001 29 SNIP_COPY_TAG = 0x0002 30 SNIP_SKIP_LEADING_SPACES = 0x0004 31 SNIP_MAP_HTMLENCODE = -1 32 33 RC_SUCCESS = 0x0000 34 RC_MEMORY_EXHAUSTED = 0x0001 35 RC_INVALID_FORMAT = 0x0002 36 RC_FILE_OPERATION_ERROR = 0x0003 37 RC_INVALID_ARGUMENT = 0x0004 38 RC_OTHER_ERROR = 0x0005 39 40 ENC_DEFAULT = 0x0000 41 ENC_NONE = 0x0001 42 ENC_EUC_JP = 0x0002 43 ENC_UTF8 = 0x0003 44 ENC_SJIS = 0x0004 45 46 REC_DOCUMENT = 0x0000 47 REC_SECTION = 0x0001 48 REC_POSITION = 0x0002 49 REC_USERDEF = 0x0003 50 REC_NONE = 0x0004 51 52 SEL_OR = 0x0000 53 SEL_AND = 0x0001 54 SEL_BUT = 0x0002 55 SEL_ADJUST = 0x0003 56 SEL_OPERATOR = 0x0004 57 58 SEL_EXACT = 0x0000 59 SEL_PARTIAL = 0x0001 60 SEL_UNSPLIT = 0x0002 61 SEL_NEAR = 0x0003 62 SEL_NEAR2 = 0x0004 63 SEL_SIMILAR = 0x0005 64 SEL_TERM_EXTRACT = 0x0006 65 SEL_PREFIX = 0x0007 66 SEL_SUFFIX = 0x0008 67 68 SORT_DESCENDING = 0x0000 69 SORT_ASCENDING = 0x0001 70 71 LOG_NONE = 0x0000 72 LOG_EMERG = 0x0001 73 LOG_ALERT = 0x0002 74 LOG_CRIT = 0x0003 75 LOG_ERROR = 0x0004 76 LOG_WARNING = 0x0005 77 LOG_NOTICE = 0x0006 78 LOG_INFO = 0x0007 79 LOG_DEBUG = 0x0008 80 LOG_DUMP = 0x0009 81 82 LOG_TIME = 0x0001 83 LOG_TITLE = 0x0002 84 LOG_MESSAGE = 0x0004 85 LOG_LOCATION = 0x0008 86 87 DEFAULT_PORT = 10041 88 89 CTX_MORE = 0x0001 90 CTX_TAIL = 0x0002 91 CTX_HEAD = 0x0004 92 CTX_QUIET = 0x0008 93 CTX_QUIT = 0x0010 94 95 STR_REMOVEBLANK = 0x0001 96 STR_WITH_CTYPES = 0x0002 97 STR_WITH_CHECKS = 0x0004 98 99 class Index 100 attr_reader :handle 101 attr_reader :key_size, :flags, :initial_n_segments, :encoding 102 attr_reader :nrecords_keys, :file_size_keys 103 attr_reader :nrecords_lexicon, :file_size_lexicon 104 attr_reader :inv_seg_size, :inv_chunk_size 105 106 def Index.callback(handle) 107 proc { Senna::sen_index_close(handle) } 108 end 109 110 def Index::create(path, key_size=0, flags=0, initial_n_segments=0, encoding=ENC_DEFAULT) 111 handle = Senna::sen_index_create(path, key_size, flags, initial_n_segments, encoding) 112 return new(handle) 113 end 114 115 def Index::open(path) 116 handle = Senna::sen_index_open(path) 117 return new(handle) 118 end 119 120 def initialize(handle) 121 @handle = handle 122 ObjectSpace.define_finalizer(self, Index.callback(handle)) 123 end 124 125 def upd(id, oldvalue, newvalue) 126 id = [id].pack('L') if Fixnum === id 127 oldvalue ||= '' 128 newvalue ||= '' 129 Senna::sen_index_upd(@handle, id, oldvalue, oldvalue.length, 130 newvalue, newvalue.length) 131 end 132 133 def update(id, section, oldvalue, newvalue) 134 # section must be >= 1 135 id = [id].pack('L') if Fixnum === id 136 oldvalue = Values::open(oldvalue) if String === oldvalue 137 newvalue = Values::open(newvalue) if String === newvalue 138 oldvalue ||= Values::open 139 newvalue ||= Values::open 140 Senna::sen_index_update(@handle, id, section, oldvalue.handle, newvalue.handle) 141 end 142 143 def sel(query) 144 r = Senna::sen_index_sel(@handle, query, query.length) 145 return Records.new(r) if r 146 end 147 148 def select(query, records=nil, op=SEL_OR, optarg=nil) 149 records ||= Records.new 150 Senna::sen_index_select(@handle, query, query.length, records.handle, op, optarg) 151 return records 152 end 153 154 def query(query, default_op=SEL_OR, max_exprs=32, encoding=ENC_DEFAULT) 155 return unless String === query 156 q = Senna::sen_query_open(query, query.length, default_op, max_exprs, encoding) 157 r = Senna::sen_records_open(REC_DOCUMENT, REC_NONE, 0) 158 Senna::sen_query_exec(@handle, q, r, SEL_OR) 159 Senna::sen_query_close(q) 160 return Records.new(r) 161 end 162 163 def keys 164 Senna::Sym.new(Senna::sen_index_keys(@handle)) 165 end 166 167 def lexicon 168 Senna::Sym.new(Senna::sen_index_lexicon(@handle)) 169 end 170 171 def inv 172 Senna::sen_index_inv(@handle) 173 end 174 175 def close 176 rc = Senna::sen_index_close(@handle) 177 @handle = nil 178 ObjectSpace.undefine_finalizer(self) 179 return rc 180 end 181 182 def path 183 size, = Senna::sen_index_path(@handle, 1) 184 _,res = Senna::sen_index_path(@handle, size) 185 return res 186 end 187 188 def remove 189 _path = path 190 self.close 191 Senna::sen_index_remove(_path) 192 end 193 194 def rename(newpath) 195 _path = path 196 close 197 Senna::sen_index_rename(_path, newpath) 198 @handle = Senna::sen_index_open(newpath) 199 ObjectSpace.define_finalizer(self, Index.callback(@handle)) 200 end 201 202 def inv_check(term) 203 e = InvEntry.new(self, term) 204 205 printf("key: %10s\n", e.key) 206 printf("term_id: %10d\n", e.term_id) 207 208 case e.info_rc 209 when 0 210 puts('no entry in array') 211 when 1 212 puts('entry is void') 213 when 2 214 printf("entry: %10d\n", e.entry) 215 when 3 216 printf("entry: %10d\n", e.entry) 217 puts('no buffer in inv') 218 when 4 219 printf("entry: %10d\n", e.entry) 220 printf("pocket: %10d\n", e.pocket) 221 printf("chunk: %10d\n", e.chunk) 222 printf("chunk_size: %10d\n", e.chunk_size) 223 printf("buffer_free: %10d\n", e.buffer_free) 224 printf("nterms: %10d\n", e.nterms) 225 printf("nterms_void: %10d\n", e.nterms_void) 226 printf("tid_in_buffer: %10d\n", e.tid_in_buffer) 227 printf("size_in_chunk: %10d\n", e.size_in_chunk) 228 printf("pos_in_chunk: %10d\n", e.pos_in_chunk) 229 printf("size_in_buffer: %10d\n", e.size_in_buffer) 230 printf("pos_in_buffer: %10d\n", e.pos_in_buffer) 231 end 232 end 233 234 def key_check(key) 235 case key 236 when String 237 str = key 238 rid = keys.at(key) 239 when Fixnum 240 _,str = keys.key(key) 241 rid = key 242 else 243 return 244 end 245 pocket = keys.pocket(rid) 246 printf("str: %10s\n", str) 247 printf("rid: %10d\n", rid) 248 printf("pocket: %10d\n", pocket) 249 end 250 251 def info 252 info = Senna::sen_index_info(@handle) 253 @key_size, @flags, @initial_n_segments, @encoding, 254 @nrecords_keys, @file_size_keys, 255 @nrecords_lexicon, @file_size_lexicon, 256 @inv_seg_size, @inv_chunk_size = info 257 return info 258 end 259 260 def query_exec(query, records=nil, op=SEL_OR) 261 records ||= Records.new 262 Senna::sen_query_exec(@handle, query.handle, records.handle, op) 263 return records 264 end 265 end 266 267 class InvEntry 268 attr_reader :index, :key, :term_id 269 attr_reader :info_rc, :entry, :pocket, :chunk, :chunk_size, :buffer_free 270 attr_reader :nterms, :nterms_void, :tid_in_buffer, :size_in_chunk 271 attr_reader :pos_in_chunk, :size_in_buffer, :pos_in_buffer 272 def initialize(index, term) 273 @index = index 274 case term 275 when String 276 @key = term 277 @term_id = index.lexicon.at(term) 278 when Fixnum 279 _, @key = index.lexicon.key(term) 280 @term_id = term 281 else 282 return 283 end 284 info = Senna::sen_inv_entry_info(index.inv, @term_id) 285 @info_rc, @entry, @pocket, @chunk, @chunk_size, @buffer_free, 286 @nterms, @nterms_void, @tid_in_buffer, @size_in_chunk, 287 @pos_in_chunk, @size_in_buffer, @pos_in_buffer = 288 Senna::sen_inv_entry_info(index.inv, term_id) 289 end 290 end 291 292 class Sym 293 attr_reader :handle, :path 294 def Sym.callback(handle) 295 proc { Senna::sen_sym_close(handle) } 296 end 297 298 def Sym::create(path, key_size=0, flags=0, encoding=0) 299 handle = Senna::sen_sym_create(path, key_size, flags, encoding) 300 return new(handle, path) 301 end 302 303 def index_create_with_keys(path, flags=0, initial_n_segments=0, encoding=0) 304 handle = Senna::sen_index_create_with_keys(path, @handle, flags, initial_n_segments, encoding) 305 return Senna::Index.new(handle) 306 end 307 308 def index_open_with_keys(path) 309 handle = Senna::sen_index_open_with_keys(path, @handle) 310 return Senna::Index.new(handle) 311 end 312 313 def Sym::open(path) 314 handle = Senna::sen_sym_open(path) 315 return new(handle, path) 316 end 317 318 def initialize(handle, path=nil) 319 @handle = handle 320 @path = path 321 ObjectSpace.define_finalizer(self, Sym.callback(@handle)) if path 322 end 323 324 def close 325 return unless @path 326 rc = Senna::sen_sym_close(@handle) 327 @handle = nil 328 ObjectSpace.undefine_finalizer(self) 329 return rc 330 end 331 332 def remove 333 return unless @path 334 self.close 335 Senna::sen_sym_remove(@path) 336 end 337 338 def info 339 inf = Senna::sen_sym_info(@handle) 340 return {'key_size' => inf[1], 'flags' => inf[2], 'encoding' => inf[3], 'nrecords' => inf[4], 'file_size' => inf[5]} 341 end 342 343 def get(key) 344 Senna::sen_sym_get(@handle, key) 345 end 346 347 def at(key) 348 Senna::sen_sym_at(@handle, key) 349 end 350 351 def del(key) 352 Senna::sen_sym_del(@handle, key) 353 end 354 355 def key(id) 356 Senna::sen_sym_key(@handle, id, SYM_MAX_KEY_SIZE) 357 end 358 359 def pocket_get(id) 360 Senna::sen_sym_pocket_get(@handle, id) 361 end 362 363 def pocket_set(id, value) 364 Senna::sen_sym_pocket_set(@handle, id, value) 365 end 366 367 def size 368 Senna::sen_sym_size(@handle) 369 end 370 371 def next(id) 372 Senna::sen_sym_next(@handle, id) 373 end 374 375 def terms2array(terms) 376 return nil unless terms 377 res = [] 378 c = Senna::sen_set_cursor_open(terms) 379 loop { 380 eh,id,_ = Senna::sen_set_cursor_next(c, 4, -1) 381 break unless eh 382 _,str = Senna::sen_sym_key(@handle, id.unpack('L')[0], SYM_MAX_KEY_SIZE) 383 res << str 384 } 385 Senna::sen_set_cursor_close(c) 386 res 387 end 388 389 def prefix_search(key) 390 terms2array(Senna::sen_sym_prefix_search(@handle, key)) 391 end 392 393 def suffix_search(key) 394 terms2array(Senna::sen_sym_suffix_search(@handle, key)) 395 end 396 397 def common_prefix_search(key) 398 id = Senna::sen_sym_common_prefix_search(@handle, key) 399 _,str = Senna::sen_sym_key(@handle, id, SYM_MAX_KEY_SIZE) 400 return str 401 end 402 end 403 404 class Element 405 attr_reader :phandle, :handle, :key, :value 406 def initialize(phandle, handle) 407 @phandle = phandle 408 @handle = handle 409 @key, @value = Senna::sen_record_info(@phandle, @handle) 410 end 411 end 412 413 class Set 414 attr_reader :handle 415 def Set.callback(handle) 416 proc { Senna::sen_set_close(handle) } 417 end 418 419 def Set::open(keysize, value_size, index_size) 420 handle = Senna::sen_set_open(keysize, value_size, index_size); 421 return new(handle) 422 end 423 424 def initialize(handle, closable=true) 425 @handle = handle 426 @closable = closable 427 ObjectSpace.define_finalizer(self, Set.callback(handle)) if closable 428 end 429 430 def close 431 return unless @closable 432 rc = Senna::sen_set_close(@handle) 433 @handle = nil 434 ObjectSpace.undefine_finalizer(self) 435 return rc 436 end 437 438 def info 439 inf = Senna::sen_set_info(@handle) 440 return { 'key_size' => inf[0], 'value_size' => inf[1], 'n_entries' => inf[2] } 441 end 442 443 def []=(key, value) 444 return get(key, value) 445 end 446 447 def get(key, value) 448 eh, _ = Senna::sen_set_get(@handle, key, value) 449 return eh 450 end 451 452 def [](key) 453 _, value = Senna::sen_set_at(@handle, key) 454 return value 455 end 456 457 def at(key) 458 eh, _ = Senna::sen_set_at(@handle, key) 459 return eh 460 end 461 462 def del(eh) 463 Senna::sen_set_del(@handle, eh) 464 end 465 466 def cursor_open 467 Senna::sen_set_cursor_open(@handle) 468 end 469 470 def cursor_next(cursor) 471 eh, _, _ = Senna::sen_set_cursor_next(cursor) 472 return eh 473 end 474 475 def cursor_close(cursor) 476 Senna::sen_set_cursor_close(cursor) 477 end 478 479 def element_info(eh, key=0, value=0) 480 Senna::sen_set_element_info(@handle, eh, key, value) 481 end 482 483 def +(obj) 484 union(obj) 485 end 486 487 def union(obj) 488 Senna::sen_set_union(@handle, obj) 489 end 490 491 def -(obj) 492 subtract(obj) 493 end 494 495 def subtract(obj) 496 Senna::sen_set_subtract(@handle, obj) 497 end 498 499 def *(obj) 500 intersect(obj) 501 end 502 503 def intersect(obj) 504 Senna::sen_set_intersect(@handle, obj) 505 end 506 507 def difference(obj) 508 Senna::sen_set_difference(@handle, obj) 509 end 510 511 def sort(limit=0, optarg=nil) 512 Senna::sen_set_sort(@handle, limit, optarg) 513 end 514 end 515 516 class Records 517 attr_reader :handle 518 def Records.callback(handle) 519 proc { Senna::sen_records_close(handle) } 520 end 521 522 def Records::open(record_unit=REC_DOCUMENT, subrec_unit=REC_NONE, max_n_subrecs=0) 523 handle = Senna::sen_records_open(record_unit, subrec_unit, max_n_subrecs) 524 return new(handle) 525 end 526 527 def initialize(handle=nil, closable=true) 528 handle ||= Senna::sen_records_open(REC_DOCUMENT, REC_NONE, 0) 529 @handle = handle 530 @closable = closable 531 ObjectSpace.define_finalizer(self, Records.callback(handle)) if closable 532 end 533 534 def nhits 535 Senna::sen_records_nhits(@handle) 536 end 537 538 def each 539 while rec = self.next do 540 yield self.curr_key, self.curr_score 541 end 542 end 543 544 def find(key) 545 key = [key].pack('L') if Fixnum === key 546 Senna::sen_records_find(@handle, key) 547 end 548 549 def rewind 550 Senna::sen_records_rewind(@handle) 551 end 552 553 def next 554 size,_,score = Senna::sen_records_next(@handle, 0) 555 if size > 0 556 _, res = Senna::sen_records_curr_key(@handle, size) 557 else 558 res = nil 559 end 560 return res 561 end 562 563 def curr_rec(bufsize=0) 564 rh = Senna::sen_records_curr_rec(@handle) 565 return Record.new(@handle, rh, bufsize) 566 end 567 568 def at(key, section, pos, bufsize=0) 569 rh, _, _ = Senna::sen_records_at(@handle, key, section, pos) 570 return Record.new(@handle, rh, bufsize) 571 end 572 573 def curr_key 574 size, res = Senna::sen_records_curr_key(@handle, 128) 575 return res if size <= 128 576 _, res = Senna::sen_records_curr_key(@handle, size) 577 return res 578 end 579 580 def curr_score 581 return Senna::sen_records_curr_score(@handle) 582 end 583 584 def sort(limit, opt=nil) 585 return Senna::sen_records_sort(@handle, limit, opt) 586 end 587 588 def group(limit, opt=nil) 589 return Senna::sen_records_group(@handle, limit, opt) 590 end 591 592 def close 593 return unless @closable 594 rc = Senna::sen_records_close(@handle) 595 @handle = nil 596 self.invalidation 597 return rc 598 end 599 600 def invalidation 601 @handle = nil 602 ObjectSpace.undefine_finalizer(self) 603 end 604 605 def records_restruct(subj, newhandle) 606 self.invalidation 607 subj.invalidation 608 @handle = newhandle 609 ObjectSpace.define_finalizer(self, Records.callback(handle)) 610 end 611 612 def union(subj) 613 newhandle = Senna::sen_records_union(@handle, subj.handle) 614 self.records_restruct(subj, newhandle) 615 end 616 617 def subtract(subj) 618 newhandle = Senna::sen_records_subtract(@handle, subj.handle) 619 self.records_restruct(subj, newhandle) 620 end 621 622 def intersect(subj) 623 newhandle = Senna::sen_records_intersect(@handle, subj.handle) 624 self.records_restruct(subj, newhandle) 625 end 626 627 def difference(subj) 628 Senna::sen_records_difference(@handle, subj.handle) 629 end 630 631 def sort(limit, optarg=nil) 632 Senna::sen_records_sort(@handle, limit, optarg) 633 end 634 635 def group(limit, optarg=nil) 636 Senna::sen_records_group(@handle, limit, optarg) 637 end 638 end 639 640 class Record 641 attr_reader :phandle, :handle, :key, :keysize, :section, :pos, :score, :n_subrecs 642 def initialize(phandle, handle, bufsize=0) 643 @phandle = phandle 644 @handle = handle 645 rc, @key, @keysize, @section, @pos, @score, @n_subrecs = 646 Senna::sen_record_info(@phandle, @handle, bufsize) 647 end 648 649 def subrec(index, bufsize=0) 650 return SubRecord.new(@phandle, @handle, index, bufsize) 651 end 652 end 653 654 class SubRecord 655 attr_reader :phandle, :handle, :index, :key, :keysize, :section, :pos, :score 656 def initialize(phandle, handle, index, bufsize=0) 657 @phandle = phandle 658 @handle = handle 659 @index = index 660 rc, @key, @keysize, @section, @pos, @score = 661 Senna::sen_record_subrec_info(@phandle, @handle, index, bufsize) 662 end 663 end 664 665 class Values 666 attr_reader :handle 667 def Values.callback(handle) 668 proc { Senna::sen_values_close(handle) } 669 end 670 671 def Values::open(str=nil, weight=0) 672 handle = Senna::sen_values_open 673 ret = new(handle) 674 ret.add(str, weight) unless str.nil? 675 return ret 676 end 677 678 def add(str, weight=0) 679 Senna::sen_values_add(@handle, str, str.length, weight) 680 end 681 682 def initialize(handle) 683 @handle = handle 684 ObjectSpace.define_finalizer(self, Values.callback(@handle)) 685 end 686 687 def close 688 rc = Senna::sen_values_close(@handle) 689 @handle = nil 690 ObjectSpace.undefine_finalizer(self) 691 return rc 692 end 693 end 694 695 class Query 696 attr_reader :handle 697 def Query.callback(handle) 698 proc { Senna::sen_query_close(handle) } 699 end 700 701 def Query::open(str, default_op=SEL_OR, max_exprs=32, encoding=ENC_DEFAULT) 702 handle = Senna::sen_query_open(str, str.length, default_op, max_exprs, encoding) 703 return new(handle) 704 end 705 706 def initialize(handle) 707 @handle = handle 708 ObjectSpace.define_finalizer(self, Query.callback(@handle)) 709 end 710 711 def close 712 rc = Senna::sen_query_close(@handle) 713 @handle = nil 714 ObjectSpace.undefine_finalizer(self) 715 return rc 716 end 717 718 def rest 719 len, _ = Senna::sen_query_rest(@handle, 0) 720 return Senna::sen_query_rest(@handle, len)[1] 721 end 722 723 def exec(index, records=nil, op=SEL_OR) 724 records ||= Records.new 725 Senna::sen_query_exec(index.handle, @handle, records.handle, op) 726 return records 727 end 728 729 def term 730 # TODO: implement! 731 raise NameError, 'Query#term is not implemented' 732 end 733 734 def scan(strs, flags) 735 # TODO: implement! 736 # [found, score] 737 raise NameError, 'Query#scan is not implemented' 738 end 739 740 def snip(flags, width, max_results, tags) 741 # TODO: implement! 742 # Snip.new(handle) 743 raise NameError, 'Query#snip is not implemented' 744 end 745 end 746 747 class Snip 748 def Snip.callback(handle) 749 proc { Senna::sen_snip_close(handle) } 750 end 751 752 def Snip::open(conds=nil, width=100, max_results=3, opentag='', closetag='', map=nil, enc=nil, flags=SNIP_NORMALIZE) 753 enc ||= [nil, 'NONE', 'EUC', 'UTF8', 'SJIS'].index($KCODE) || 0 754 case conds 755 when Array 756 condkeys = conds 757 condvals = Hash.new([nil, nil]) 758 when Hash 759 condkeys = conds.keys 760 condvals = conds 761 when String 762 condkeys = [conds] 763 condvals = { conds => [nil, nil] } 764 else 765 condkeys = [] 766 end 767 handle = Senna::sen_snip_open(enc, flags, width, max_results, 768 opentag, opentag.length, 769 closetag, closetag.length, 770 map) 771 condkeys.each {|c| 772 opentag_len = condvals[c][0].nil? ? 0 : condvals[c][0].length 773 closetag_len = condvals[c][1].nil? ? 0 : condvals[c][1].length 774 Senna::sen_snip_add_cond(handle, c, c.length, 775 condvals[c][0], opentag_len, 776 condvals[c][1], closetag_len) 777 } 778 return Snip.new(handle) 779 end 780 781 def initialize(handle) 782 @handle = handle 783 ObjectSpace.define_finalizer(self, Snip.callback(@handle)) 784 end 785 786 def add(cond, opentag=nil, closetag=nil) 787 opentag_len = opentag.nil? ? 0 : opentag.length 788 closetag_len = closetag.nil? ? 0 : closetag.length 789 Senna::sen_snip_add_cond(@handle, cond, cond.length, 790 opentag, opentag_len, 791 closetag, closetag_len) 792 end 793 794 def exec(str) 795 rc, n, maxsize = Senna::sen_snip_exec(@handle, str, str.length) 796 res = [] 797 for i in 0..n-1 798 rc, r = Senna::sen_snip_get_result(@handle, i, maxsize) 799 res << r if rc == 0 800 end 801 return res 802 end 803 804 def close 805 rc = Senna::sen_snip_close(@handle) 806 @handle = nil 807 ObjectSpace.undefine_finalizer(self) 808 return rc 809 end 810 end 811 812 class Ctx 813 def Ctx::connect(host='localhost', port=DEFAULT_PORT) 814 handle = Senna::sen_ctx_connect(host, port, 0) 815 return new(handle, nil) 816 end 817 818 def Ctx::open(path) 819 db = Senna::sen_db_open(path) 820 raise "sen_db_open(#{path}) failed" unless db 821 handle = Senna::sen_ctx_open(db, 1) 822 return new(handle, db) 823 end 824 825 def Ctx::create(path, flags=0, encoding=ENC_DEFAULT) 826 db = Senna::sen_db_create(path, flags, encoding) 827 raise "sen_db_create(#{path}) failed" unless db 828 handle = Senna::sen_ctx_open(db, 1) 829 return new(handle, db) 830 end 831 832 def Ctx.callback(handle, db) 833 proc { 834 Senna::sen_ctx_close(handle) 835 Senna::sen_db_close(db) if db 836 } 837 end 838 839 def initialize(handle, db) 840 @handle = handle 841 @db = db 842 ObjectSpace.define_finalizer(self, Ctx.callback(@handle, @db)) 843 end 844 845 def send(message) 846 return Senna::sen_ctx_send(@handle, message, message.length, 0) 847 end 848 849 def recv() 850 return Senna::sen_ctx_recv(@handle) 851 end 852 853 def exec(*messages) 854 res = nil 855 messages.each {|message| 856 rc = send(message) 857 raise IOError, 'send error' if rc != RC_SUCCESS 858 res = [] 859 loop { 860 rc, value, flags = recv 861 raise IOError, 'recv error' if rc != RC_SUCCESS 862 res << value 863 break if (flags & CTX_MORE) == 0 864 } 865 } 866 res 867 end 868 869 end 870 871 def Senna::get_select_optarg(mode, similarity_threshold_max_interval=0, weight_vector=nil, &func) 872 i = Senna::SelectOptarg.new 873 i.mode = mode 874 case mode 875 when SEL_NEAR 876 i.max_interval = similarity_threshold_max_interval 877 when SEL_SIMILAR 878 i.similarity_threshold = similarity_threshold_max_interval 879 end 880 i.weight_vector = weight_vector 881 i.func = proc {|records, docid, secno, func_arg| r = Records.new(records, false); return func.call(r, docid, secno) || 0} if func 882 return i 883 end 884 885 def Senna::get_group_optarg(mode, key_size, &func) 886 i = Senna::GroupOptarg.new 887 i.mode = mode 888 i.func = proc {|records, rh, func_arg| r = Record.new(records, rh, key_size); return func.call(r) || 0} if func 889 i.key_size = key_size 890 return i 891 end 892 893 def Senna::get_sort_optarg(mode, &compar) 894 i = Senna::SortOptarg.new 895 i.mode = mode 896 i.compar = proc {|records1, rh1, records2, rh2, compar_arg| 897 r1 = Record.new(records1, rh1); 898 r2 = Record.new(records2, rh2); 899 return compar.call(r1, r2) || 0} if compar 900 return i 901 end 902 903 def Senna::get_set_sort_optarg(mode, &compar) 904 i = Senna::SetSortOptarg.new 905 i.mode = mode 906 i.compar = proc {|set1, eh1, set2, eh2, compar_arg| 907 s1 = Set.new(set1, false); 908 s2 = Set.new(set2, false); 909 return compar.call(s1, eh1, s2, eh2) || 0} if compar 910 return i 911 end 912 913 def Senna::logger_info_set(level, flags, &func) 914 @@logger_info = LoggerInfo.new 915 @@logger_info.max_level = level 916 @@logger_info.flags = flags 917 @@logger_info.func = func 918 sen_logger_info_set(@@logger_info) 919 end 920 921 def Senna::log(level, fmt, *args) 922 msg = format(fmt, *args) 923 sen_logger_put(level, '', 0, '', '%s', msg) 924 end 925 926 def Senna::str_normalize(str, encoding, flags) 927 l, = sen_str_normalize(str, str.size, encoding, flags, 0) 928 return sen_str_normalize(str, str.size, encoding, flags, l + 1)[1] 929 end 930end 931