1 /* dzl-fuzzy-index-builder.c
2 *
3 * Copyright (C) 2016 Christian Hergert <christian@hergert.me>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #define G_LOG_DOMAIN "dzl-fuzzy-index-builder"
20 #define MAX_KEY_ENTRIES (0x00FFFFFF)
21
22 #include "config.h"
23
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "search/dzl-fuzzy-index-builder.h"
28 #include "util/dzl-macros.h"
29 #include "util/dzl-variant.h"
30
31 struct _DzlFuzzyIndexBuilder
32 {
33 GObject object;
34
35 guint case_sensitive : 1;
36
37 /*
38 * This hash table contains a mapping of GVariants so that we
39 * deduplicate insertions of the same document. This helps when
40 * we have indexes that contain multiple strings to the same
41 * piece of data.
42 */
43 GHashTable *documents_hash;
44
45 /*
46 * This array contains a pointer to the individual GVariants
47 * while building the index. When writing the index to disk,
48 * we create a fixed array from this array of varians.
49 */
50 GPtrArray *documents;
51
52 /*
53 * Since we will need to keep a copy of a lot of strings, we
54 * use a GString chunk to reduce the presure on the allocator.
55 * It can certainly leave some gaps that are unused in the
56 * sequence of pages, but it is generally better than using
57 * a GByteArray or some other pow^2 growing array.
58 */
59 GStringChunk *strings;
60
61 /*
62 * This maps a pointer to a string that is found in the strings
63 * string chunk to a key id (stored as a pointer). The input
64 * string must exist within strings as we use a direct hash from
65 * the input pointer to map to the string to save on the cost
66 * of key equality checks.
67 */
68 GHashTable *key_ids;
69
70 /*
71 * An array of keys where the index of the key is the "key_id" used
72 * in other structures. The pointer points to a key within the
73 * strings GStringChunk.
74 */
75 GPtrArray *keys;
76
77 /*
78 * This array maps our document id to a key id. When building the
79 * search index we use this to disambiguate between multiple
80 * documents pointing to the same document.
81 */
82 GArray *kv_pairs;
83
84 /*
85 * Metadata for the search index, which is stored as the "metadata"
86 * key in the final search index. You can use fuzzy_index_get_metadata()
87 * to retrieve values stored here.
88 *
89 * This might be useful to store things like the mtime of the data
90 * you are indexes so that you know if you need to reindex. You might
91 * also store the version of your indexer here so that when you update
92 * your indexer code, you can force a rebuild of the index.
93 */
94 GHashTable *metadata;
95 };
96
97 typedef struct
98 {
99 /* The position within the keys array of the key. */
100 guint key_id;
101
102 /* The position within the documents array of the document */
103 guint document_id;
104 } KVPair;
105
106 typedef struct
107 {
108 /*
109 * The character position within the string in terms of unicode
110 * characters, not byte-position.
111 */
112 guint position;
113
114 /* The index into the kvpairs */
115 guint lookaside_id;
116 } IndexItem;
117
118 G_DEFINE_TYPE (DzlFuzzyIndexBuilder, dzl_fuzzy_index_builder, G_TYPE_OBJECT)
119
120 enum {
121 PROP_0,
122 PROP_CASE_SENSITIVE,
123 N_PROPS
124 };
125
126 static GParamSpec *properties [N_PROPS];
127
128 static guint
mask_priority(guint key_id)129 mask_priority (guint key_id)
130 {
131 return key_id & 0x00FFFFFF;
132 }
133
134 static void
dzl_fuzzy_index_builder_finalize(GObject * object)135 dzl_fuzzy_index_builder_finalize (GObject *object)
136 {
137 DzlFuzzyIndexBuilder *self = (DzlFuzzyIndexBuilder *)object;
138
139 g_clear_pointer (&self->documents_hash, g_hash_table_unref);
140 g_clear_pointer (&self->documents, g_ptr_array_unref);
141 g_clear_pointer (&self->strings, g_string_chunk_free);
142 g_clear_pointer (&self->kv_pairs, g_array_unref);
143 g_clear_pointer (&self->metadata, g_hash_table_unref);
144 g_clear_pointer (&self->key_ids, g_hash_table_unref);
145 g_clear_pointer (&self->keys, g_ptr_array_unref);
146
147 G_OBJECT_CLASS (dzl_fuzzy_index_builder_parent_class)->finalize (object);
148 }
149
150 static void
dzl_fuzzy_index_builder_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)151 dzl_fuzzy_index_builder_get_property (GObject *object,
152 guint prop_id,
153 GValue *value,
154 GParamSpec *pspec)
155 {
156 DzlFuzzyIndexBuilder *self = DZL_FUZZY_INDEX_BUILDER (object);
157
158 switch (prop_id)
159 {
160 case PROP_CASE_SENSITIVE:
161 g_value_set_boolean (value, dzl_fuzzy_index_builder_get_case_sensitive (self));
162 break;
163
164 default:
165 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
166 }
167 }
168
169 static void
dzl_fuzzy_index_builder_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)170 dzl_fuzzy_index_builder_set_property (GObject *object,
171 guint prop_id,
172 const GValue *value,
173 GParamSpec *pspec)
174 {
175 DzlFuzzyIndexBuilder *self = DZL_FUZZY_INDEX_BUILDER (object);
176
177 switch (prop_id)
178 {
179 case PROP_CASE_SENSITIVE:
180 dzl_fuzzy_index_builder_set_case_sensitive (self, g_value_get_boolean (value));
181 break;
182
183 default:
184 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
185 }
186 }
187
188 static void
dzl_fuzzy_index_builder_class_init(DzlFuzzyIndexBuilderClass * klass)189 dzl_fuzzy_index_builder_class_init (DzlFuzzyIndexBuilderClass *klass)
190 {
191 GObjectClass *object_class = G_OBJECT_CLASS (klass);
192
193 object_class->finalize = dzl_fuzzy_index_builder_finalize;
194 object_class->get_property = dzl_fuzzy_index_builder_get_property;
195 object_class->set_property = dzl_fuzzy_index_builder_set_property;
196
197 properties [PROP_CASE_SENSITIVE] =
198 g_param_spec_boolean ("case-sensitive",
199 "Case Sensitive",
200 "Case Sensitive",
201 FALSE,
202 (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
203
204 g_object_class_install_properties (object_class, N_PROPS, properties);
205 }
206
207 static void
dzl_fuzzy_index_builder_init(DzlFuzzyIndexBuilder * self)208 dzl_fuzzy_index_builder_init (DzlFuzzyIndexBuilder *self)
209 {
210 self->documents = g_ptr_array_new_with_free_func ((GDestroyNotify)g_variant_unref);
211 self->documents_hash = g_hash_table_new (dzl_g_variant_hash, g_variant_equal);
212 self->kv_pairs = g_array_new (FALSE, FALSE, sizeof (KVPair));
213 self->strings = g_string_chunk_new (4096);
214 self->key_ids = g_hash_table_new (NULL, NULL);
215 self->keys = g_ptr_array_new ();
216 }
217
218 DzlFuzzyIndexBuilder *
dzl_fuzzy_index_builder_new(void)219 dzl_fuzzy_index_builder_new (void)
220 {
221 return g_object_new (DZL_TYPE_FUZZY_INDEX_BUILDER, NULL);
222 }
223
224 /**
225 * dzl_fuzzy_index_builder_insert:
226 * @self: A #DzlFuzzyIndexBuilder
227 * @key: The UTF-8 encoded key for the document
228 * @document: The document to store
229 * @priority: An optional priority for the keyword.
230 *
231 * Inserts @document into the index using @key as the lookup key.
232 *
233 * If a matching document (checked by hashing @document) has already
234 * been inserted, only a single instance of the document will be stored.
235 *
236 * If @document is floating, it will be consumed.
237 *
238 * @priority may be used to group results by priority. Priority must be
239 * less than 256.
240 *
241 * Returns: The document id registered for @document.
242 */
243 guint64
dzl_fuzzy_index_builder_insert(DzlFuzzyIndexBuilder * self,const gchar * key,GVariant * document,guint priority)244 dzl_fuzzy_index_builder_insert (DzlFuzzyIndexBuilder *self,
245 const gchar *key,
246 GVariant *document,
247 guint priority)
248 {
249 g_autoptr(GVariant) sunk_variant = NULL;
250 GVariant *real_document = NULL;
251 gpointer document_id = NULL;
252 gpointer key_id = NULL;
253 KVPair pair;
254
255 g_return_val_if_fail (DZL_IS_FUZZY_INDEX_BUILDER (self), 0L);
256 g_return_val_if_fail (key != NULL, 0L);
257 g_return_val_if_fail (document != NULL, 0L);
258 g_return_val_if_fail (priority <= 0xFF, 0L);
259
260 if (g_variant_is_floating (document))
261 sunk_variant = g_variant_ref_sink (document);
262
263 /* move the priority bits into the proper area */
264 priority = (priority & 0xFF) << 24;
265
266 if (self->keys->len > MAX_KEY_ENTRIES)
267 {
268 g_warning ("Index is full, cannot add more entries");
269 return 0L;
270 }
271
272 key = g_string_chunk_insert_const (self->strings, key);
273
274 /*
275 * We try to deduplicate document entries here by hashing the document and
276 * looking for another matching it. This way our generated index can stay
277 * relatively small when it comes to documents.
278 */
279 if (!g_hash_table_lookup_extended (self->documents_hash,
280 document,
281 (gpointer *)&real_document,
282 &document_id))
283 {
284 document_id = GUINT_TO_POINTER (self->documents->len);
285 real_document = g_variant_ref (document);
286 g_ptr_array_add (self->documents, real_document);
287 g_hash_table_insert (self->documents_hash, real_document, document_id);
288 }
289
290 /*
291 * If we already have the key then reuse its key index. If not, then add it.
292 */
293 if (!g_hash_table_lookup_extended (self->key_ids, key, NULL, &key_id))
294 {
295 key_id = GUINT_TO_POINTER (self->keys->len);
296 g_ptr_array_add (self->keys, (gchar *)key);
297 g_hash_table_insert (self->key_ids, (gpointer)key, key_id);
298 }
299
300 /*
301 * A bit of slight-of-hand here. We share keys between all key<->document
302 * pairs, but steal the high bits for the key priority in the kvpair entry.
303 * This allows for both deduplication and different priorities based on
304 * certain document pairs.
305 */
306 pair.key_id = GPOINTER_TO_UINT (key_id) | priority;
307 pair.document_id = GPOINTER_TO_UINT (document_id);
308
309 g_array_append_val (self->kv_pairs, pair);
310
311 return pair.document_id;
312 }
313
314 static gint
pos_doc_pair_compare(gconstpointer a,gconstpointer b)315 pos_doc_pair_compare (gconstpointer a,
316 gconstpointer b)
317 {
318 const IndexItem *paira = a;
319 const IndexItem *pairb = b;
320 gint ret;
321
322 ret = paira->lookaside_id - pairb->lookaside_id;
323
324 if (ret == 0)
325 ret = paira->position - pairb->position;
326
327 return ret;
328 }
329
330 static GVariant *
dzl_fuzzy_index_builder_build_keys(DzlFuzzyIndexBuilder * self)331 dzl_fuzzy_index_builder_build_keys (DzlFuzzyIndexBuilder *self)
332 {
333 g_assert (DZL_IS_FUZZY_INDEX_BUILDER (self));
334
335 return g_variant_new_strv ((const gchar * const *)self->keys->pdata,
336 self->keys->len);
337 }
338
339 static GVariant *
dzl_fuzzy_index_builder_build_lookaside(DzlFuzzyIndexBuilder * self)340 dzl_fuzzy_index_builder_build_lookaside (DzlFuzzyIndexBuilder *self)
341 {
342 g_assert (DZL_IS_FUZZY_INDEX_BUILDER (self));
343
344 return g_variant_new_fixed_array ((const GVariantType *)"(uu)",
345 self->kv_pairs->data,
346 self->kv_pairs->len,
347 sizeof (KVPair));
348 }
349
350 static GVariant *
dzl_fuzzy_index_builder_build_index(DzlFuzzyIndexBuilder * self)351 dzl_fuzzy_index_builder_build_index (DzlFuzzyIndexBuilder *self)
352 {
353 g_autoptr(GHashTable) rows = NULL;
354 GVariantDict dict;
355 GHashTableIter iter;
356 gpointer keyptr;
357 GArray *row;
358 guint i;
359
360 g_assert (DZL_IS_FUZZY_INDEX_BUILDER (self));
361
362 rows = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_array_unref);
363
364 for (i = 0; i < self->kv_pairs->len; i++)
365 {
366 g_autofree gchar *lower = NULL;
367 const gchar *key;
368 const gchar *tmp;
369 KVPair *kvpair;
370 IndexItem item;
371 guint position = 0;
372
373 kvpair = &g_array_index (self->kv_pairs, KVPair, i);
374 key = g_ptr_array_index (self->keys, mask_priority (kvpair->key_id));
375
376 /* the priority for the key is stashed in the high 8 bits of
377 * the kvpair.key_id. So we need to propagate that to the
378 * entry in the index for resolution later.
379 */
380 item.lookaside_id = i | (kvpair->key_id & 0xFF000000);
381
382 if (!self->case_sensitive)
383 key = lower = g_utf8_casefold (key, -1);
384
385 for (tmp = key; *tmp; tmp = g_utf8_next_char (tmp))
386 {
387 gunichar ch = g_utf8_get_char (tmp);
388
389 row = g_hash_table_lookup (rows, GUINT_TO_POINTER (ch));
390
391 if G_UNLIKELY (row == NULL)
392 {
393 row = g_array_new (FALSE, FALSE, sizeof (IndexItem));
394 g_hash_table_insert (rows, GUINT_TO_POINTER (ch), row);
395 }
396
397 item.position = position++;
398 g_array_append_val (row, item);
399 }
400 }
401
402 g_variant_dict_init (&dict, NULL);
403
404 g_hash_table_iter_init (&iter, rows);
405
406 while (g_hash_table_iter_next (&iter, &keyptr, (gpointer *)&row))
407 {
408 gchar key[12];
409 GVariant *variant;
410 gunichar ch = GPOINTER_TO_UINT (keyptr);
411
412 key [g_unichar_to_utf8 (ch, key)] = 0;
413
414 g_array_sort (row, pos_doc_pair_compare);
415
416 variant = g_variant_new_fixed_array ((const GVariantType *)"(uu)",
417 row->data,
418 row->len,
419 sizeof (IndexItem));
420 g_variant_dict_insert_value (&dict, key, variant);
421 }
422
423 return g_variant_dict_end (&dict);
424 }
425
426 static GVariant *
dzl_fuzzy_index_builder_build_metadata(DzlFuzzyIndexBuilder * self)427 dzl_fuzzy_index_builder_build_metadata (DzlFuzzyIndexBuilder *self)
428 {
429 GVariantDict dict;
430 GHashTableIter iter;
431
432 g_assert (DZL_IS_FUZZY_INDEX_BUILDER (self));
433
434 g_variant_dict_init (&dict, NULL);
435
436 if (self->metadata != NULL)
437 {
438 const gchar *key;
439 GVariant *value;
440
441 g_hash_table_iter_init (&iter, self->metadata);
442 while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
443 g_variant_dict_insert_value (&dict, key, value);
444 }
445
446 g_variant_dict_insert (&dict, "case-sensitive", "b", self->case_sensitive);
447
448 return g_variant_dict_end (&dict);
449 }
450
451 static void
dzl_fuzzy_index_builder_write_worker(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)452 dzl_fuzzy_index_builder_write_worker (GTask *task,
453 gpointer source_object,
454 gpointer task_data,
455 GCancellable *cancellable)
456 {
457 DzlFuzzyIndexBuilder *self = source_object;
458 g_autoptr(GVariant) variant = NULL;
459 g_autoptr(GVariant) documents = NULL;
460 g_autoptr(GError) error = NULL;
461 GVariantDict dict;
462 GFile *file = task_data;
463
464 g_assert (G_IS_TASK (task));
465 g_assert (DZL_IS_FUZZY_INDEX_BUILDER (self));
466 g_assert (G_IS_FILE (file));
467 g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
468
469 g_variant_dict_init (&dict, NULL);
470
471 /* Set our version number for the document */
472 g_variant_dict_insert (&dict, "version", "i", 1);
473
474 /* Build our dicitionary of metadata */
475 g_variant_dict_insert_value (&dict,
476 "metadata",
477 dzl_fuzzy_index_builder_build_metadata (self));
478
479 /* Keys is an array of string keys where the index is the "key_id" */
480 g_variant_dict_insert_value (&dict,
481 "keys",
482 dzl_fuzzy_index_builder_build_keys (self));
483
484 /* The lookaside is a mapping of kvpair to the repsective keys and
485 * documents. This allows the tables to use the kvpair id as the value
486 * in the index so we can have both document deduplication as well as
487 * the ability to disambiguate the keys which point to the same
488 * document. The contents are "a{uu}".
489 */
490 g_variant_dict_insert_value (&dict,
491 "lookaside",
492 dzl_fuzzy_index_builder_build_lookaside (self));
493
494 /* Build our dicitionary of character → [(pos,lookaside_id),..] tuples.
495 * The position is the utf8 character position within the string.
496 * The lookaside_id is the index within the lookaside buffer to locate
497 * the document_id or key_id.
498 */
499 g_variant_dict_insert_value (&dict,
500 "tables",
501 dzl_fuzzy_index_builder_build_index (self));
502
503 /*
504 * The documents are stored as an array where the document identifier is
505 * their index position. We then use a lookaside buffer to map the insertion
506 * id to the document id. Otherwise, we can't disambiguate between two
507 * keys that insert the same document (as we deduplicate documents inserted
508 * into the index).
509 */
510 documents = g_variant_new_array (NULL,
511 (GVariant * const *)self->documents->pdata,
512 self->documents->len);
513 g_variant_dict_insert_value (&dict, "documents", g_variant_ref_sink (documents));
514
515 /* Now write the variant to disk */
516 variant = g_variant_ref_sink (g_variant_dict_end (&dict));
517 if (!g_file_replace_contents (file,
518 g_variant_get_data (variant),
519 g_variant_get_size (variant),
520 NULL,
521 FALSE,
522 G_FILE_CREATE_NONE,
523 NULL,
524 cancellable,
525 &error))
526 g_task_return_error (task, g_steal_pointer (&error));
527 else
528 g_task_return_boolean (task, TRUE);
529 }
530
531 /**
532 * dzl_fuzzy_index_builder_write_async:
533 * @self: A #DzlFuzzyIndexBuilder
534 * @file: A #GFile to write the index to
535 * @io_priority: The priority for IO operations
536 * @cancellable: (nullable): An optional #GCancellable or %NULL
537 * @callback: A callback for completion or %NULL
538 * @user_data: User data for @callback
539 *
540 * Builds and writes the index to @file. The file format is a
541 * GVariant on disk and can be loaded and searched using
542 * #FuzzyIndex.
543 */
544 void
dzl_fuzzy_index_builder_write_async(DzlFuzzyIndexBuilder * self,GFile * file,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)545 dzl_fuzzy_index_builder_write_async (DzlFuzzyIndexBuilder *self,
546 GFile *file,
547 gint io_priority,
548 GCancellable *cancellable,
549 GAsyncReadyCallback callback,
550 gpointer user_data)
551 {
552 g_autoptr(GTask) task = NULL;
553
554 g_return_if_fail (DZL_IS_FUZZY_INDEX_BUILDER (self));
555 g_return_if_fail (G_IS_FILE (file));
556 g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
557
558 task = g_task_new (self, cancellable, callback, user_data);
559 g_task_set_source_tag (task, dzl_fuzzy_index_builder_write_async);
560 g_task_set_priority (task, io_priority);
561 g_task_set_task_data (task, g_object_ref (file), g_object_unref);
562 g_task_run_in_thread (task, dzl_fuzzy_index_builder_write_worker);
563 }
564
565 gboolean
dzl_fuzzy_index_builder_write_finish(DzlFuzzyIndexBuilder * self,GAsyncResult * result,GError ** error)566 dzl_fuzzy_index_builder_write_finish (DzlFuzzyIndexBuilder *self,
567 GAsyncResult *result,
568 GError **error)
569 {
570 g_return_val_if_fail (DZL_IS_FUZZY_INDEX_BUILDER (self), FALSE);
571 g_return_val_if_fail (G_IS_TASK (result), FALSE);
572
573 return g_task_propagate_boolean (G_TASK (result), error);
574 }
575
576 gboolean
dzl_fuzzy_index_builder_write(DzlFuzzyIndexBuilder * self,GFile * file,gint io_priority,GCancellable * cancellable,GError ** error)577 dzl_fuzzy_index_builder_write (DzlFuzzyIndexBuilder *self,
578 GFile *file,
579 gint io_priority,
580 GCancellable *cancellable,
581 GError **error)
582 {
583 g_autoptr(GTask) task = NULL;
584
585 g_return_val_if_fail (DZL_IS_FUZZY_INDEX_BUILDER (self), FALSE);
586 g_return_val_if_fail (G_IS_FILE (file), FALSE);
587 g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
588
589 task = g_task_new (self, cancellable, NULL, NULL);
590 g_task_set_source_tag (task, dzl_fuzzy_index_builder_write);
591 g_task_set_priority (task, io_priority);
592 g_task_set_task_data (task, g_object_ref (file), g_object_unref);
593
594 dzl_fuzzy_index_builder_write_worker (task, self, file, cancellable);
595
596 return g_task_propagate_boolean (task, error);
597 }
598
599 /**
600 * dzl_fuzzy_index_builder_get_document:
601 *
602 * Returns the document that was inserted in a previous call to
603 * dzl_fuzzy_index_builder_insert().
604 *
605 * Returns: (transfer none): A #GVariant
606 */
607 const GVariant *
dzl_fuzzy_index_builder_get_document(DzlFuzzyIndexBuilder * self,guint64 document_id)608 dzl_fuzzy_index_builder_get_document (DzlFuzzyIndexBuilder *self,
609 guint64 document_id)
610 {
611 g_return_val_if_fail (DZL_IS_FUZZY_INDEX_BUILDER (self), NULL);
612 g_return_val_if_fail ((guint)document_id < self->documents->len, NULL);
613
614 return g_ptr_array_index (self->documents, (guint)document_id);
615 }
616
617 void
dzl_fuzzy_index_builder_set_metadata(DzlFuzzyIndexBuilder * self,const gchar * key,GVariant * value)618 dzl_fuzzy_index_builder_set_metadata (DzlFuzzyIndexBuilder *self,
619 const gchar *key,
620 GVariant *value)
621 {
622 g_return_if_fail (DZL_IS_FUZZY_INDEX_BUILDER (self));
623 g_return_if_fail (key != NULL);
624
625 if (self->metadata == NULL)
626 self->metadata = g_hash_table_new_full (g_str_hash,
627 g_str_equal,
628 g_free,
629 (GDestroyNotify)g_variant_unref);
630
631 if (value != NULL)
632 g_hash_table_insert (self->metadata,
633 g_strdup (key),
634 g_variant_ref_sink (value));
635 else
636 g_hash_table_remove (self->metadata, key);
637 }
638
639 void
dzl_fuzzy_index_builder_set_metadata_string(DzlFuzzyIndexBuilder * self,const gchar * key,const gchar * value)640 dzl_fuzzy_index_builder_set_metadata_string (DzlFuzzyIndexBuilder *self,
641 const gchar *key,
642 const gchar *value)
643 {
644 g_return_if_fail (DZL_IS_FUZZY_INDEX_BUILDER (self));
645 g_return_if_fail (key != NULL);
646 g_return_if_fail (value != NULL);
647
648 dzl_fuzzy_index_builder_set_metadata (self, key, g_variant_new_string (value));
649 }
650
651 void
dzl_fuzzy_index_builder_set_metadata_uint32(DzlFuzzyIndexBuilder * self,const gchar * key,guint32 value)652 dzl_fuzzy_index_builder_set_metadata_uint32 (DzlFuzzyIndexBuilder *self,
653 const gchar *key,
654 guint32 value)
655 {
656 g_return_if_fail (DZL_IS_FUZZY_INDEX_BUILDER (self));
657 g_return_if_fail (key != NULL);
658
659 dzl_fuzzy_index_builder_set_metadata (self, key, g_variant_new_uint32 (value));
660 }
661
662 void
dzl_fuzzy_index_builder_set_metadata_uint64(DzlFuzzyIndexBuilder * self,const gchar * key,guint64 value)663 dzl_fuzzy_index_builder_set_metadata_uint64 (DzlFuzzyIndexBuilder *self,
664 const gchar *key,
665 guint64 value)
666 {
667 g_return_if_fail (DZL_IS_FUZZY_INDEX_BUILDER (self));
668 g_return_if_fail (key != NULL);
669
670 dzl_fuzzy_index_builder_set_metadata (self, key, g_variant_new_uint64 (value));
671 }
672
673 gboolean
dzl_fuzzy_index_builder_get_case_sensitive(DzlFuzzyIndexBuilder * self)674 dzl_fuzzy_index_builder_get_case_sensitive (DzlFuzzyIndexBuilder *self)
675 {
676 g_return_val_if_fail (DZL_IS_FUZZY_INDEX_BUILDER (self), FALSE);
677
678 return self->case_sensitive;
679 }
680
681 void
dzl_fuzzy_index_builder_set_case_sensitive(DzlFuzzyIndexBuilder * self,gboolean case_sensitive)682 dzl_fuzzy_index_builder_set_case_sensitive (DzlFuzzyIndexBuilder *self,
683 gboolean case_sensitive)
684 {
685 g_return_if_fail (DZL_IS_FUZZY_INDEX_BUILDER (self));
686
687 case_sensitive = !!case_sensitive;
688
689 if (self->case_sensitive != case_sensitive)
690 {
691 self->case_sensitive = case_sensitive;
692 g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CASE_SENSITIVE]);
693 }
694 }
695