fts-backend-solr.c revision b03b0570081336e0195415418a39e0ce2484b314
/* Copyright (c) 2006-2010 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "str.h"
#include "strescape.h"
#include "mail-storage-private.h"
#include "mail-namespace.h"
#include "solr-connection.h"
#include "fts-solr-plugin.h"
#include <ctype.h>
#define SOLR_MAX_ROWS 100000
#define FTS_SOLR_MAX_BOX_INC_PATTERNS 5
#define FTS_SOLR_MAX_BOX_EXC_PATTERNS 5
struct solr_fts_backend {
struct fts_backend backend;
struct mail_namespace *default_ns;
};
struct solr_fts_backend_build_context {
struct fts_backend_build_context ctx;
struct solr_connection_post *post;
bool headers;
bool field_open;
};
struct solr_virtual_uid_map_context {
struct fts_backend *backend;
};
struct fts_backend *backend;
};
{
/* ugly workaround to allow selecting INBOX from a Maildir/
when it's not in the inbox=yes namespace. */
*name = "INBOX";
}
}
static const char *
{
return name;
}
static void
{
unsigned int i;
for (i = 0; i < len; i++) {
switch (data[i]) {
case '&':
break;
case '<':
break;
case '>':
break;
case '\t':
case '\n':
case '\r':
/* exceptions to the following control char check */
break;
default:
if (data[i] < 32) {
/* SOLR doesn't like control characters.
replace them with spaces. */
} else {
}
break;
}
}
}
{
}
static const char *solr_escape_id_str(const char *str)
{
const char *p;
for (p = str; *p != '\0'; p++) {
if (*p == '/' || *p == '!')
break;
}
if (*p == '\0')
return str;
for (p = str; *p != '\0'; p++) {
switch (*p) {
case '/':
break;
case '!':
break;
default:
str_append_c(tmp, *p);
break;
}
}
}
{
}
{
}
static struct fts_backend *
{
struct fts_solr_user *fuser =
struct solr_fts_backend *backend;
struct mail_namespace *ns;
i_fatal("fts_solr: default_ns setting points to "
"nonexisting namespace");
}
} else {
}
}
if (set->substring_search)
}
{
}
static void
{
if (!neg)
else
} else {
if (!neg)
else
}
}
static void
struct mail_namespace *ns)
{
}
{
struct mail_namespace *ns;
struct mailbox_status status;
const char *box_name;
unsigned int count;
return -1;
if (count == 0) {
/* nothing indexed yet for this mailbox */
*last_uid_r = 0;
} else {
i_error("fts_solr: Last UID lookup returned multiple rows");
return -1;
}
return 0;
}
{
struct mail_namespace *ns;
struct mailbox_status status;
const char *box_name;
unsigned int count;
return -1;
if (count == 0) {
/* either nothing is indexed or we're converting from an
older database format without the last_uid fields */
} else {
i_error("fts_solr: Last UID lookup returned multiple rows");
return -1;
}
return 0;
}
static struct mail_namespace *
{
return backend->default_ns;
else
}
static bool
{
struct fts_backend_uid_map *map;
struct mail_namespace *ns;
const char *vname;
}
return FALSE;
}
static void
{
const char *name, *p;
return;
}
/* first check if there are any wildcards in the pattern */
for (p = name; *p != '\0'; p++) {
if (*p == '%' || *p == '*')
break;
}
if (*p == '\0') {
/* full mailbox name */
return;
}
/* there are at least some wildcards. */
for (p = name; *p != '\0'; p++) {
if (*p == '%' || *p == '*') {
} else {
if (!i_isalnum(*p))
str_append_c(str, *p);
}
}
}
static void
{
struct mail_namespace *ns;
/* First see if there are any patterns that begin with a wildcard.
Solr doesn't allow them, so in that case we'll need to return
all mailboxes. */
for (i = 0; i < inc_count; i++) {
break;
}
/* we can filter what mailboxes we want returned */
for (i = 0; i < inc_count; i++) {
if (i != 0)
}
}
for (i = 0; i < exc_count; i++) {
}
}
}
static int
{
}
static int
struct fts_backend_build_context **ctx_r)
{
struct solr_fts_backend_build_context *ctx;
struct mailbox_status status;
return 0;
}
static void
{
struct solr_fts_backend *backend =
struct mail_namespace *ns;
const char *box_name;
"<field name=\"uid\">%u</field>"
"<field name=\"uidv\">%u</field>",
}
}
{
if (uid != 0)
else
}
}
static void
{
} else {
if (ctx->field_open) {
}
}
}
static void
{
struct solr_fts_backend_build_context *ctx =
(struct solr_fts_backend_build_context *)_ctx;
else {
if (ctx->field_open) {
}
}
}
static bool
const char *content_disposition ATTR_UNUSED)
{
struct solr_fts_backend_build_context *ctx =
(struct solr_fts_backend_build_context *)_ctx;
return FALSE;
else {
/* body comes first, then headers */
}
if (!ctx->field_open) {
}
return TRUE;
}
static int
{
struct solr_fts_backend_build_context *ctx =
(struct solr_fts_backend_build_context *)_ctx;
}
return 0;
}
static int
{
int ret;
return 0;
if (ctx->field_open) {
}
/* Update the mailbox's last_uid field, replacing the existing
document. Note that since there is no locking, it's possible that
if another session is indexing at the same time, the last_uid value
may shrink. This doesn't really matter, we'll simply do more work
in future by reindexing some messages. */
"<field name=\"id\">");
/* commit and wait until the documents we just indexed are
visible to the following search */
"waitSearcher=\"true\"/>") < 0)
ret = -1;
return ret;
}
static int
{
struct solr_fts_backend_build_context *ctx =
(struct solr_fts_backend_build_context *)_ctx;
int ret;
return ret;
}
static void
{
struct mailbox_status status;
T_BEGIN {
} T_END;
}
static void
bool committed ATTR_UNUSED)
{
"<commit waitFlush=\"false\" waitSearcher=\"false\"/>");
}
{
return 1;
}
{
}
void *context)
{
struct mail_namespace *ns;
const char *vname;
bool convert_inbox;
return TRUE;
}
return FALSE;
}
{
struct mail_namespace *ns;
const struct fts_backend_lookup_field *fields;
const char *box_name;
unsigned int i, count;
struct mailbox_status status;
bool virtual;
if (!virtual) {
} else {
"&sort=box+asc,uid+asc&q=",
}
/* build a lucene search query from the fields */
for (i = 0; i < count; i++) {
if (i > 0)
/* body only */
/* header only */
} else {
/* both */
}
}
/* use a separate filter query for selecting the mailbox. it shouldn't
affect the score and there could be some caching benefits too. */
if (virtual)
else {
}
if (!virtual) {
} else {
}
}
struct fts_backend fts_backend_solr = {
.name = "solr",
{
NULL,
NULL,
NULL,
}
};