~ubuntu-branches/ubuntu/wily/dovecot/wily-proposed

« back to all changes in this revision

Viewing changes to src/plugins/fts/fts-search.c

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2008-08-02 14:00:15 UTC
  • mto: (1.11.1 upstream) (4.2.1 sid)
  • mto: This revision was merged to the branch mainline in revision 39.
  • Revision ID: james.westby@ubuntu.com-20080802140015-zbmjsgoodeyc9z4s
Tags: upstream-1.1.2
ImportĀ upstreamĀ versionĀ 1.1.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2006-2008 Dovecot authors, see the included COPYING file */
 
2
 
 
3
#include "lib.h"
 
4
#include "array.h"
 
5
#include "str.h"
 
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"
 
12
 
 
13
static void
 
14
uid_range_to_seqs(struct mailbox *box, const ARRAY_TYPE(seq_range) *uid_range,
 
15
                  ARRAY_TYPE(seq_range) *seq_range)
 
16
{
 
17
        const struct seq_range *range;
 
18
        unsigned int i, count;
 
19
        uint32_t seq1, seq2;
 
20
 
 
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,
 
25
                                 &seq1, &seq2);
 
26
                if (seq1 != 0)
 
27
                        seq_range_array_add_range(seq_range, seq1, seq2);
 
28
        }
 
29
}
 
30
 
 
31
static void fts_uid_results_to_seq(struct fts_search_context *fctx)
 
32
{
 
33
        ARRAY_TYPE(seq_range) uid_range;
 
34
 
 
35
        uid_range = fctx->definite_seqs;
 
36
        uid_range_to_seqs(fctx->t->box, &uid_range, &fctx->definite_seqs);
 
37
        array_free(&uid_range);
 
38
 
 
39
        uid_range = fctx->maybe_seqs;
 
40
        uid_range_to_seqs(fctx->t->box, &uid_range, &fctx->maybe_seqs);
 
41
        array_free(&uid_range);
 
42
}
 
43
 
 
44
static int fts_search_lookup_arg(struct fts_search_context *fctx,
 
45
                                 struct mail_search_arg *arg)
 
46
{
 
47
        struct fts_backend *backend;
 
48
        struct fts_backend_lookup_context **lookup_ctx_p;
 
49
        enum fts_lookup_flags flags = 0;
 
50
        const char *key;
 
51
        string_t *key_utf8;
 
52
        enum charset_result result;
 
53
        int ret;
 
54
 
 
55
        switch (arg->type) {
 
56
        case SEARCH_HEADER:
 
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;
 
61
                key = arg->value.str;
 
62
                if (*key == '\0') {
 
63
                        /* we're only checking the existence
 
64
                           of the header. */
 
65
                        key = arg->hdr_field_name;
 
66
                }
 
67
                break;
 
68
        case SEARCH_TEXT:
 
69
        case SEARCH_TEXT_FAST:
 
70
                flags = FTS_LOOKUP_FLAG_HEADER;
 
71
        case SEARCH_BODY:
 
72
        case SEARCH_BODY_FAST:
 
73
                flags |= FTS_LOOKUP_FLAG_BODY;
 
74
                key = arg->value.str;
 
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;
 
79
                break;
 
80
        default:
 
81
                /* can't filter this */
 
82
                return 0;
 
83
        }
 
84
        if (arg->not)
 
85
                flags |= FTS_LOOKUP_FLAG_INVERT;
 
86
 
 
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 */
 
92
                ret = 0;
 
93
        } else if (result != CHARSET_RET_OK) {
 
94
                /* let the core code handle this error */
 
95
                ret = 0;
 
96
        } else if (!backend->locked && fts_backend_lock(backend) <= 0)
 
97
                ret = -1;
 
98
        else {
 
99
                ret = 0;
 
100
                if (backend == fctx->fbox->backend_substr)
 
101
                        lookup_ctx_p = &fctx->lookup_ctx_substr;
 
102
                else
 
103
                        lookup_ctx_p = &fctx->lookup_ctx_fast;
 
104
 
 
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);
 
108
        }
 
109
        return ret;
 
110
}
 
111
 
 
112
void fts_search_lookup(struct fts_search_context *fctx)
 
113
{
 
114
        struct mail_search_arg *arg;
 
115
        bool have_seqs;
 
116
        int ret;
 
117
 
 
118
        if (fctx->best_arg == NULL)
 
119
                return;
 
120
 
 
121
        i_array_init(&fctx->definite_seqs, 64);
 
122
        i_array_init(&fctx->maybe_seqs, 64);
 
123
        i_array_init(&fctx->score_map, 64);
 
124
 
 
125
        /* start lookup with the best arg */
 
126
        T_BEGIN {
 
127
                ret = fts_search_lookup_arg(fctx, fctx->best_arg);
 
128
        } T_END;
 
129
        /* filter the rest */
 
130
        for (arg = fctx->args; arg != NULL && ret == 0; arg = arg->next) {
 
131
                if (arg != fctx->best_arg) {
 
132
                        T_BEGIN {
 
133
                                ret = fts_search_lookup_arg(fctx, arg);
 
134
                        } T_END;
 
135
                }
 
136
        }
 
137
 
 
138
        have_seqs = FALSE;
 
139
        if (fctx->fbox->backend_fast != NULL) {
 
140
                if (fctx->lookup_ctx_fast != NULL) {
 
141
                        have_seqs = TRUE;
 
142
                        fts_backend_lookup_deinit(&fctx->lookup_ctx_fast,
 
143
                                                  &fctx->definite_seqs,
 
144
                                                  &fctx->maybe_seqs,
 
145
                                                  &fctx->score_map);
 
146
                }
 
147
                if (fctx->fbox->backend_fast->locked)
 
148
                        fts_backend_unlock(fctx->fbox->backend_fast);
 
149
        }
 
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,
 
156
                                                  &fctx->maybe_seqs,
 
157
                                                  &fctx->score_map);
 
158
                } else {
 
159
                        /* have to merge the results */
 
160
                        ARRAY_TYPE(seq_range) tmp_def, tmp_maybe;
 
161
                        ARRAY_TYPE(fts_score_map) tmp_scores;
 
162
 
 
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,
 
170
                                                  &tmp_scores);
 
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);
 
176
                }
 
177
                if (fctx->fbox->backend_substr->locked)
 
178
                        fts_backend_unlock(fctx->fbox->backend_substr);
 
179
        }
 
180
 
 
181
        if (ret == 0) {
 
182
                fctx->seqs_set = TRUE;
 
183
                fts_uid_results_to_seq(fctx);
 
184
        }
 
185
}
 
186
 
 
187
static bool arg_is_better(const struct mail_search_arg *new_arg,
 
188
                          const struct mail_search_arg *old_arg)
 
189
{
 
190
        if (old_arg == NULL)
 
191
                return TRUE;
 
192
        if (new_arg == NULL)
 
193
                return FALSE;
 
194
 
 
195
        /* avoid NOTs */
 
196
        if (old_arg->not && !new_arg->not)
 
197
                return TRUE;
 
198
        if (!old_arg->not && new_arg->not)
 
199
                return FALSE;
 
200
 
 
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)
 
204
                return TRUE;
 
205
        else if (new_arg->type == SEARCH_HEADER)
 
206
                return FALSE;
 
207
 
 
208
        return strlen(new_arg->value.str) > strlen(old_arg->value.str);
 
209
}
 
210
 
 
211
static void
 
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)
 
215
{
 
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;
 
222
                        break;
 
223
                case SEARCH_BODY:
 
224
                case SEARCH_TEXT:
 
225
                case SEARCH_HEADER:
 
226
                        if (arg_is_better(args, *best_substr_arg))
 
227
                                *best_substr_arg = args;
 
228
                        break;
 
229
                default:
 
230
                        break;
 
231
                }
 
232
        }
 
233
}
 
234
 
 
235
void fts_search_analyze(struct fts_search_context *fctx)
 
236
{
 
237
        struct mail_search_arg *best_fast_arg = NULL, *best_substr_arg = NULL;
 
238
 
 
239
        fts_search_args_find_best(fctx->args, &best_fast_arg, &best_substr_arg);
 
240
 
 
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;
 
249
        }
 
250
}