1
/* Copyright (c) 2006-2008 Dovecot authors, see the included COPYING file */
6
#include "seq-range-array.h"
7
#include "charset-utf8.h"
8
#include "mail-search.h"
9
#include "mail-storage-private.h"
10
#include "fts-api-private.h"
11
#include "fts-storage.h"
14
uid_range_to_seqs(struct mailbox *box, const ARRAY_TYPE(seq_range) *uid_range,
15
ARRAY_TYPE(seq_range) *seq_range)
17
const struct seq_range *range;
18
unsigned int i, count;
21
range = array_get(uid_range, &count);
22
i_array_init(seq_range, count);
23
for (i = 0; i < count; i++) {
24
mailbox_get_uids(box, range[i].seq1, range[i].seq2,
27
seq_range_array_add_range(seq_range, seq1, seq2);
31
static void fts_uid_results_to_seq(struct fts_search_context *fctx)
33
ARRAY_TYPE(seq_range) uid_range;
35
uid_range = fctx->definite_seqs;
36
uid_range_to_seqs(fctx->t->box, &uid_range, &fctx->definite_seqs);
37
array_free(&uid_range);
39
uid_range = fctx->maybe_seqs;
40
uid_range_to_seqs(fctx->t->box, &uid_range, &fctx->maybe_seqs);
41
array_free(&uid_range);
44
static int fts_search_lookup_arg(struct fts_search_context *fctx,
45
struct mail_search_arg *arg)
47
struct fts_backend *backend;
48
struct fts_backend_lookup_context **lookup_ctx_p;
49
enum fts_lookup_flags flags = 0;
52
enum charset_result result;
57
/* we can filter out messages that don't have the header,
58
but we can't trust definite results list. */
59
flags = FTS_LOOKUP_FLAG_HEADER;
60
backend = fctx->fbox->backend_substr;
63
/* we're only checking the existence
65
key = arg->hdr_field_name;
69
case SEARCH_TEXT_FAST:
70
flags = FTS_LOOKUP_FLAG_HEADER;
72
case SEARCH_BODY_FAST:
73
flags |= FTS_LOOKUP_FLAG_BODY;
75
backend = fctx->fbox->backend_fast != NULL &&
76
(arg->type == SEARCH_TEXT_FAST ||
77
arg->type == SEARCH_BODY_FAST) ?
78
fctx->fbox->backend_fast : fctx->fbox->backend_substr;
81
/* can't filter this */
85
flags |= FTS_LOOKUP_FLAG_INVERT;
87
/* convert key to titlecase */
88
key_utf8 = t_str_new(128);
89
if (charset_to_utf8_str(fctx->charset, CHARSET_FLAG_DECOMP_TITLECASE,
90
key, key_utf8, &result) < 0) {
91
/* unknown charset, can't handle this */
93
} else if (result != CHARSET_RET_OK) {
94
/* let the core code handle this error */
96
} else if (!backend->locked && fts_backend_lock(backend) <= 0)
100
if (backend == fctx->fbox->backend_substr)
101
lookup_ctx_p = &fctx->lookup_ctx_substr;
103
lookup_ctx_p = &fctx->lookup_ctx_fast;
105
if (*lookup_ctx_p == NULL)
106
*lookup_ctx_p = fts_backend_lookup_init(backend);
107
fts_backend_lookup_add(*lookup_ctx_p, str_c(key_utf8), flags);
112
void fts_search_lookup(struct fts_search_context *fctx)
114
struct mail_search_arg *arg;
118
if (fctx->best_arg == NULL)
121
i_array_init(&fctx->definite_seqs, 64);
122
i_array_init(&fctx->maybe_seqs, 64);
123
i_array_init(&fctx->score_map, 64);
125
/* start lookup with the best arg */
127
ret = fts_search_lookup_arg(fctx, fctx->best_arg);
129
/* filter the rest */
130
for (arg = fctx->args; arg != NULL && ret == 0; arg = arg->next) {
131
if (arg != fctx->best_arg) {
133
ret = fts_search_lookup_arg(fctx, arg);
139
if (fctx->fbox->backend_fast != NULL) {
140
if (fctx->lookup_ctx_fast != NULL) {
142
fts_backend_lookup_deinit(&fctx->lookup_ctx_fast,
143
&fctx->definite_seqs,
147
if (fctx->fbox->backend_fast->locked)
148
fts_backend_unlock(fctx->fbox->backend_fast);
150
if (fctx->fbox->backend_substr != NULL) {
151
if (fctx->lookup_ctx_substr == NULL) {
152
/* no substr lookups */
153
} else if (!have_seqs) {
154
fts_backend_lookup_deinit(&fctx->lookup_ctx_substr,
155
&fctx->definite_seqs,
159
/* have to merge the results */
160
ARRAY_TYPE(seq_range) tmp_def, tmp_maybe;
161
ARRAY_TYPE(fts_score_map) tmp_scores;
163
i_array_init(&tmp_def, 64);
164
i_array_init(&tmp_maybe, 64);
165
i_array_init(&tmp_scores, 64);
166
/* FIXME: for now we just ignore the other scores,
167
since squat doesn't support it anyway */
168
fts_backend_lookup_deinit(&fctx->lookup_ctx_substr,
169
&tmp_def, &tmp_maybe,
171
fts_filter_uids(&fctx->definite_seqs, &tmp_def,
172
&fctx->maybe_seqs, &tmp_maybe);
173
array_free(&tmp_def);
174
array_free(&tmp_maybe);
175
array_free(&tmp_scores);
177
if (fctx->fbox->backend_substr->locked)
178
fts_backend_unlock(fctx->fbox->backend_substr);
182
fctx->seqs_set = TRUE;
183
fts_uid_results_to_seq(fctx);
187
static bool arg_is_better(const struct mail_search_arg *new_arg,
188
const struct mail_search_arg *old_arg)
196
if (old_arg->not && !new_arg->not)
198
if (!old_arg->not && new_arg->not)
201
/* prefer not to use headers. they have a larger possibility of
202
having lots of identical strings */
203
if (old_arg->type == SEARCH_HEADER)
205
else if (new_arg->type == SEARCH_HEADER)
208
return strlen(new_arg->value.str) > strlen(old_arg->value.str);
212
fts_search_args_find_best(struct mail_search_arg *args,
213
struct mail_search_arg **best_fast_arg,
214
struct mail_search_arg **best_substr_arg)
216
for (; args != NULL; args = args->next) {
217
switch (args->type) {
218
case SEARCH_BODY_FAST:
219
case SEARCH_TEXT_FAST:
220
if (arg_is_better(args, *best_fast_arg))
221
*best_fast_arg = args;
226
if (arg_is_better(args, *best_substr_arg))
227
*best_substr_arg = args;
235
void fts_search_analyze(struct fts_search_context *fctx)
237
struct mail_search_arg *best_fast_arg = NULL, *best_substr_arg = NULL;
239
fts_search_args_find_best(fctx->args, &best_fast_arg, &best_substr_arg);
241
if (best_fast_arg != NULL && fctx->fbox->backend_fast != NULL) {
242
/* use fast backend whenever possible */
243
fctx->best_arg = best_fast_arg;
244
fctx->build_backend = fctx->fbox->backend_fast;
245
} else if (best_fast_arg != NULL || best_substr_arg != NULL) {
246
fctx->build_backend = fctx->fbox->backend_substr;
247
fctx->best_arg = arg_is_better(best_substr_arg, best_fast_arg) ?
248
best_substr_arg : best_fast_arg;