Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
test-inverted-index.c
Go to the documentation of this file.
1 /* -*- c-basic-offset: 2; coding: utf-8 -*- */
2 /*
3  Copyright (C) 2008-2012 Kouhei Sutou <kou@clear-code.com>
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Lesser General Public
7  License version 2.1 as published by the Free Software Foundation.
8 
9  This library is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  Lesser General Public License for more details.
13 
14  You should have received a copy of the GNU Lesser General Public
15  License along with this library; if not, write to the Free Software
16  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18 
19 #include <ii.h>
20 
21 #include <gcutter.h>
22 #include <glib/gstdio.h>
23 
24 #include "../lib/grn-assertions.h"
25 
26 void test_create(void);
30 void test_open(void);
35 void test_crud(void);
36 void test_array_index(void);
37 void test_scalar_index(void);
38 void test_int_index(void);
39 void test_mroonga_index(void);
40 void test_mroonga_index_score(void);
41 
42 #define TYPE_SIZE 1024
43 
44 static grn_logger_info *logger;
45 
46 static GList *expected_messages;
47 static GList *record_ids;
48 
49 static gchar *tmp_directory;
50 static gchar *path;
51 static grn_ctx *context;
52 static grn_obj *db;
53 static grn_obj *type;
54 static grn_obj *lexicon;
55 static grn_ii *inverted_index;
56 
57 void
59 {
60  tmp_directory = g_build_filename(grn_test_get_tmp_dir(),
61  "inverted-index",
62  NULL);
63 }
64 
65 void
67 {
68  g_free(tmp_directory);
69  cut_remove_path(grn_test_get_tmp_dir(), NULL);
70 }
71 
72 static void
73 remove_tmp_directory(void)
74 {
75  cut_remove_path(tmp_directory, NULL);
76 }
77 
78 void
79 cut_setup(void)
80 {
81  gchar *table_path;
82  const gchar *type_name, *table_name;
83 
84  cut_set_fixture_data_dir(grn_test_get_base_dir(),
85  "fixtures",
86  "inverted-index",
87  NULL);
88 
89  logger = setup_grn_logger();
90 
91  expected_messages = NULL;
92  record_ids = NULL;
93 
94  remove_tmp_directory();
95  g_mkdir_with_parents(tmp_directory, 0700);
96  path = g_build_filename(tmp_directory, "inverted-index", NULL);
97 
98  context = g_new0(grn_ctx, 1);
101 
102  db = grn_db_create(context, NULL, NULL);
103  grn_ctx_use(context, db);
104 
105  type_name = "name";
106  type = grn_type_create(context, type_name, strlen(type_name),
108 
109  table_name = "lexicon";
110  table_path = g_build_filename(tmp_directory, "lexicon-table", NULL);
111  lexicon = grn_table_create(context,
112  table_name, strlen(table_name),
113  table_path,
115  type, NULL);
116 
118  grn_ctx_at(context, GRN_DB_BIGRAM));
119 
120  g_free(table_path);
121 
122  inverted_index = NULL;
123 }
124 
125 static void
126 inverted_index_free(void)
127 {
128  if (inverted_index) {
129  grn_ii_close(context, inverted_index);
130  inverted_index = NULL;
131  }
132 }
133 
134 static void
135 expected_messages_free(void)
136 {
137  if (expected_messages) {
138  gcut_list_string_free(expected_messages);
139  expected_messages = NULL;
140  }
141 }
142 
143 static void
144 record_ids_free(void)
145 {
146  if (record_ids) {
147  gcut_list_string_free(record_ids);
148  record_ids = NULL;
149  }
150 }
151 
152 void
154 {
155  if (context) {
156  inverted_index_free();
157  if (path)
158  grn_ii_remove(context, path);
159  grn_obj_close(context, db);
160  grn_ctx_fin(context);
161  g_free(context);
162  }
163 
164  if (path) {
165  g_free(path);
166  path = NULL;
167  }
168 
169  remove_tmp_directory();
170 
171  record_ids_free();
172 
173  expected_messages_free();
174 
175  teardown_grn_logger(logger);
176 }
177 
178 #define clear_messages() \
179  grn_collect_logger_clear_messages(logger)
180 
181 #define messages() \
182  grn_collect_logger_get_messages(logger)
183 
184 #define cut_assert_create() do \
185 { \
186  inverted_index = grn_ii_create(context, path, lexicon, 0); \
187  ((grn_db_obj *)inverted_index)->header.domain = GRN_DB_VOID; \
188  cut_assert_not_null(inverted_index); \
189  cut_assert_file_exist(cut_take_printf("%s.c", path)); \
190 } while (0)
191 
192 void
194 {
196 }
197 
198 void
200 {
201  inverted_index = grn_ii_create(context, NULL, lexicon, 0);
202  ((grn_db_obj *)inverted_index)->header.domain = GRN_DB_VOID;
203  cut_assert_not_null(inverted_index);
204 }
205 
206 void
208 {
209  gssize max_size = PATH_MAX - 6;
210  GString *long_path;
211  const gchar last_component[] = G_DIR_SEPARATOR_S "index";
212 
213  long_path = grn_long_path_new(path, max_size - strlen(last_component) - 1);
214  g_free(path);
215 
216  g_mkdir_with_parents(long_path->str, 0700);
217  g_string_append(long_path, last_component);
218  path = g_string_free(long_path, FALSE);
219 
220  cut_assert_equal_int(max_size, strlen(path) + 1);
222 
223  inverted_index_free();
224 
225  long_path = g_string_new(path);
226  g_free(path);
227 
228  g_string_append(long_path, "X");
229  path = g_string_free(long_path, FALSE);
230 
231  inverted_index = grn_ii_create(context, path, lexicon, 0);
232  cut_assert_null(inverted_index);
233 }
234 
235 void
237 {
238  inverted_index = grn_ii_create(context, path, NULL, 0);
239  cut_assert_null(inverted_index);
240 }
241 
242 void
244 {
246  inverted_index_free();
247 
248  inverted_index = grn_ii_open(context, path, lexicon);
249  cut_assert(inverted_index);
250 }
251 
252 void
254 {
255  inverted_index = grn_ii_open(context, path, lexicon);
256  cut_assert_null(inverted_index);
257 }
258 
259 void
261 {
262  grn_io *io;
263  gchar *id_string;
264  const gchar *expected_error_message = "syscall error";
265 
266  io = grn_io_create(context, path, 10, 10, 10,
268  cut_assert_not_null(io);
269  id_string = grn_io_header(io);
270  strcpy(id_string, "WRONG-ID");
271  grn_io_close(context, io);
272 
273  clear_messages();
274  inverted_index = grn_ii_open(context, path, lexicon);
275  cut_assert_null(inverted_index);
276 
277  cut_assert_equal_substring(expected_error_message,
278  messages()->data,
279  strlen(expected_error_message));
280 }
281 
282 void
284 {
285  grn_io *io;
286  gchar *id_string;
287  gchar expected_error_message[] = "file type unmatch";
288 
289  io = grn_io_create(context, path, 10, 10, 10, grn_io_auto, GRN_IO_EXPIRE_SEGMENT);
290  cut_assert_not_null(io);
291  id_string = grn_io_header(io);
292  strcpy(id_string, "WRONG-ID");
293  grn_io_close(context, io);
294 
295  io = grn_io_create(context, cut_take_printf("%s.c", path),
296  10, 10, 10, grn_io_auto, GRN_IO_EXPIRE_SEGMENT);
297  cut_assert_not_null(io);
298  grn_io_close(context, io);
299 
300  clear_messages();
301  inverted_index = grn_ii_open(context, path, lexicon);
302  cut_assert_null(inverted_index);
303 
304  cut_assert_equal_substring(expected_error_message,
305  messages()->data,
306  strlen(expected_error_message));
307 }
308 
309 void
311 {
313  inverted_index_free();
314 
315  inverted_index = grn_ii_open(context, path, NULL);
316  cut_assert_null(inverted_index);
317 }
318 
319 
320 static void
321 update_data(grn_id record_id, unsigned int section,
322  const gchar *old_name, const gchar *new_name)
323 {
324  grn_obj old_value, new_value;
325  const gchar *old_data, *new_data;
326 
329 
330  if (old_name) {
331  old_data = cut_get_fixture_data_string(old_name, NULL);
332  GRN_TEXT_SET_REF(&old_value, old_data, strlen(old_data));
333  }
334 
335  if (new_name) {
336  new_data = cut_get_fixture_data_string(new_name, NULL);
337  GRN_TEXT_SET_REF(&new_value, new_data, strlen(new_data));
338  }
339 
340  grn_ii_column_update(context, inverted_index, record_id, section,
341  &old_value, &new_value, NULL);
342  grn_obj_close(context, &old_value);
343  grn_obj_close(context, &new_value);
344 }
345 
346 static void
347 add_data(grn_id record_id, unsigned int section, const gchar *name)
348 {
349  update_data(record_id, section, NULL, name);
350 }
351 
352 static void
353 remove_data(grn_id record_id, unsigned int section, const gchar *name)
354 {
355  update_data(record_id, section, name, NULL);
356 }
357 
358 static GList *
359 retrieve_record_ids(const gchar *term)
360 {
361  grn_id term_id;
362  grn_ii_cursor *cursor;
363  grn_ii_posting *posting;
364 
365  term_id = grn_table_get(context, lexicon, term, strlen(term));
366  if (term_id == GRN_ID_NIL)
367  return NULL;
368 
369  record_ids_free();
370 
371  cursor = grn_ii_cursor_open(context, inverted_index, term_id,
372  GRN_ID_NIL, GRN_ID_MAX, 5, 0);
373  if (!cursor)
374  return NULL;
375 
376  while ((posting = grn_ii_cursor_next(context, cursor))) {
377  record_ids = g_list_append(record_ids, g_strdup_printf("%d", posting->rid));
378  }
379  grn_ii_cursor_close(context, cursor);
380 
381  return record_ids;
382 }
383 
384 void
386 {
388 
389  add_data(1, 1, "API.JA");
390  add_data(2, 1, "CHECKINSTALL.JA");
391  add_data(3, 1, "FUTUREWORKS.JA");
392  add_data(4, 1, "INSTALL.JA");
393  gcut_assert_equal_list_string(gcut_take_new_list_string("1", "2", "3", NULL),
394  retrieve_record_ids("検索"));
395 
396  remove_data(1, 1, "API.JA");
397  gcut_assert_equal_list_string(gcut_take_new_list_string("2", "3", NULL),
398  retrieve_record_ids("検索"));
399 
400  update_data(3, 1, "FUTUREWORKS.JA", "Makefile.am");
401  gcut_assert_equal_list_string(gcut_take_new_list_string("2", NULL),
402  retrieve_record_ids("検索"));
403 
404  remove_data(2, 1, "CHECKINSTALL.JA");
405  add_data(3, 2, "FUTUREWORKS.JA");
406  gcut_assert_equal_list_string(gcut_take_new_list_string("3", NULL),
407  retrieve_record_ids("検索"));
408 
409  update_data(4, 1, "INSTALL.JA", "README.JA");
410  gcut_assert_equal_list_string(gcut_take_new_list_string("3", "4", NULL),
411  retrieve_record_ids("検索"));
412 
413  remove_data(4, 1, "README.JA");
414  gcut_assert_equal_list_string(gcut_take_new_list_string("3", NULL),
415  retrieve_record_ids("検索"));
416 
417  remove_data(3, 2, "FUTUREWORKS.JA");
418  gcut_assert_equal_list_string(NULL, retrieve_record_ids("検索"),
419  cut_message("this assertion is wrong?"));
420 }
421 
422 static grn_rc
423 set_index_source(grn_obj *index, grn_obj *source)
424 {
425  grn_rc rc;
426  grn_obj buf;
427  grn_id id = grn_obj_id(context, source);
428  GRN_TEXT_INIT(&buf, 0);
429  grn_bulk_write(context, &buf, (void *)&id, sizeof(grn_id));
430  cut_assert_equal_int(grn_obj_get_nhooks(context, source, GRN_HOOK_SET), 0);
431  rc = grn_obj_set_info(context, index, GRN_INFO_SOURCE, &buf);
432  cut_assert_equal_int(grn_obj_get_nhooks(context, source, GRN_HOOK_SET), 1);
433  GRN_BULK_REWIND(&buf);
434  cut_assert_null(grn_obj_get_hook(context, source, GRN_HOOK_SET, 0, &buf));
435  cut_assert_equal_int(*((grn_id *)GRN_BULK_HEAD(&buf)), grn_obj_id(context, index));
436  grn_obj_close(context, &buf);
437  return rc;
438 }
439 
440 static void
441 insert_and_search(grn_obj *users, grn_obj *items, grn_obj *checks, grn_obj *checked)
442 {
443  grn_id user1 = grn_table_add(context, users, NULL, 0, NULL);
444  grn_id user2 = grn_table_add(context, users, NULL, 0, NULL);
445  grn_id item = grn_table_add(context, items, NULL, 0, NULL);
446  grn_obj value, *res;
447  GRN_TEXT_INIT(&value, 0);
448  res = grn_table_create(context, NULL, 0, NULL, GRN_TABLE_HASH_KEY, users, 0);
449  cut_assert_not_null(res);
450  grn_bulk_write(context, &value, (void *)&item, sizeof(grn_id));
451  value.header.domain = grn_obj_id(context, items);
452  grn_test_assert(grn_obj_set_value(context, checks, user1, &value, GRN_OBJ_SET));
453  grn_test_assert(grn_obj_search(context, checked, &value, res, GRN_OP_OR, NULL));
454  cut_assert_equal_int(grn_table_size(context, res), 1);
455  grn_test_assert(grn_obj_set_value(context, checks, user2, &value, GRN_OBJ_SET));
456  grn_test_assert(grn_obj_search(context, checked, &value, res, GRN_OP_OR, NULL));
457  cut_assert_equal_int(grn_table_size(context, res), 2);
458  grn_obj_close(context, &value);
459  grn_obj_close(context, res);
460 }
461 
462 void
464 {
465  gchar *db_path;
466  const gchar *name;
467  grn_obj *users, *items, *checks, *checked;
468 
469  grn_obj_close(context, db);
470 
471  remove_tmp_directory();
472  g_mkdir_with_parents(tmp_directory, 0700);
473  db_path = g_build_filename(tmp_directory, "inverted-index", NULL);
474  db = grn_db_create(context, db_path, NULL);
475  g_free(db_path);
476 
477  name = "users";
478  users = grn_table_create(context, name, strlen(name), NULL,
480  cut_assert_not_null(users);
481 
482  name = "items";
483  items = grn_table_create(context, name, strlen(name), NULL,
485  cut_assert_not_null(items);
486 
487  name = "checks";
488  checks = grn_column_create(context, users, name, strlen(name), NULL,
490  cut_assert_not_null(checks);
491 
492  name = "checked";
493  checked = grn_column_create(context, items, name, strlen(name), NULL,
495  cut_assert_not_null(checked);
496 
497  grn_test_assert(set_index_source(checked, checks));
498 
499  insert_and_search(users, items, checks, checked);
500 
501  grn_obj_close(context, checks);
502  grn_obj_close(context, checked);
503  grn_obj_close(context, items);
504  grn_obj_close(context, users);
505 }
506 
507 void
509 {
510  gchar *db_path;
511  const gchar *name;
512  grn_obj *users, *items, *checks, *checked;
513 
514  grn_obj_close(context, db);
515 
516  remove_tmp_directory();
517  g_mkdir_with_parents(tmp_directory, 0700);
518  db_path = g_build_filename(tmp_directory, "inverted-index", NULL);
519  db = grn_db_create(context, db_path, NULL);
520  g_free(db_path);
521 
522  name = "users";
523  users = grn_table_create(context, name, strlen(name), NULL,
525  cut_assert_not_null(users);
526 
527  name = "items";
528  items = grn_table_create(context, name, strlen(name), NULL,
530  cut_assert_not_null(items);
531 
532  name = "checks";
533  checks = grn_column_create(context, users, name, strlen(name), NULL,
535  cut_assert_not_null(checks);
536 
537  name = "checked";
538  checked = grn_column_create(context, items, name, strlen(name), NULL,
540  cut_assert_not_null(checked);
541 
542  grn_test_assert(set_index_source(checked, checks));
543 
544  insert_and_search(users, items, checks, checked);
545 
546  grn_obj_close(context, checks);
547  grn_obj_close(context, checked);
548  grn_obj_close(context, items);
549  grn_obj_close(context, users);
550 }
551 
552 void
554 {
555  gchar *db_path;
556  const gchar *name;
557  grn_obj *users, *items, *checks, *checked, *int_type;
558 
559  grn_obj_close(context, db);
560 
561  remove_tmp_directory();
562  g_mkdir_with_parents(tmp_directory, 0700);
563  db_path = g_build_filename(tmp_directory, "inverted-index", NULL);
564  db = grn_db_create(context, db_path, NULL);
565  g_free(db_path);
566 
567  int_type = grn_ctx_at(context, GRN_DB_INT32);
568  cut_assert_not_null(int_type);
569 
570  name = "users";
571  users = grn_table_create(context, name, strlen(name), NULL,
573  cut_assert_not_null(users);
574 
575  name = "items";
576  items = grn_table_create(context, name, strlen(name), NULL,
578  cut_assert_not_null(items);
579 
580  name = "checks";
581  checks = grn_column_create(context, users, name, strlen(name), NULL,
583  cut_assert_not_null(checks);
584 
585  name = "checked";
586  checked = grn_column_create(context, items, name, strlen(name), NULL,
588  cut_assert_not_null(checked);
589 
590  grn_test_assert(set_index_source(checked, checks));
591 
592  {
593  int32_t key = 1;
594  grn_obj value, query, *res;
595  grn_id user1 = grn_table_add(context, users, NULL, 0, NULL);
596  grn_id user2 = grn_table_add(context, users, NULL, 0, NULL);
597  grn_id item = grn_table_add(context, items, &key, sizeof(int32_t), NULL);
598  GRN_TEXT_INIT(&value, 0);
599  GRN_TEXT_INIT(&query, 0);
600  res = grn_table_create(context, NULL, 0, NULL, GRN_TABLE_HASH_KEY, users, 0);
601  cut_assert_not_null(res);
602  grn_bulk_write(context, &value, (void *)&item, sizeof(grn_id));
603  value.header.domain = grn_obj_id(context, items);
604  grn_bulk_write(context, &query, (void *)&key, sizeof(int32_t));
605  query.header.domain = GRN_DB_INT32;
606  grn_test_assert(grn_obj_set_value(context, checks, user1, &value, GRN_OBJ_SET));
607  grn_test_assert(grn_obj_search(context, checked, &value, res, GRN_OP_OR, NULL));
608  cut_assert_equal_int(grn_table_size(context, res), 1);
609  grn_test_assert(grn_obj_set_value(context, checks, user2, &value, GRN_OBJ_SET));
610  grn_test_assert(grn_obj_search(context, checked, &query, res, GRN_OP_OR, NULL));
611  cut_assert_equal_int(grn_table_size(context, res), 2);
612  grn_obj_close(context, &query);
613  grn_obj_close(context, &value);
614  grn_obj_close(context, res);
615  }
616 
617  grn_obj_close(context, checks);
618  grn_obj_close(context, checked);
619  grn_obj_close(context, items);
620  grn_obj_close(context, users);
621 }
622 
623 void
625 {
626  grn_obj *t1,*c1,*lc,*ft;
627  grn_obj buff;
628  grn_id c1_id,r1,r2,r3,r4;
629  const gchar *mrn_dir;
630 
631  mrn_dir = cut_build_path(tmp_directory, "mrn", NULL);
632  g_mkdir_with_parents(mrn_dir, 0700);
633 
634  grn_obj_close(context, db);
635  db = grn_db_create(context,
636  cut_build_path(mrn_dir, "mroonga.grn", NULL),
637  NULL);
638  cut_assert_not_null(db);
639 
640  /* actual table */
641  t1 = grn_table_create(context, "t1", 2,
642  cut_build_path(mrn_dir, "t1.grn", NULL),
644  cut_assert_not_null(t1);
645 
646  /* lexicon table */
647  lc = grn_table_create(context, "lc", 2,
648  cut_build_path(mrn_dir, "lc.grn", NULL),
650  grn_ctx_at(context, GRN_DB_SHORT_TEXT), 0);
651  cut_assert_not_null(lc);
653  grn_ctx_at(context, GRN_DB_BIGRAM)));
654 
655  /* actual column */
656  c1 = grn_column_create(context, t1, "c1", 2,
657  cut_build_path(mrn_dir, "t1.c1.grn", NULL),
659  grn_ctx_at(context, GRN_DB_TEXT));
660  cut_assert_not_null(c1);
661 
662  /* fulltext index */
663  ft = grn_column_create(context, lc, "ft", 2,
664  cut_build_path(mrn_dir, "lc.ft.grn", NULL),
666  cut_assert_not_null(ft);
667 
668  GRN_TEXT_INIT(&buff,0);
669 
670  /* link between actual column and fulltext index */
671  c1_id = grn_obj_id(context, c1);
672  GRN_TEXT_SET(context, &buff, (char*)&c1_id, sizeof(grn_id));
673  grn_obj_set_info(context, ft, GRN_INFO_SOURCE, &buff); /* need to use grn_id */
674 
675  /* insert row */
676  r1 = grn_table_add(context, t1, NULL, 0, NULL);
677  cut_assert_equal_int(1,r1);
678  GRN_TEXT_SET(context, &buff, "abcde", 5);
679  grn_test_assert(grn_obj_set_value(context, c1, r1, &buff, GRN_OBJ_SET));
680 
681  r2 = grn_table_add(context, t1, NULL, 0, NULL);
682  cut_assert_equal_int(2,r2);
683  GRN_TEXT_SET(context, &buff, "fghij", 5);
684  grn_test_assert(grn_obj_set_value(context, c1, r2, &buff, GRN_OBJ_SET));
685 
686  r3 = grn_table_add(context, t1, NULL, 0, NULL);
687  cut_assert_equal_int(3,r3);
688  GRN_TEXT_SET(context, &buff, "11 22 33", 8);
689  grn_test_assert(grn_obj_set_value(context, c1, r3, &buff, GRN_OBJ_SET));
690 
691  r4 = grn_table_add(context, t1, NULL, 0, NULL);
692  cut_assert_equal_int(4,r4);
693  GRN_TEXT_SET(context, &buff, "44 22 55", 8);
694  grn_test_assert(grn_obj_set_value(context, c1, r4, &buff, GRN_OBJ_SET));
695 
696  /* confirm record are inserted in both column and index */
697  cut_assert_equal_int(4,grn_table_size(context,t1));
698  cut_assert_equal_int(23,grn_table_size(context,lc));
699 
700  /* nlq search */
701  {
702  grn_id id, docid;
703  grn_obj *res;
704  grn_table_cursor *tc;
705  res = grn_table_create(context, NULL, 0, NULL, GRN_TABLE_HASH_KEY, t1, 0);
706  GRN_BULK_REWIND(&buff);
707  GRN_TEXT_SET(context, &buff, "hi", 2);
708  grn_obj_search(context, ft, &buff, res, GRN_OP_OR, NULL);
709  cut_assert_equal_int(1, grn_table_size(context, res));
710  tc = grn_table_cursor_open(context, res, NULL, 0, NULL, 0, 0, -1, 0);
711  while ((id = grn_table_cursor_next(context, tc))) {
712  GRN_BULK_REWIND(&buff);
713  grn_table_get_key(context, res, id, &docid, sizeof(grn_id));
714  cut_assert_equal_int(2, docid);
715  cut_assert_not_null(grn_obj_get_value(context, c1, docid, &buff));
716  cut_assert_equal_int(5 ,GRN_TEXT_LEN(&buff));
717  cut_assert_equal_substring("fghij", (char*) GRN_BULK_HEAD(&buff),GRN_TEXT_LEN(&buff));
718  }
719  grn_table_cursor_close(context, tc);
720  grn_obj_close(context, res);
721  }
722 
723  /* boolean search */
724  {
725  grn_id id, docid;
726  grn_obj *match_columns, *match_columns_variable;
727  grn_obj *expression, *expression_variable;
728  grn_obj *res;
729  grn_table_cursor *tc;
730  const char *match_columns_expression = "c1";
731  const char *qstr = "+22 -55";
732 
733  GRN_EXPR_CREATE_FOR_QUERY(context, t1,
734  match_columns, match_columns_variable);
735  grn_expr_parse(context, match_columns,
736  match_columns_expression,
737  strlen(match_columns_expression),
738  NULL, GRN_OP_MATCH, GRN_OP_AND,
740  GRN_EXPR_CREATE_FOR_QUERY(context, t1, expression, expression_variable);
741  res = grn_table_create(context, NULL, 0, NULL, GRN_TABLE_HASH_KEY, t1, 0);
742  grn_test_assert(grn_expr_parse(context, expression,
743  qstr, strlen(qstr),
744  match_columns,
747  grn_table_select(context, t1, expression, res, GRN_OP_OR);
748  cut_assert_equal_int(1, grn_table_size(context, res));
749  tc = grn_table_cursor_open(context, res, NULL, 0, NULL, 0, 0, -1, 0);
750  while ((id = grn_table_cursor_next(context, tc))) {
751  GRN_BULK_REWIND(&buff);
752  grn_table_get_key(context, res, id, &docid, sizeof(grn_id));
753  cut_assert_equal_int(3, docid);
754  cut_assert_not_null(grn_obj_get_value(context, c1, docid, &buff));
755  cut_assert_equal_int(8 ,GRN_TEXT_LEN(&buff));
756  cut_assert_equal_substring("11 22 33", (char*) GRN_BULK_HEAD(&buff),GRN_TEXT_LEN(&buff));
757  }
758  grn_obj_close(context, expression);
759  grn_obj_close(context, match_columns);
760  grn_table_cursor_close(context ,tc);
761  grn_obj_close(context, res);
762  }
763 
764  grn_obj_close(context, &buff);
765  grn_obj_close(context, ft);
766  grn_obj_close(context, c1);
767  grn_obj_close(context, lc);
768  grn_obj_close(context, t1);
769 }
770 
771 void
773 {
774  grn_obj *t1,*c1,*lc,*ft;
775  grn_obj buff;
776  grn_id r1,r2,r3,r4;
777  const gchar *mrn_dir;
778 
779  mrn_dir = cut_build_path(tmp_directory, "mrn", NULL);
780  g_mkdir_with_parents(mrn_dir, 0700);
781 
782  grn_obj_close(context, db);
783  db = grn_db_create(context,
784  cut_build_path(mrn_dir, "mroonga.grn", NULL),
785  NULL);
786  cut_assert_not_null(db);
787 
788  /* actual table */
789  t1 = grn_table_create(context, "t1", 2,
790  cut_build_path(mrn_dir, "t1.grn", NULL),
792  cut_assert_not_null(t1);
793 
794  /* lexicon table */
795  lc = grn_table_create(context, "lc", 2,
796  cut_build_path(mrn_dir, "lc.grn", NULL),
798  grn_ctx_at(context, GRN_DB_SHORT_TEXT), 0);
799  cut_assert_not_null(lc);
801  grn_ctx_at(context, GRN_DB_BIGRAM)));
802 
803  /* actual column */
804  c1 = grn_column_create(context, t1, "c1", 2,
805  cut_build_path(mrn_dir, "t1.c1.grn", NULL),
807  grn_ctx_at(context, GRN_DB_TEXT));
808  cut_assert_not_null(c1);
809 
810  /* fulltext index */
811  ft = grn_column_create(context, lc, "ft", 2,
812  cut_build_path(mrn_dir, "lc.ft.grn", NULL),
814  cut_assert_not_null(ft);
815 
816  GRN_TEXT_INIT(&buff,0);
817 
818  /* link between actual column and fulltext index */
819  GRN_UINT32_SET(context, &buff, grn_obj_id(context, c1));
820  grn_obj_set_info(context, ft, GRN_INFO_SOURCE, &buff); /* need to use grn_id */
821 
822  /* insert row */
823  r1 = grn_table_add(context, t1, NULL, 0, NULL);
824  cut_assert_equal_int(1,r1);
825  GRN_TEXT_SETS(context, &buff, "abcde");
826  grn_test_assert(grn_obj_set_value(context, c1, r1, &buff, GRN_OBJ_SET));
827 
828  r2 = grn_table_add(context, t1, NULL, 0, NULL);
829  cut_assert_equal_int(2,r2);
830  GRN_TEXT_SETS(context, &buff, "fghij");
831  grn_test_assert(grn_obj_set_value(context, c1, r2, &buff, GRN_OBJ_SET));
832 
833  r3 = grn_table_add(context, t1, NULL, 0, NULL);
834  cut_assert_equal_int(3,r3);
835  GRN_TEXT_SETS(context, &buff, "11 22 33");
836  grn_test_assert(grn_obj_set_value(context, c1, r3, &buff, GRN_OBJ_SET));
837 
838  r4 = grn_table_add(context, t1, NULL, 0, NULL);
839  cut_assert_equal_int(4,r4);
840  GRN_TEXT_SETS(context, &buff, "44 22 55");
841  grn_test_assert(grn_obj_set_value(context, c1, r4, &buff, GRN_OBJ_SET));
842 
843  /* confirm record are inserted in both column and index */
844  cut_assert_equal_int(4,grn_table_size(context,t1));
845  cut_assert_equal_int(23,grn_table_size(context,lc));
846 
847  /* nlq search */
848  {
849  grn_id id, docid;
850  grn_obj *res;
851  grn_table_cursor *tc;
852  grn_obj score, *score_column;
853  res = grn_table_create(context, NULL, 0, NULL,
855  GRN_UINT32_INIT(&score, 0);
856  GRN_BULK_REWIND(&buff);
857  GRN_TEXT_SETS(context, &buff, "hij");
858  grn_obj_search(context, ft, &buff, res, GRN_OP_OR, NULL);
859  cut_assert_equal_int(1, grn_table_size(context, res));
860  score_column = grn_obj_column(context, res, "_score", 6);
861  tc = grn_table_cursor_open(context, res, NULL, 0, NULL, 0, 0, -1, 0);
862  while ((id = grn_table_cursor_next(context, tc))) {
863  GRN_BULK_REWIND(&buff);
864  grn_table_get_key(context, res, id, &docid, sizeof(grn_id));
865  cut_assert_equal_int(2, docid);
866  cut_assert_not_null(grn_obj_get_value(context, c1, docid, &buff));
867  cut_assert_equal_int(5 ,GRN_TEXT_LEN(&buff));
868  cut_assert_equal_substring("fghij", (char*) GRN_BULK_HEAD(&buff),GRN_TEXT_LEN(&buff));
869  grn_obj_get_value(context, score_column, id, &score);
870  cut_assert_equal_uint(1, GRN_UINT32_VALUE(&score));
871  }
872  grn_table_cursor_close(context, tc);
873  grn_obj_close(context, score_column);
874  grn_obj_close(context, res);
875  }
876 
877  /* boolean search */
878  {
879  grn_id id, docid;
880  grn_obj *res;
881  grn_obj *match_columns, *match_columns_variable;
882  grn_obj *expression, *expression_variable;
883  grn_table_cursor *tc;
884  grn_obj score, *score_column;
885  const char *match_columns_expression = "c1 * 5";
886  const char *qstr = "+22 -55";
887 
888  GRN_EXPR_CREATE_FOR_QUERY(context, t1,
889  match_columns, match_columns_variable);
890  grn_expr_parse(context, match_columns,
891  match_columns_expression,
892  strlen(match_columns_expression),
893  NULL, GRN_OP_MATCH, GRN_OP_AND,
895  GRN_EXPR_CREATE_FOR_QUERY(context, t1, expression, expression_variable);
896  res = grn_table_create(context, NULL, 0, NULL,
898  grn_test_assert(grn_expr_parse(context, expression,
899  qstr, strlen(qstr),
900  match_columns,
903  grn_table_select(context, t1, expression, res, GRN_OP_OR);
904  cut_assert_equal_int(1, grn_table_size(context, res));
905  GRN_UINT32_INIT(&score, 0);
906  score_column = grn_obj_column(context, res, "_score", 6);
907  tc = grn_table_cursor_open(context, res, NULL, 0, NULL, 0, 0, -1, 0);
908  while ((id = grn_table_cursor_next(context, tc))) {
909  GRN_BULK_REWIND(&buff);
910  grn_table_get_key(context, res, id, &docid, sizeof(grn_id));
911  cut_assert_equal_int(3, docid);
912  cut_assert_not_null(grn_obj_get_value(context, c1, docid, &buff));
913  cut_assert_equal_int(8, GRN_TEXT_LEN(&buff));
914  cut_assert_equal_substring("11 22 33", (char*) GRN_BULK_HEAD(&buff),GRN_TEXT_LEN(&buff));
915  grn_obj_get_value(context, score_column, id, &score);
916  cut_assert_equal_uint(5, GRN_UINT32_VALUE(&score));
917  }
918  grn_obj_close(context, expression);
919  grn_obj_close(context, match_columns);
920  grn_table_cursor_close(context ,tc);
921  grn_obj_close(context, score_column);
922  grn_obj_close(context, res);
923  }
924 
925  grn_obj_close(context, &buff);
926  grn_obj_close(context, ft);
927  grn_obj_close(context, c1);
928  grn_obj_close(context, lc);
929  grn_obj_close(context, t1);
930 }