]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Added support for sorting by X-SCORE. Currently it's only set by fts-solr.
authorTimo Sirainen <tss@iki.fi>
Sun, 13 Jul 2008 16:09:55 +0000 (19:09 +0300)
committerTimo Sirainen <tss@iki.fi>
Sun, 13 Jul 2008 16:09:55 +0000 (19:09 +0300)
--HG--
branch : HEAD

13 files changed:
src/imap/cmd-sort.c
src/lib-storage/index/index-mail.c
src/lib-storage/index/index-sort.c
src/lib-storage/mail-storage.h
src/plugins/fts-solr/fts-backend-solr.c
src/plugins/fts-solr/solr-connection.c
src/plugins/fts-solr/solr-connection.h
src/plugins/fts/fts-api-private.h
src/plugins/fts/fts-api.c
src/plugins/fts/fts-api.h
src/plugins/fts/fts-search.c
src/plugins/fts/fts-storage.c
src/plugins/fts/fts-storage.h

index 93756e09d9f2d7ece72ddfd7ef0adbf7571126e8..6b98ace024b74d061dcd9c6f9ec5e294c6c7f64b 100644 (file)
@@ -12,15 +12,16 @@ struct sort_name {
 };
 
 static struct sort_name sort_names[] = {
-       { MAIL_SORT_ARRIVAL,    "arrival" },
-       { MAIL_SORT_CC,         "cc" },
-       { MAIL_SORT_DATE,       "date" },
-       { MAIL_SORT_FROM,       "from" },
-       { MAIL_SORT_SIZE,       "size" },
-       { MAIL_SORT_SUBJECT,    "subject" },
-       { MAIL_SORT_TO,         "to" },
-
-       { MAIL_SORT_END,        NULL }
+       { MAIL_SORT_ARRIVAL,            "arrival" },
+       { MAIL_SORT_CC,                 "cc" },
+       { MAIL_SORT_DATE,               "date" },
+       { MAIL_SORT_FROM,               "from" },
+       { MAIL_SORT_SIZE,               "size" },
+       { MAIL_SORT_SUBJECT,            "subject" },
+       { MAIL_SORT_TO,                 "to" },
+       { MAIL_SORT_SEARCH_SCORE,       "x-score" },
+
+       { MAIL_SORT_END,                NULL }
 };
 
 static int
index fae638f79325580e83759f1b8cb4bdcd5360d63e..8bd906e73fc765d2dfc3bdb30fa7d2246230ec04 100644 (file)
@@ -1007,6 +1007,7 @@ int index_mail_get_special(struct mail *_mail,
        case MAIL_FETCH_FROM_ENVELOPE:
        case MAIL_FETCH_UIDL_FILE_NAME:
        case MAIL_FETCH_UIDL_BACKEND:
+       case MAIL_FETCH_SEARCH_SCORE:
                *value_r = "";
                return 0;
        case MAIL_FETCH_HEADER_MD5:
index 8bfe55792027e844c21c021476fea3d3caab5013..def10eeebd64d2d4d54d84cdf8982982cd3b8986 100644 (file)
@@ -23,6 +23,12 @@ struct mail_sort_node_size {
 };
 ARRAY_DEFINE_TYPE(mail_sort_node_size, struct mail_sort_node_size);
 
+struct mail_sort_node_float {
+       uint32_t seq;
+       float num;
+};
+ARRAY_DEFINE_TYPE(mail_sort_node_float, struct mail_sort_node_float);
+
 struct sort_cmp_context {
        struct mail_search_sort_program *program;
        struct mail *mail;
@@ -74,6 +80,28 @@ index_sort_list_add_size(struct mail_search_sort_program *program,
                node->size = 0;
 }
 
+static float index_sort_get_score(struct mail *mail)
+{
+       const char *str;
+
+       if (mail_get_special(mail, MAIL_FETCH_SEARCH_SCORE, &str) < 0)
+               return 0;
+       else
+               return strtod(str, NULL);
+}
+
+static void
+index_sort_list_add_score(struct mail_search_sort_program *program,
+                         struct mail *mail)
+{
+       ARRAY_TYPE(mail_sort_node_float) *nodes = program->context;
+       struct mail_sort_node_float *node;
+
+       node = array_append_space(nodes);
+       node->seq = mail->seq;
+       node->num = index_sort_get_score(mail);
+}
+
 void index_sort_list_add(struct mail_search_sort_program *program,
                         struct mail *mail)
 {
@@ -142,6 +170,36 @@ index_sort_list_finish_size(struct mail_search_sort_program *program)
        program->context = NULL;
 }
 
+static int sort_node_float_cmp(const void *p1, const void *p2)
+{
+       struct sort_cmp_context *ctx = &static_node_cmp_context;
+       const struct mail_sort_node_float *n1 = p1, *n2 = p2;
+
+       if (n1->num < n2->num)
+               return !ctx->reverse ? -1 : 1;
+       if (n1->num > n2->num)
+               return !ctx->reverse ? 1 : -1;
+
+       return index_sort_node_cmp_type(ctx->mail,
+                                       ctx->program->sort_program + 1,
+                                       n1->seq, n2->seq);
+}
+
+static void
+index_sort_list_finish_float(struct mail_search_sort_program *program)
+{
+       ARRAY_TYPE(mail_sort_node_float) *nodes = program->context;
+       struct mail_sort_node_float *float_nodes;
+       unsigned int count;
+
+       float_nodes = array_get_modifiable(nodes, &count);
+       qsort(float_nodes, count, sizeof(struct mail_sort_node_float),
+             sort_node_float_cmp);
+       memcpy(&program->seqs, nodes, sizeof(program->seqs));
+       i_free(nodes);
+       program->context = NULL;
+}
+
 void index_sort_list_finish(struct mail_search_sort_program *program)
 {
        memset(&static_node_cmp_context, 0, sizeof(static_node_cmp_context));
@@ -224,6 +282,16 @@ index_sort_program_init(struct mailbox_transaction_context *t,
                program->sort_list_finish = index_sort_list_finish_string;
                index_sort_list_init_string(program);
                break;
+       case MAIL_SORT_SEARCH_SCORE: {
+               ARRAY_TYPE(mail_sort_node_float) *nodes;
+
+               nodes = i_malloc(sizeof(*nodes));
+               i_array_init(nodes, 128);
+               program->sort_list_add = index_sort_list_add_score;
+               program->sort_list_finish = index_sort_list_finish_float;
+               program->context = nodes;
+               break;
+       }
        default:
                i_unreached();
        }
@@ -303,6 +371,7 @@ int index_sort_node_cmp_type(struct mail *mail,
        enum mail_sort_type sort_type;
        time_t time1, time2;
        uoff_t size1, size2;
+       float float1, float2;
        int ret = 0;
 
        sort_type = *sort_program & MAIL_SORT_MASK;
@@ -366,6 +435,15 @@ int index_sort_node_cmp_type(struct mail *mail,
                ret = size1 < size2 ? -1 :
                        (size1 > size2 ? 1 : 0);
                break;
+       case MAIL_SORT_SEARCH_SCORE:
+               mail_set_seq(mail, seq1);
+               float1 = index_sort_get_score(mail);
+               mail_set_seq(mail, seq2);
+               float2 = index_sort_get_score(mail);
+
+               ret = float1 < float2 ? -1 :
+                       (float1 > float2 ? 1 : 0);
+               break;
        case MAIL_SORT_END:
                return seq1 < seq2 ? -1 :
                        (seq1 > seq2 ? 1 : 0);
index 30036529f3496480f761280c277ff2d218bfa831..464c44a04fc47bd191fb364a9076dc767a6e23c7 100644 (file)
@@ -86,7 +86,7 @@ enum mailbox_search_result_flags {
 
 enum mail_sort_type {
 /* Maximum size for sort program (each one separately + END) */
-#define MAX_SORT_PROGRAM_SIZE (7 + 1)
+#define MAX_SORT_PROGRAM_SIZE (8 + 1)
 
        MAIL_SORT_ARRIVAL       = 0x0001,
        MAIL_SORT_CC            = 0x0002,
@@ -95,6 +95,7 @@ enum mail_sort_type {
        MAIL_SORT_SIZE          = 0x0010,
        MAIL_SORT_SUBJECT       = 0x0020,
        MAIL_SORT_TO            = 0x0040,
+       MAIL_SORT_SEARCH_SCORE  = 0x0080,
 
        MAIL_SORT_MASK          = 0x0fff,
        MAIL_SORT_FLAG_REVERSE  = 0x1000, /* reverse this mask type */
@@ -126,7 +127,8 @@ enum mail_fetch_field {
        MAIL_FETCH_HEADER_MD5           = 0x00010000,
        MAIL_FETCH_UIDL_FILE_NAME       = 0x00020000,
        MAIL_FETCH_UIDL_BACKEND         = 0x00040000,
-       MAIL_FETCH_MAILBOX_NAME         = 0x00080000
+       MAIL_FETCH_MAILBOX_NAME         = 0x00080000,
+       MAIL_FETCH_SEARCH_SCORE         = 0x00100000
 };
 
 enum mailbox_transaction_flags {
index a14bae54f4fe062ae3908640f1da9d7b53df23d4..fc27988648a24ae3ba25fc619fb360c89e2c66f0 100644 (file)
@@ -96,7 +96,7 @@ static int fts_backend_solr_get_last_uid(struct fts_backend *backend,
        solr_quote_str(str, backend->box->storage->user);
 
        t_array_init(&uids, 1);
-       if (solr_connection_select(solr_conn, str_c(str), &uids) < 0)
+       if (solr_connection_select(solr_conn, str_c(str), &uids, NULL) < 0)
                return -1;
 
        uidvals = array_get(&uids, &count);
@@ -257,7 +257,8 @@ static void fts_backend_solr_unlock(struct fts_backend *backend ATTR_UNUSED)
 
 static int fts_backend_solr_lookup(struct fts_backend_lookup_context *ctx,
                                   ARRAY_TYPE(seq_range) *definite_uids,
-                                  ARRAY_TYPE(seq_range) *maybe_uids)
+                                  ARRAY_TYPE(seq_range) *maybe_uids,
+                                  ARRAY_TYPE(fts_score_map) *scores)
 {
        struct mailbox *box = ctx->backend->box;
        const struct fts_backend_lookup_field *fields;
@@ -268,7 +269,8 @@ static int fts_backend_solr_lookup(struct fts_backend_lookup_context *ctx,
        mailbox_get_status(box, STATUS_UIDVALIDITY, &status);
 
        str = t_str_new(256);
-       str_printfa(str, "fl=uid&rows=%u&q=", status.uidnext);
+       str_printfa(str, "fl=uid,score&rows=%u&sort=uid%%20asc&q=",
+                   status.uidnext);
 
        /* build a lucene search query from the fields */
        fields = array_get(&ctx->fields, &count);
@@ -301,7 +303,8 @@ static int fts_backend_solr_lookup(struct fts_backend_lookup_context *ctx,
        solr_quote_str(str, box->storage->user);
 
        array_clear(maybe_uids);
-       return solr_connection_select(solr_conn, str_c(str), definite_uids);
+       return solr_connection_select(solr_conn, str_c(str),
+                                     definite_uids, scores);
 }
 
 struct fts_backend fts_backend_solr = {
index 5f452c7d6a37ded2a7f3700bfa19608864a06041..9bff0e605103599780700df0e4cb5eb489de352b 100644 (file)
@@ -3,6 +3,7 @@
 /* curl: 7.16.0 curl_multi_timeout */
 
 #include "lib.h"
+#include "array.h"
 #include "str.h"
 #include "strescape.h"
 #include "solr-connection.h"
@@ -29,7 +30,11 @@ struct solr_lookup_xml_context {
        enum solr_xml_content_state content_state;
        int depth;
 
+       uint32_t uid;
+       float score;
+
        ARRAY_TYPE(seq_range) *uids;
+       ARRAY_TYPE(fts_score_map) *scores;
 };
 
 struct solr_connection_post {
@@ -225,8 +230,11 @@ solr_lookup_xml_start(void *context, const char *name, const char **attrs)
                        ctx->state++;
                break;
        case SOLR_XML_RESPONSE_STATE_RESULT:
-               if (strcmp(name, "doc") == 0)
+               if (strcmp(name, "doc") == 0) {
                        ctx->state++;
+                       ctx->uid = 0;
+                       ctx->score = 0;
+               }
                break;
        case SOLR_XML_RESPONSE_STATE_DOC:
                name_attr = attrs_get_name(attrs);
@@ -243,6 +251,23 @@ solr_lookup_xml_start(void *context, const char *name, const char **attrs)
        }
 }
 
+static void solr_lookup_add_doc(struct solr_lookup_xml_context *ctx)
+{
+       struct fts_score_map *score;
+
+       if (ctx->uid == 0) {
+               i_error("fts_solr: missing uid");
+               return;
+       }
+
+       seq_range_array_add(ctx->uids, 0, ctx->uid);
+       if (ctx->scores != NULL && ctx->score != 0) {
+               score = array_append_space(ctx->scores);
+               score->uid = ctx->uid;
+               score->score = ctx->score;
+       }
+}
+
 static void solr_lookup_xml_end(void *context, const char *name ATTR_UNUSED)
 {
        struct solr_lookup_xml_context *ctx = context;
@@ -250,6 +275,8 @@ static void solr_lookup_xml_end(void *context, const char *name ATTR_UNUSED)
        i_assert(ctx->depth >= (int)ctx->state);
 
        if (ctx->depth == (int)ctx->state) {
+               if (ctx->state == SOLR_XML_RESPONSE_STATE_DOC)
+                       solr_lookup_add_doc(ctx);
                ctx->state--;
                ctx->content_state = SOLR_XML_CONTENT_STATE_NONE;
        }
@@ -275,16 +302,19 @@ static void solr_lookup_xml_data(void *context, const char *str, int len)
                        i_error("fts_solr: received invalid uid");
                        break;
                }
-               seq_range_array_add(ctx->uids, 0, uid);
+               ctx->uid = uid;
                break;
        case SOLR_XML_CONTENT_STATE_SCORE:
-               /* FIXME */
+               T_BEGIN {
+                       ctx->score = strtod(t_strndup(str, len), NULL);
+               } T_END;
                break;
        }
 }
 
 int solr_connection_select(struct solr_connection *conn, const char *query,
-                          ARRAY_TYPE(seq_range) *uids)
+                          ARRAY_TYPE(seq_range) *uids,
+                          ARRAY_TYPE(fts_score_map) *scores)
 {
        struct solr_lookup_xml_context solr_lookup_context;
        string_t *str;
@@ -295,6 +325,7 @@ int solr_connection_select(struct solr_connection *conn, const char *query,
 
        memset(&solr_lookup_context, 0, sizeof(solr_lookup_context));
        solr_lookup_context.uids = uids;
+       solr_lookup_context.scores = scores;
 
        i_free_and_null(conn->http_failure);
        conn->xml_failed = FALSE;
index f86449bcf44868700d51c34375f9e7982191eeb4..c9eda72e99617818036a6cbfcaa73be35ece7eff 100644 (file)
@@ -2,6 +2,7 @@
 #define SOLR_CONNECTION_H
 
 #include "seq-range-array.h"
+#include "fts-api.h"
 
 struct solr_connection *solr_connection_init(const char *url, bool debug);
 void solr_connection_deinit(struct solr_connection *conn);
@@ -10,7 +11,8 @@ void solr_connection_quote_str(struct solr_connection *conn, string_t *dest,
                               const char *str);
 
 int solr_connection_select(struct solr_connection *conn, const char *query,
-                          ARRAY_TYPE(seq_range) *uids);
+                          ARRAY_TYPE(seq_range) *uids,
+                          ARRAY_TYPE(fts_score_map) *scores);
 int solr_connection_post(struct solr_connection *conn, const char *cmd);
 
 struct solr_connection_post *
index 3b82ab4472b27e1bfcb8d0f049d33b175607772c..974ef4b79b078bbe8fca1e0bd8c99a517e6192a8 100644 (file)
@@ -33,7 +33,8 @@ struct fts_backend_vfuncs {
 
        int (*lookup2)(struct fts_backend_lookup_context *ctx,
                       ARRAY_TYPE(seq_range) *definite_uids,
-                      ARRAY_TYPE(seq_range) *maybe_uids);
+                      ARRAY_TYPE(seq_range) *maybe_uids,
+                      ARRAY_TYPE(fts_score_map) *scores);
 };
 
 enum fts_backend_flags {
index 91a052f0939e33ede364d5ba522ea070bb553846..814e1b53cda8ada2dd327b92e6b141a65f9d7085 100644 (file)
@@ -298,16 +298,20 @@ static int fts_backend_lookup_old(struct fts_backend_lookup_context *ctx,
 
 int fts_backend_lookup_deinit(struct fts_backend_lookup_context **_ctx,
                              ARRAY_TYPE(seq_range) *definite_uids,
-                             ARRAY_TYPE(seq_range) *maybe_uids)
+                             ARRAY_TYPE(seq_range) *maybe_uids,
+                             ARRAY_TYPE(fts_score_map) *scores)
 {
        struct fts_backend_lookup_context *ctx = *_ctx;
        int ret;
 
        *_ctx = NULL;
-       if (ctx->backend->v.lookup2 != NULL)
-               ret = ctx->backend->v.lookup2(ctx, definite_uids, maybe_uids);
-       else
+       if (ctx->backend->v.lookup2 != NULL) {
+               ret = ctx->backend->v.lookup2(ctx, definite_uids, maybe_uids,
+                                             scores);
+       } else {
+               array_clear(scores);
                ret = fts_backend_lookup_old(ctx, definite_uids, maybe_uids);
+       }
        pool_unref(&ctx->pool);
        return ret;
 }
index 196c467dd6cc83fe31bd97b7f7f8459ea1db6f7b..9882ac15f255ea71236a130304e6cc25ae4a4791 100644 (file)
@@ -13,6 +13,12 @@ enum fts_lookup_flags {
        FTS_LOOKUP_FLAG_INVERT  = 0x04
 };
 
+struct fts_score_map {
+       uint32_t uid;
+       float score;
+};
+ARRAY_DEFINE_TYPE(fts_score_map, struct fts_score_map);
+
 struct fts_backend *
 fts_backend_init(const char *backend_name, struct mailbox *box);
 void fts_backend_deinit(struct fts_backend **backend);
@@ -61,6 +67,7 @@ void fts_backend_lookup_add(struct fts_backend_lookup_context *ctx,
 /* Finish the lookup and return found UIDs. */
 int fts_backend_lookup_deinit(struct fts_backend_lookup_context **ctx,
                              ARRAY_TYPE(seq_range) *definite_uids,
-                             ARRAY_TYPE(seq_range) *maybe_uids);
+                             ARRAY_TYPE(seq_range) *maybe_uids,
+                             ARRAY_TYPE(fts_score_map) *scores);
 
 #endif
index 00c3f6eb8205e2127a11513c055374771a3cfbe2..1c7d7ff9d1d9ca55b5d5d0bb86986d7444a27b3c 100644 (file)
@@ -121,6 +121,7 @@ void fts_search_lookup(struct fts_search_context *fctx)
 
        i_array_init(&fctx->definite_seqs, 64);
        i_array_init(&fctx->maybe_seqs, 64);
+       i_array_init(&fctx->score_map, 64);
 
        /* start lookup with the best arg */
        T_BEGIN {
@@ -141,7 +142,8 @@ void fts_search_lookup(struct fts_search_context *fctx)
                        have_seqs = TRUE;
                        fts_backend_lookup_deinit(&fctx->lookup_ctx_fast,
                                                  &fctx->definite_seqs,
-                                                 &fctx->maybe_seqs);
+                                                 &fctx->maybe_seqs,
+                                                 &fctx->score_map);
                }
                if (fctx->fbox->backend_fast->locked)
                        fts_backend_unlock(fctx->fbox->backend_fast);
@@ -152,19 +154,26 @@ void fts_search_lookup(struct fts_search_context *fctx)
                } else if (!have_seqs) {
                        fts_backend_lookup_deinit(&fctx->lookup_ctx_substr,
                                                  &fctx->definite_seqs,
-                                                 &fctx->maybe_seqs);
+                                                 &fctx->maybe_seqs,
+                                                 &fctx->score_map);
                } else {
                        /* have to merge the results */
                        ARRAY_TYPE(seq_range) tmp_def, tmp_maybe;
+                       ARRAY_TYPE(fts_score_map) tmp_scores;
 
                        i_array_init(&tmp_def, 64);
                        i_array_init(&tmp_maybe, 64);
+                       i_array_init(&tmp_scores, 64);
+                       /* FIXME: for now we just ignore the other scores,
+                          since squat doesn't support it anyway */
                        fts_backend_lookup_deinit(&fctx->lookup_ctx_substr,
-                                                 &tmp_def, &tmp_maybe);
+                                                 &tmp_def, &tmp_maybe,
+                                                 &tmp_scores);
                        fts_filter_uids(&fctx->definite_seqs, &tmp_def,
                                        &fctx->maybe_seqs, &tmp_maybe);
                        array_free(&tmp_def);
                        array_free(&tmp_maybe);
+                       array_free(&tmp_scores);
                }
                if (fctx->fbox->backend_substr->locked)
                        fts_backend_unlock(fctx->fbox->backend_substr);
index 4a2142ce0d209c43d3e99730d37d0e41ee79f338..50ae01fb65cd986a73cc3fa397f59661a5379ade 100644 (file)
 #define FTS_SEARCH_NONBLOCK_COUNT 10
 #define FTS_BUILD_NOTIFY_INTERVAL_SECS 10
 
+struct fts_mail {
+       union mail_module_context module_ctx;
+       char score[30];
+};
+
 struct fts_storage_build_context {
        struct mail_search_context *search_ctx;
        struct mail_search_args *search_args;
@@ -39,6 +44,7 @@ struct fts_transaction_context {
        union mailbox_transaction_module_context module_ctx;
 
        struct fts_storage_build_context *build_ctx;
+       ARRAY_TYPE(fts_score_map) *score_map;
        struct mail *mail;
 
        uint32_t last_uid;
@@ -334,6 +340,7 @@ fts_mailbox_search_init(struct mailbox_transaction_context *t,
                        struct mail_search_args *args,
                        const enum mail_sort_type *sort_program)
 {
+       struct fts_transaction_context *ft = FTS_CONTEXT(t);
        struct fts_mailbox *fbox = FTS_CONTEXT(t->box);
        struct mail_search_context *ctx;
        struct fts_search_context *fctx;
@@ -349,6 +356,8 @@ fts_mailbox_search_init(struct mailbox_transaction_context *t,
        if (fbox->backend_substr == NULL && fbox->backend_fast == NULL)
                return ctx;
 
+       ft->score_map = &fctx->score_map;
+
        fts_search_analyze(fctx);
        (void)fts_try_build_init(fctx);
        return ctx;
@@ -486,9 +495,13 @@ static int fts_mailbox_search_next_update_seq(struct mail_search_context *ctx)
 
 static int fts_mailbox_search_deinit(struct mail_search_context *ctx)
 {
+       struct fts_transaction_context *ft = FTS_CONTEXT(ctx->transaction);
        struct fts_mailbox *fbox = FTS_CONTEXT(ctx->transaction->box);
        struct fts_search_context *fctx = FTS_CONTEXT(ctx);
 
+       if (ft->score_map == &fctx->score_map)
+               ft->score_map = NULL;
+
        if (fctx->build_ctx != NULL) {
                /* the search was cancelled */
                fts_build_deinit(&fctx->build_ctx);
@@ -498,6 +511,8 @@ static int fts_mailbox_search_deinit(struct mail_search_context *ctx)
                array_free(&fctx->definite_seqs);
        if (array_is_created(&fctx->maybe_seqs))
                array_free(&fctx->maybe_seqs);
+       if (array_is_created(&fctx->score_map))
+               array_free(&fctx->score_map);
        i_free(fctx);
        return fbox->module_ctx.super.search_deinit(ctx);
 }
@@ -505,7 +520,7 @@ static int fts_mailbox_search_deinit(struct mail_search_context *ctx)
 static void fts_mail_expunge(struct mail *_mail)
 {
        struct mail_private *mail = (struct mail_private *)_mail;
-       union mail_module_context *fmail = FTS_MAIL_CONTEXT(mail);
+       struct fts_mail *fmail = FTS_MAIL_CONTEXT(mail);
        struct fts_mailbox *fbox = FTS_CONTEXT(_mail->box);
        struct fts_transaction_context *ft = FTS_CONTEXT(_mail->transaction);
 
@@ -515,7 +530,44 @@ static void fts_mail_expunge(struct mail *_mail)
        if (fbox->backend_fast != NULL)
                fts_backend_expunge(fbox->backend_fast, _mail);
 
-       fmail->super.expunge(_mail);
+       fmail->module_ctx.super.expunge(_mail);
+}
+
+static int fts_score_cmp(const void *key, const void *data)
+{
+       const uint32_t *uid = key;
+       const struct fts_score_map *score = data;
+
+       return *uid < score->uid ? -1 :
+               (*uid > score->uid ? 1 : 0);
+}
+
+static int fts_mail_get_special(struct mail *_mail, enum mail_fetch_field field,
+                               const char **value_r)
+{
+       struct mail_private *mail = (struct mail_private *)_mail;
+       struct fts_mail *fmail = FTS_MAIL_CONTEXT(mail);
+       struct fts_transaction_context *ft = FTS_CONTEXT(_mail->transaction);
+       const struct fts_score_map *scores;
+       unsigned int count;
+
+       if (field != MAIL_FETCH_SEARCH_SCORE || ft->score_map == NULL ||
+           !array_is_created(ft->score_map))
+               scores = NULL;
+       else {
+               scores = array_get(ft->score_map, &count);
+               scores = bsearch(&_mail->uid, scores, count, sizeof(*scores),
+                                fts_score_cmp);
+       }
+       if (scores != NULL) {
+               i_assert(scores->uid == _mail->uid);
+               i_snprintf(fmail->score, sizeof(fmail->score),
+                          "%f", scores->score);
+               *value_r = fmail->score;
+               return 0;
+       }
+
+       return fmail->module_ctx.super.get_special(_mail, field, value_r);
 }
 
 static struct mail *
@@ -524,7 +576,7 @@ fts_mail_alloc(struct mailbox_transaction_context *t,
               struct mailbox_header_lookup_ctx *wanted_headers)
 {
        struct fts_mailbox *fbox = FTS_CONTEXT(t->box);
-       union mail_module_context *fmail;
+       struct fts_mail *fmail;
        struct mail *_mail;
        struct mail_private *mail;
 
@@ -533,11 +585,12 @@ fts_mail_alloc(struct mailbox_transaction_context *t,
        if (fbox->backend_substr != NULL || fbox->backend_fast != NULL) {
                mail = (struct mail_private *)_mail;
 
-               fmail = p_new(mail->pool, union mail_module_context, 1);
-               fmail->super = mail->v;
+               fmail = p_new(mail->pool, struct fts_mail, 1);
+               fmail->module_ctx.super = mail->v;
 
                mail->v.expunge = fts_mail_expunge;
-               MODULE_CONTEXT_SET_SELF(mail, fts_mail_module, fmail);
+               mail->v.get_special = fts_mail_get_special;
+               MODULE_CONTEXT_SET(mail, fts_mail_module, fmail);
        }
        return _mail;
 }
index 5dec96fd73577ad64ff577db4811043f6c953b12..29372fcf20504eaf64b4a3599677fa4a59eecd20 100644 (file)
@@ -20,6 +20,7 @@ struct fts_search_context {
 
        struct fts_backend_lookup_context *lookup_ctx_substr, *lookup_ctx_fast;
        ARRAY_TYPE(seq_range) definite_seqs, maybe_seqs;
+       ARRAY_TYPE(fts_score_map) score_map;
        unsigned int definite_idx, maybe_idx;
 
        struct fts_backend *build_backend;