Mercurial > dovecot > original-hg > dovecot-1.2
comparison src/plugins/fts-solr/fts-backend-solr.c @ 8499:252b29ac5f43 HEAD
fts: Added support for handling multiple namespaces.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 30 Nov 2008 01:27:19 +0200 |
parents | ffb37c392166 |
children | 3efcdc45d111 |
comparison
equal
deleted
inserted
replaced
8498:fb5fedcf4deb | 8499:252b29ac5f43 |
---|---|
11 | 11 |
12 #include <ctype.h> | 12 #include <ctype.h> |
13 | 13 |
14 #define SOLR_CMDBUF_SIZE (1024*64) | 14 #define SOLR_CMDBUF_SIZE (1024*64) |
15 #define SOLR_MAX_ROWS 100000 | 15 #define SOLR_MAX_ROWS 100000 |
16 #define FTS_SOLR_MAX_BOX_INC_PATTERNS 5 | |
17 #define FTS_SOLR_MAX_BOX_EXC_PATTERNS 5 | |
16 | 18 |
17 struct solr_fts_backend { | 19 struct solr_fts_backend { |
18 struct fts_backend backend; | 20 struct fts_backend backend; |
19 char *id_username; | 21 char *id_username, *id_namespace; |
22 struct mail_namespace *default_ns; | |
20 }; | 23 }; |
21 | 24 |
22 struct solr_fts_backend_build_context { | 25 struct solr_fts_backend_build_context { |
23 struct fts_backend_build_context ctx; | 26 struct fts_backend_build_context ctx; |
24 | 27 |
26 uint32_t prev_uid, uid_validity; | 29 uint32_t prev_uid, uid_validity; |
27 string_t *cmd; | 30 string_t *cmd; |
28 bool headers; | 31 bool headers; |
29 }; | 32 }; |
30 | 33 |
34 struct solr_virtual_uid_map_context { | |
35 struct fts_backend *backend; | |
36 struct mailbox *box; | |
37 string_t *vname; | |
38 }; | |
39 | |
31 struct fts_backend_solr_get_last_uids_context { | 40 struct fts_backend_solr_get_last_uids_context { |
41 struct fts_backend *backend; | |
32 pool_t pool; | 42 pool_t pool; |
33 ARRAY_TYPE(fts_backend_uid_map) *last_uids; | 43 ARRAY_TYPE(fts_backend_uid_map) *last_uids; |
44 | |
45 struct mailbox *box; | |
46 string_t *vname; | |
34 }; | 47 }; |
35 | 48 |
36 static struct solr_connection *solr_conn = NULL; | 49 static struct solr_connection *solr_conn = NULL; |
37 | |
38 static void solr_quote_str(string_t *dest, const char *str) | |
39 { | |
40 solr_connection_quote_str(solr_conn, dest, str); | |
41 } | |
42 | 50 |
43 static void | 51 static void |
44 xml_encode_data(string_t *dest, const unsigned char *data, unsigned int len) | 52 xml_encode_data(string_t *dest, const unsigned char *data, unsigned int len) |
45 { | 53 { |
46 unsigned int i; | 54 unsigned int i; |
95 } | 103 } |
96 } | 104 } |
97 return str_c(tmp); | 105 return str_c(tmp); |
98 } | 106 } |
99 | 107 |
108 static void solr_quote(string_t *dest, const char *str) | |
109 { | |
110 str_append_c(dest, '"'); | |
111 str_append(dest, str_escape(str)); | |
112 str_append_c(dest, '"'); | |
113 } | |
114 | |
115 static void solr_quote_http(string_t *dest, const char *str) | |
116 { | |
117 str_append(dest, "%22"); | |
118 solr_connection_http_escape(solr_conn, dest, str); | |
119 str_append(dest, "%22"); | |
120 } | |
121 | |
100 static struct fts_backend * | 122 static struct fts_backend * |
101 fts_backend_solr_init(struct mailbox *box) | 123 fts_backend_solr_init(struct mailbox *box) |
102 { | 124 { |
103 const struct fts_solr_settings *set = &fts_solr_settings; | 125 const struct fts_solr_settings *set = &fts_solr_settings; |
104 struct solr_fts_backend *backend; | 126 struct solr_fts_backend *backend; |
105 const char *username = box->storage->ns->user->username; | 127 struct mail_namespace *ns = box->storage->ns; |
128 const char *str; | |
106 | 129 |
107 if (solr_conn == NULL) | 130 if (solr_conn == NULL) |
108 solr_conn = solr_connection_init(set->url, set->debug); | 131 solr_conn = solr_connection_init(set->url, set->debug); |
109 | 132 |
110 backend = i_new(struct solr_fts_backend, 1); | 133 backend = i_new(struct solr_fts_backend, 1); |
111 backend->id_username = i_strdup(solr_escape_id_str(username)); | 134 str = fts_solr_settings.default_ns_prefix; |
135 if (str != NULL) { | |
136 backend->default_ns = | |
137 mail_namespace_find_prefix(ns->user->namespaces, str); | |
138 if (backend->default_ns == NULL) { | |
139 i_fatal("fts_solr: default_ns setting points to " | |
140 "nonexisting namespace"); | |
141 } | |
142 } else { | |
143 backend->default_ns = | |
144 mail_namespace_find_inbox(ns->user->namespaces); | |
145 } | |
146 str = solr_escape_id_str(ns->user->username); | |
147 backend->id_username = i_strdup(str); | |
148 if (box->storage->ns != backend->default_ns) { | |
149 str = solr_escape_id_str(ns->prefix); | |
150 backend->id_namespace = i_strdup(str); | |
151 } | |
112 backend->backend = fts_backend_solr; | 152 backend->backend = fts_backend_solr; |
113 | 153 |
114 if (set->substring_search) | 154 if (set->substring_search) |
115 backend->backend.flags |= FTS_BACKEND_FLAG_SUBSTRING_LOOKUPS; | 155 backend->backend.flags |= FTS_BACKEND_FLAG_SUBSTRING_LOOKUPS; |
116 return &backend->backend; | 156 return &backend->backend; |
118 | 158 |
119 static void fts_backend_solr_deinit(struct fts_backend *_backend) | 159 static void fts_backend_solr_deinit(struct fts_backend *_backend) |
120 { | 160 { |
121 struct solr_fts_backend *backend = (struct solr_fts_backend *)_backend; | 161 struct solr_fts_backend *backend = (struct solr_fts_backend *)_backend; |
122 | 162 |
163 i_free(backend->id_namespace); | |
123 i_free(backend->id_username); | 164 i_free(backend->id_username); |
124 i_free(backend); | 165 i_free(backend); |
125 } | 166 } |
126 | 167 |
127 static const char *fts_backend_solr_username(struct fts_backend *_backend) | 168 static void |
169 solr_add_ns_query(string_t *str, struct fts_backend *_backend, | |
170 struct mail_namespace *ns) | |
128 { | 171 { |
129 struct solr_fts_backend *backend = (struct solr_fts_backend *)_backend; | 172 struct solr_fts_backend *backend = (struct solr_fts_backend *)_backend; |
130 | 173 |
131 return backend->id_username; | 174 if (ns == backend->default_ns || *ns->prefix == '\0') |
175 str_append(str, " -ns:[* TO *]"); | |
176 else { | |
177 str_append(str, " ns:"); | |
178 solr_quote(str, ns->prefix); | |
179 } | |
180 } | |
181 | |
182 static void | |
183 solr_add_ns_query_http(string_t *str, struct fts_backend *backend, | |
184 struct mail_namespace *ns) | |
185 { | |
186 string_t *tmp; | |
187 | |
188 tmp = t_str_new(64); | |
189 solr_add_ns_query(tmp, backend, ns); | |
190 solr_connection_http_escape(solr_conn, str, str_c(tmp)); | |
132 } | 191 } |
133 | 192 |
134 static int fts_backend_solr_get_last_uid_fallback(struct fts_backend *backend, | 193 static int fts_backend_solr_get_last_uid_fallback(struct fts_backend *backend, |
135 uint32_t *last_uid_r) | 194 uint32_t *last_uid_r) |
136 { | 195 { |
196 struct mailbox *box = backend->box; | |
137 struct mailbox_status status; | 197 struct mailbox_status status; |
138 ARRAY_TYPE(seq_range) uids; | 198 ARRAY_TYPE(seq_range) uids; |
139 const struct seq_range *uidvals; | 199 const struct seq_range *uidvals; |
140 unsigned int count; | 200 unsigned int count; |
141 string_t *str; | 201 string_t *str; |
142 | 202 |
143 str = t_str_new(256); | 203 str = t_str_new(256); |
144 str_append(str, "fl=uid&rows=1&sort=uid%20desc&q="); | 204 str_append(str, "fl=uid&rows=1&sort=uid+desc&q="); |
145 | 205 |
146 mailbox_get_status(backend->box, STATUS_UIDVALIDITY, &status); | 206 mailbox_get_status(box, STATUS_UIDVALIDITY, &status); |
147 str_printfa(str, "uidv:%u%%20box:", status.uidvalidity); | 207 str_printfa(str, "uidv:%u+box:", status.uidvalidity); |
148 solr_quote_str(str, backend->box->name); | 208 solr_quote_http(str, box->name); |
149 str_append(str, "%20user:"); | 209 solr_add_ns_query_http(str, backend, box->storage->ns); |
150 solr_quote_str(str, backend->box->storage->ns->user->username); | 210 str_append(str, "+user:"); |
211 solr_quote_http(str, box->storage->ns->user->username); | |
151 | 212 |
152 t_array_init(&uids, 1); | 213 t_array_init(&uids, 1); |
153 if (solr_connection_select(solr_conn, str_c(str), | 214 if (solr_connection_select(solr_conn, str_c(str), |
154 NULL, NULL, &uids, NULL) < 0) | 215 NULL, NULL, &uids, NULL) < 0) |
155 return -1; | 216 return -1; |
168 } | 229 } |
169 | 230 |
170 static int fts_backend_solr_get_last_uid(struct fts_backend *backend, | 231 static int fts_backend_solr_get_last_uid(struct fts_backend *backend, |
171 uint32_t *last_uid_r) | 232 uint32_t *last_uid_r) |
172 { | 233 { |
234 struct mailbox *box = backend->box; | |
173 struct mailbox_status status; | 235 struct mailbox_status status; |
174 ARRAY_TYPE(seq_range) uids; | 236 ARRAY_TYPE(seq_range) uids; |
175 const struct seq_range *uidvals; | 237 const struct seq_range *uidvals; |
176 unsigned int count; | 238 unsigned int count; |
177 string_t *str; | 239 string_t *str; |
178 | 240 |
179 str = t_str_new(256); | 241 str = t_str_new(256); |
180 str_append(str, "fl=uid&rows=1&q=last_uid:TRUE%20"); | 242 str_append(str, "fl=uid&rows=1&q=last_uid:TRUE+"); |
181 | 243 |
182 mailbox_get_status(backend->box, STATUS_UIDVALIDITY, &status); | 244 mailbox_get_status(box, STATUS_UIDVALIDITY, &status); |
183 str_printfa(str, "uidv:%u%%20box:", status.uidvalidity); | 245 str_printfa(str, "uidv:%u+box:", status.uidvalidity); |
184 solr_quote_str(str, backend->box->name); | 246 solr_quote_http(str, box->name); |
185 str_append(str, "%20user:"); | 247 solr_add_ns_query_http(str, backend, box->storage->ns); |
186 solr_quote_str(str, backend->box->storage->ns->user->username); | 248 str_append(str, "+user:"); |
249 solr_quote_http(str, box->storage->ns->user->username); | |
187 | 250 |
188 t_array_init(&uids, 1); | 251 t_array_init(&uids, 1); |
189 if (solr_connection_select(solr_conn, str_c(str), | 252 if (solr_connection_select(solr_conn, str_c(str), |
190 NULL, NULL, &uids, NULL) < 0) | 253 NULL, NULL, &uids, NULL) < 0) |
191 return -1; | 254 return -1; |
203 return -1; | 266 return -1; |
204 } | 267 } |
205 return 0; | 268 return 0; |
206 } | 269 } |
207 | 270 |
271 static const char * | |
272 solr_get_vmailbox(struct fts_backend *_backend, | |
273 struct mailbox *box, const char *ns_prefix, | |
274 const char *mailbox, string_t *dest) | |
275 { | |
276 struct solr_fts_backend *backend = (struct solr_fts_backend *)_backend; | |
277 struct mail_namespace *namespaces = box->storage->ns->user->namespaces; | |
278 struct mail_namespace *ns; | |
279 | |
280 if (ns_prefix == NULL) | |
281 ns = backend->default_ns; | |
282 else { | |
283 ns = mail_namespace_find_prefix(namespaces, ns_prefix); | |
284 if (ns == NULL) | |
285 return FALSE; | |
286 } | |
287 return mail_namespace_get_vname(ns, dest, mailbox); | |
288 } | |
289 | |
208 static bool | 290 static bool |
209 solr_virtual_get_last_uids(const char *mailbox, uint32_t uidvalidity, | 291 solr_virtual_get_last_uids(const char *ns_prefix, const char *mailbox, |
210 uint32_t *uid, void *context) | 292 uint32_t uidvalidity, uint32_t *uid, void *context) |
211 { | 293 { |
212 struct fts_backend_solr_get_last_uids_context *ctx = context; | 294 struct fts_backend_solr_get_last_uids_context *ctx = context; |
213 struct fts_backend_uid_map *map; | 295 struct fts_backend_uid_map *map; |
296 const char *vname; | |
297 | |
298 vname = solr_get_vmailbox(ctx->backend, ctx->box, ns_prefix, | |
299 mailbox, ctx->vname); | |
214 | 300 |
215 map = array_append_space(ctx->last_uids); | 301 map = array_append_space(ctx->last_uids); |
216 map->mailbox = p_strdup(ctx->pool, mailbox); | 302 map->mailbox = p_strdup(ctx->pool, vname); |
217 map->uidvalidity = uidvalidity; | 303 map->uidvalidity = uidvalidity; |
218 map->uid = *uid; | 304 map->uid = *uid; |
219 return FALSE; | 305 return FALSE; |
220 } | 306 } |
221 | 307 |
222 static void add_pattern_as_solr(string_t *str, const char *pattern) | 308 static void |
223 { | 309 solr_add_pattern(string_t *str, const struct mailbox_virtual_pattern *pattern) |
224 const char *p; | 310 { |
311 const char *name, *p; | |
312 | |
313 name = pattern->pattern; | |
314 if (!mail_namespace_update_name(pattern->ns, &name)) | |
315 name = mail_namespace_fix_sep(pattern->ns, name); | |
225 | 316 |
226 /* first check if there are any wildcards in the pattern */ | 317 /* first check if there are any wildcards in the pattern */ |
227 for (p = pattern; *p != '\0'; p++) { | 318 for (p = name; *p != '\0'; p++) { |
228 if (*p == '%' || *p == '*') | 319 if (*p == '%' || *p == '*') |
229 break; | 320 break; |
230 } | 321 } |
231 if (*p == '\0') { | 322 if (*p == '\0') { |
232 /* full mailbox name */ | 323 /* full mailbox name */ |
233 str_append_c(str, '"'); | 324 solr_quote(str, name); |
234 str_append(str, str_escape(pattern)); | |
235 str_append_c(str, '"'); | |
236 return; | 325 return; |
237 } | 326 } |
238 | 327 |
239 /* there are at least some wildcards. */ | 328 /* there are at least some wildcards. */ |
240 for (p = pattern; *p != '\0'; p++) { | 329 for (p = name; *p != '\0'; p++) { |
241 if (*p == '%' || *p == '*') { | 330 if (*p == '%' || *p == '*') { |
242 if (p == pattern || (p[-1] != '%' && p[-1] != '*')) | 331 if (p == name || (p[-1] != '%' && p[-1] != '*')) |
243 str_append_c(str, '*'); | 332 str_append_c(str, '*'); |
244 } else { | 333 } else { |
245 if (!i_isalnum(*p)) | 334 if (!i_isalnum(*p)) |
246 str_append_c(str, '\\'); | 335 str_append_c(str, '\\'); |
247 str_append_c(str, *p); | 336 str_append_c(str, *p); |
248 } | 337 } |
249 } | 338 } |
250 } | 339 } |
251 | 340 |
252 static void | 341 static void |
253 fts_backend_solr_filter_mailboxes(struct solr_connection *solr_conn, | 342 fts_backend_solr_filter_mailboxes(struct fts_backend *_backend, |
254 string_t *str, struct mailbox *box) | 343 string_t *str, struct mailbox *box) |
255 { | 344 { |
256 ARRAY_TYPE(const_string) includes_arr, excludes_arr; | 345 struct solr_fts_backend *backend = (struct solr_fts_backend *)_backend; |
257 const char *const *includes, *const *excludes; | 346 ARRAY_TYPE(mailbox_virtual_patterns) includes_arr, excludes_arr; |
258 unsigned int i, inc_count, exc_count; | 347 const struct mailbox_virtual_pattern *includes, *excludes; |
348 unsigned int i, inc_count, exc_count, len; | |
259 string_t *fq; | 349 string_t *fq; |
260 | 350 |
261 t_array_init(&includes_arr, 16); | 351 t_array_init(&includes_arr, 16); |
262 t_array_init(&excludes_arr, 16); | 352 t_array_init(&excludes_arr, 16); |
263 mailbox_get_virtual_box_patterns(box, &includes_arr, &excludes_arr); | 353 mailbox_get_virtual_box_patterns(box, &includes_arr, &excludes_arr); |
267 | 357 |
268 /* First see if there are any patterns that begin with a wildcard. | 358 /* First see if there are any patterns that begin with a wildcard. |
269 Solr doesn't allow them, so in that case we'll need to return | 359 Solr doesn't allow them, so in that case we'll need to return |
270 all mailboxes. */ | 360 all mailboxes. */ |
271 for (i = 0; i < inc_count; i++) { | 361 for (i = 0; i < inc_count; i++) { |
272 if (*includes[i] == '*' || *includes[i] == '%') | 362 if (*includes[i].pattern == '*' || |
363 *includes[i].pattern == '%') | |
273 break; | 364 break; |
274 } | 365 } |
275 | 366 |
276 fq = t_str_new(128); | 367 fq = t_str_new(128); |
277 if (i == inc_count) { | 368 if (i == inc_count && inc_count <= FTS_SOLR_MAX_BOX_INC_PATTERNS) { |
278 /* we can filter what mailboxes we want returned */ | 369 /* we can filter what mailboxes we want returned */ |
279 str_append_c(fq, '('); | 370 str_append_c(fq, '('); |
280 for (i = 0; i < inc_count; i++) { | 371 for (i = 0; i < inc_count; i++) { |
281 if (i != 0) | 372 if (i != 0) |
282 str_append(fq, " OR "); | 373 str_append(fq, " OR "); |
374 str_append_c(fq, '('); | |
283 str_append(fq, "box:"); | 375 str_append(fq, "box:"); |
284 add_pattern_as_solr(fq, includes[i]); | 376 solr_add_pattern(fq, &includes[i]); |
377 solr_add_ns_query(fq, _backend, includes[i].ns); | |
378 str_append_c(fq, ')'); | |
285 } | 379 } |
286 str_append_c(fq, ')'); | 380 str_append_c(fq, ')'); |
287 } | 381 } |
382 exc_count = I_MIN(FTS_SOLR_MAX_BOX_EXC_PATTERNS, exc_count); | |
288 for (i = 0; i < exc_count; i++) { | 383 for (i = 0; i < exc_count; i++) { |
289 if (str_len(fq) > 0) | 384 if (str_len(fq) > len) |
290 str_append_c(fq, ' '); | 385 str_append_c(fq, ' '); |
386 str_append_c(fq, '('); | |
291 str_append(fq, "-box:"); | 387 str_append(fq, "-box:"); |
292 add_pattern_as_solr(fq, excludes[i]); | 388 solr_add_pattern(fq, &excludes[i]); |
389 if (excludes[i].ns == backend->default_ns) { | |
390 str_append(fq, " OR NOT"); | |
391 solr_add_ns_query(fq, _backend, excludes[i].ns); | |
392 } else if (*excludes[i].ns->prefix != '\0') { | |
393 str_append(fq, " OR -ns:"); | |
394 solr_quote(fq, excludes[i].ns->prefix); | |
395 } | |
396 str_append_c(fq, ')'); | |
293 } | 397 } |
294 if (str_len(fq) > 0) { | 398 if (str_len(fq) > 0) { |
295 str_append(str, "&fq="); | 399 str_append(str, "&fq="); |
296 solr_connection_http_escape(solr_conn, str, str_c(fq)); | 400 solr_connection_http_escape(solr_conn, str, str_c(fq)); |
297 } | 401 } |
303 { | 407 { |
304 struct fts_backend_solr_get_last_uids_context ctx; | 408 struct fts_backend_solr_get_last_uids_context ctx; |
305 string_t *str; | 409 string_t *str; |
306 | 410 |
307 memset(&ctx, 0, sizeof(ctx)); | 411 memset(&ctx, 0, sizeof(ctx)); |
412 ctx.backend = backend; | |
308 ctx.pool = pool; | 413 ctx.pool = pool; |
309 ctx.last_uids = last_uids; | 414 ctx.last_uids = last_uids; |
415 ctx.box = backend->box; | |
416 ctx.vname = t_str_new(256); | |
310 | 417 |
311 str = t_str_new(256); | 418 str = t_str_new(256); |
312 str_printfa(str, "fl=uid,box,uidv&rows=%u&q=last_uid:TRUE%%20user:", | 419 str_printfa(str, "fl=uid,box,uidv,ns&rows=%u&q=last_uid:TRUE+user:", |
313 SOLR_MAX_ROWS); | 420 SOLR_MAX_ROWS); |
314 solr_quote_str(str, backend->box->storage->ns->user->username); | 421 solr_quote_http(str, backend->box->storage->ns->user->username); |
315 fts_backend_solr_filter_mailboxes(solr_conn, str, backend->box); | 422 fts_backend_solr_filter_mailboxes(backend, str, backend->box); |
316 | 423 |
317 return solr_connection_select(solr_conn, str_c(str), | 424 return solr_connection_select(solr_conn, str_c(str), |
318 solr_virtual_get_last_uids, &ctx, | 425 solr_virtual_get_last_uids, &ctx, |
319 NULL, NULL); | 426 NULL, NULL); |
320 } | 427 } |
341 | 448 |
342 static void | 449 static void |
343 fts_backend_solr_add_doc_prefix(struct solr_fts_backend_build_context *ctx, | 450 fts_backend_solr_add_doc_prefix(struct solr_fts_backend_build_context *ctx, |
344 uint32_t uid) | 451 uint32_t uid) |
345 { | 452 { |
453 struct solr_fts_backend *backend = | |
454 (struct solr_fts_backend *)ctx->ctx.backend; | |
346 struct mailbox *box = ctx->ctx.backend->box; | 455 struct mailbox *box = ctx->ctx.backend->box; |
347 | 456 |
348 str_printfa(ctx->cmd, "<doc>" | 457 str_printfa(ctx->cmd, "<doc>" |
349 "<field name=\"uid\">%u</field>" | 458 "<field name=\"uid\">%u</field>" |
350 "<field name=\"uidv\">%u</field>", | 459 "<field name=\"uidv\">%u</field>", |
351 uid, ctx->uid_validity); | 460 uid, ctx->uid_validity); |
352 | 461 |
462 if (box->storage->ns != backend->default_ns) { | |
463 str_append(ctx->cmd, "<field name=\"ns\">"); | |
464 xml_encode(ctx->cmd, box->storage->ns->prefix); | |
465 str_append(ctx->cmd, "</field>"); | |
466 } | |
353 str_append(ctx->cmd, "<field name=\"box\">"); | 467 str_append(ctx->cmd, "<field name=\"box\">"); |
354 xml_encode(ctx->cmd, box->name); | 468 xml_encode(ctx->cmd, box->name); |
355 str_append(ctx->cmd, "</field><field name=\"user\">"); | 469 str_append(ctx->cmd, "</field><field name=\"user\">"); |
356 xml_encode(ctx->cmd, box->storage->ns->user->username); | 470 xml_encode(ctx->cmd, box->storage->ns->user->username); |
357 str_append(ctx->cmd, "</field>"); | 471 str_append(ctx->cmd, "</field>"); |
472 } | |
473 | |
474 static void xml_encode_id(string_t *str, struct fts_backend *_backend, | |
475 uint32_t uid, uint32_t uid_validity, | |
476 const char *mailbox) | |
477 { | |
478 struct solr_fts_backend *backend = (struct solr_fts_backend *)_backend; | |
479 | |
480 if (uid != 0) | |
481 str_printfa(str, "%u/", uid); | |
482 else | |
483 str_append(str, "L/"); | |
484 if (backend->id_namespace != NULL) { | |
485 xml_encode(str, backend->id_namespace); | |
486 str_append_c(str, '/'); | |
487 } | |
488 str_printfa(str, "%u/", uid_validity); | |
489 xml_encode(str, backend->id_username); | |
490 str_append_c(str, '/'); | |
491 xml_encode(str, mailbox); | |
358 } | 492 } |
359 | 493 |
360 static int | 494 static int |
361 fts_backend_solr_build_more(struct fts_backend_build_context *_ctx, | 495 fts_backend_solr_build_more(struct fts_backend_build_context *_ctx, |
362 uint32_t uid, const unsigned char *data, | 496 uint32_t uid, const unsigned char *data, |
377 str_append(cmd, "</field></doc>"); | 511 str_append(cmd, "</field></doc>"); |
378 } | 512 } |
379 ctx->prev_uid = uid; | 513 ctx->prev_uid = uid; |
380 | 514 |
381 fts_backend_solr_add_doc_prefix(ctx, uid); | 515 fts_backend_solr_add_doc_prefix(ctx, uid); |
382 str_printfa(cmd, "<field name=\"id\">%u/%u/", | 516 str_printfa(cmd, "<field name=\"id\">"); |
383 uid, ctx->uid_validity); | 517 xml_encode_id(cmd, _ctx->backend, uid, ctx->uid_validity, |
384 xml_encode(cmd, fts_backend_solr_username(ctx->ctx.backend)); | 518 box->name); |
385 str_append_c(cmd, '/'); | |
386 xml_encode(cmd, box->name); | |
387 str_append(cmd, "</field>"); | 519 str_append(cmd, "</field>"); |
388 | 520 |
389 ctx->headers = headers; | 521 ctx->headers = headers; |
390 if (headers) { | 522 if (headers) { |
391 str_append(cmd, "<field name=\"hdr\">"); | 523 str_append(cmd, "<field name=\"hdr\">"); |
423 if another session is indexing at the same time, the last_uid value | 555 if another session is indexing at the same time, the last_uid value |
424 may shrink. This doesn't really matter, we'll simply do more work | 556 may shrink. This doesn't really matter, we'll simply do more work |
425 in future by reindexing some messages. */ | 557 in future by reindexing some messages. */ |
426 fts_backend_solr_add_doc_prefix(ctx, ctx->prev_uid); | 558 fts_backend_solr_add_doc_prefix(ctx, ctx->prev_uid); |
427 str_printfa(ctx->cmd, "<field name=\"last_uid\">TRUE</field>" | 559 str_printfa(ctx->cmd, "<field name=\"last_uid\">TRUE</field>" |
428 "<field name=\"id\">L/%u/", ctx->uid_validity); | 560 "<field name=\"id\">"); |
429 xml_encode(ctx->cmd, fts_backend_solr_username(ctx->ctx.backend)); | 561 xml_encode_id(ctx->cmd, ctx->ctx.backend, 0, ctx->uid_validity, |
430 str_append_c(ctx->cmd, '/'); | 562 box->name); |
431 xml_encode(ctx->cmd, box->name); | |
432 str_append(ctx->cmd, "</field></doc></add>"); | 563 str_append(ctx->cmd, "</field></doc></add>"); |
433 | 564 |
434 solr_connection_post_more(ctx->post, str_data(ctx->cmd), | 565 solr_connection_post_more(ctx->post, str_data(ctx->cmd), |
435 str_len(ctx->cmd)); | 566 str_len(ctx->cmd)); |
436 ret = solr_connection_post_end(ctx->post); | 567 ret = solr_connection_post_end(ctx->post); |
464 | 595 |
465 T_BEGIN { | 596 T_BEGIN { |
466 string_t *cmd; | 597 string_t *cmd; |
467 | 598 |
468 cmd = t_str_new(256); | 599 cmd = t_str_new(256); |
469 str_printfa(cmd, "<delete><id>%u/%u/", | 600 str_append(cmd, "<delete><id>"); |
470 mail->uid, status.uidvalidity); | 601 xml_encode_id(cmd, backend, mail->uid, status.uidvalidity, |
471 xml_encode(cmd, fts_backend_solr_username(backend)); | 602 mail->box->name); |
472 str_append_c(cmd, '/'); | |
473 xml_encode(cmd, mail->box->name); | |
474 str_append(cmd, "</id></delete>"); | 603 str_append(cmd, "</id></delete>"); |
475 | 604 |
476 (void)solr_connection_post(solr_conn, str_c(cmd)); | 605 (void)solr_connection_post(solr_conn, str_c(cmd)); |
477 } T_END; | 606 } T_END; |
478 } | 607 } |
493 | 622 |
494 static void fts_backend_solr_unlock(struct fts_backend *backend ATTR_UNUSED) | 623 static void fts_backend_solr_unlock(struct fts_backend *backend ATTR_UNUSED) |
495 { | 624 { |
496 } | 625 } |
497 | 626 |
498 static bool solr_virtual_uid_map(const char *mailbox, uint32_t uidvalidity, | 627 static bool solr_virtual_uid_map(const char *ns_prefix, const char *mailbox, |
499 uint32_t *uid, void *context) | 628 uint32_t uidvalidity, uint32_t *uid, |
500 { | 629 void *context) |
501 struct mailbox *box = context; | 630 { |
502 | 631 struct solr_virtual_uid_map_context *ctx = context; |
503 return mailbox_get_virtual_uid(box, mailbox, uidvalidity, | 632 const char *vname; |
504 *uid, uid); | 633 |
634 vname = solr_get_vmailbox(ctx->backend, ctx->box, ns_prefix, | |
635 mailbox, ctx->vname); | |
636 return mailbox_get_virtual_uid(ctx->box, vname, uidvalidity, *uid, uid); | |
505 } | 637 } |
506 | 638 |
507 static int fts_backend_solr_lookup(struct fts_backend_lookup_context *ctx, | 639 static int fts_backend_solr_lookup(struct fts_backend_lookup_context *ctx, |
508 ARRAY_TYPE(seq_range) *definite_uids, | 640 ARRAY_TYPE(seq_range) *definite_uids, |
509 ARRAY_TYPE(seq_range) *maybe_uids, | 641 ARRAY_TYPE(seq_range) *maybe_uids, |
510 ARRAY_TYPE(fts_score_map) *scores) | 642 ARRAY_TYPE(fts_score_map) *scores) |
511 { | 643 { |
512 struct mailbox *box = ctx->backend->box; | 644 struct mailbox *box = ctx->backend->box; |
645 struct solr_virtual_uid_map_context uid_map_ctx; | |
513 const struct fts_backend_lookup_field *fields; | 646 const struct fts_backend_lookup_field *fields; |
514 unsigned int i, count; | 647 unsigned int i, count; |
515 struct mailbox_status status; | 648 struct mailbox_status status; |
516 string_t *str; | 649 string_t *str; |
517 bool virtual; | 650 bool virtual; |
519 virtual = strcmp(box->storage->name, "virtual") == 0; | 652 virtual = strcmp(box->storage->name, "virtual") == 0; |
520 mailbox_get_status(box, STATUS_UIDVALIDITY, &status); | 653 mailbox_get_status(box, STATUS_UIDVALIDITY, &status); |
521 | 654 |
522 str = t_str_new(256); | 655 str = t_str_new(256); |
523 if (!virtual) { | 656 if (!virtual) { |
524 str_printfa(str, "fl=uid,score&rows=%u&sort=uid%%20asc&q=", | 657 str_printfa(str, "fl=uid,score&rows=%u&sort=uid+asc&q=", |
525 status.uidnext); | 658 status.uidnext); |
526 } else { | 659 } else { |
527 str_printfa(str, "fl=uid,score,box,uidv&rows=%u" | 660 str_printfa(str, "fl=uid,score,box,uidv,ns&rows=%u" |
528 "&sort=box%%20asc,uid%%20asc&q=", | 661 "&sort=box+asc,uid+asc&q=", |
529 SOLR_MAX_ROWS); | 662 SOLR_MAX_ROWS); |
530 } | 663 } |
531 | 664 |
532 /* build a lucene search query from the fields */ | 665 /* build a lucene search query from the fields */ |
533 fields = array_get(&ctx->fields, &count); | 666 fields = array_get(&ctx->fields, &count); |
534 for (i = 0; i < count; i++) { | 667 for (i = 0; i < count; i++) { |
535 if (i > 0) | 668 if (i > 0) |
536 str_append(str, "%20"); | 669 str_append_c(str, '+'); |
537 | 670 |
538 if ((fields[i].flags & FTS_LOOKUP_FLAG_INVERT) != 0) | 671 if ((fields[i].flags & FTS_LOOKUP_FLAG_INVERT) != 0) |
539 str_append_c(str, '-'); | 672 str_append_c(str, '-'); |
540 | 673 |
541 if ((fields[i].flags & FTS_LOOKUP_FLAG_HEADER) == 0) { | 674 if ((fields[i].flags & FTS_LOOKUP_FLAG_HEADER) == 0) { |
547 str_append(str, "hdr:"); | 680 str_append(str, "hdr:"); |
548 } else { | 681 } else { |
549 /* both */ | 682 /* both */ |
550 str_append(str, "any:"); | 683 str_append(str, "any:"); |
551 } | 684 } |
552 solr_quote_str(str, fields[i].key); | 685 solr_quote_http(str, fields[i].key); |
553 } | 686 } |
554 | 687 |
555 /* use a separate filter query for selecting the mailbox. it shouldn't | 688 /* use a separate filter query for selecting the mailbox. it shouldn't |
556 affect the score and there could be some caching benefits too. */ | 689 affect the score and there could be some caching benefits too. */ |
557 str_append(str, "&fq=user:"); | 690 str_append(str, "&fq=user:"); |
558 solr_quote_str(str, box->storage->ns->user->username); | 691 solr_quote_http(str, box->storage->ns->user->username); |
559 if (virtual) | 692 if (virtual) |
560 fts_backend_solr_filter_mailboxes(solr_conn, str, box); | 693 fts_backend_solr_filter_mailboxes(ctx->backend, str, box); |
561 else { | 694 else { |
562 str_printfa(str, "%%20uidv:%u%%20box:", status.uidvalidity); | 695 str_printfa(str, "+uidv:%u+box:", status.uidvalidity); |
563 solr_quote_str(str, box->name); | 696 solr_quote_http(str, box->name); |
697 solr_add_ns_query_http(str, ctx->backend, box->storage->ns); | |
564 } | 698 } |
565 | 699 |
566 array_clear(maybe_uids); | 700 array_clear(maybe_uids); |
567 if (!virtual) { | 701 if (!virtual) { |
568 return solr_connection_select(solr_conn, str_c(str), NULL, NULL, | 702 return solr_connection_select(solr_conn, str_c(str), NULL, NULL, |
569 definite_uids, scores); | 703 definite_uids, scores); |
570 } else { | 704 } else { |
705 memset(&uid_map_ctx, 0, sizeof(uid_map_ctx)); | |
706 uid_map_ctx.backend = ctx->backend; | |
707 uid_map_ctx.box = box; | |
708 uid_map_ctx.vname = t_str_new(256); | |
571 return solr_connection_select(solr_conn, str_c(str), | 709 return solr_connection_select(solr_conn, str_c(str), |
572 solr_virtual_uid_map, box, | 710 solr_virtual_uid_map, |
711 &uid_map_ctx, | |
573 definite_uids, scores); | 712 definite_uids, scores); |
574 } | 713 } |
575 } | 714 } |
576 | 715 |
577 struct fts_backend fts_backend_solr = { | 716 struct fts_backend fts_backend_solr = { |