X-Git-Url: http://jsfdemo.indexdata.com/?a=blobdiff_plain;f=src%2Frelevance.c;h=a002a7b631eec74f076d89b28821febe9e1f8e9c;hb=446f32183265d59ee79e2859376c598fa24408e0;hp=7603a7a928c2422941e9f5340fe89b55d266cf51;hpb=beda9709478c02182ceadb5f0526c32d8986c039;p=pazpar2-moved-to-github.git diff --git a/src/relevance.c b/src/relevance.c index 7603a7a..a002a7b 100644 --- a/src/relevance.c +++ b/src/relevance.c @@ -1,5 +1,5 @@ /* This file is part of Pazpar2. - Copyright (C) 2006-2009 Index Data + Copyright (C) 2006-2012 Index Data Pazpar2 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free @@ -21,119 +21,239 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include #endif +#include #include #include #include "relevance.h" -#include "pazpar2.h" +#include "session.h" struct relevance { int *doc_frequency_vec; + int *term_frequency_vec_tmp; int vec_len; struct word_entry *entries; - pp2_charset_t pct; + pp2_charset_token_t prt; + int rank_cluster; + int follow_boost; + int lead_boost; + int length_divide; NMEM nmem; }; - struct word_entry { const char *norm_str; + const char *display_str; int termno; + int follow_boost; + char *ccl_field; struct word_entry *next; }; -static void add_word_entry(NMEM nmem, - struct word_entry **entries, - const char *norm_str, - int term_no) +static int word_entry_match(struct relevance *r, const char *norm_str, + const char *rank, int *mult) { - struct word_entry *ne = nmem_malloc(nmem, sizeof(*ne)); - ne->norm_str = nmem_strdup(nmem, norm_str); - ne->termno = term_no; - - ne->next = *entries; - *entries = ne; -} - - -int word_entry_match(struct word_entry *entries, const char *norm_str) -{ - for (; entries; entries = entries->next) + int i = 1; + struct word_entry *entries = r->entries; + for (; entries; entries = entries->next, i++) { - if (!strcmp(norm_str, entries->norm_str)) + if (*norm_str && !strcmp(norm_str, entries->norm_str)) + { + int extra = r->follow_boost; + struct word_entry *e_follow = entries; + const char *cp = 0; + int no_read = 0; + sscanf(rank, "%d%n", mult, &no_read); + rank += no_read; + while (*rank == ' ') + rank++; + if (no_read > 0 && (cp = strchr(rank, ' '))) + { + if ((cp - rank) == strlen(entries->ccl_field) && + memcmp(entries->ccl_field, rank, cp - rank) == 0) + *mult = atoi(cp + 1); + } + (*mult) += entries->follow_boost; + while ((e_follow = e_follow->next) != 0 && extra > 0) + { + e_follow->follow_boost = extra--; + } return entries->termno; + } + entries->follow_boost = 0; } return 0; } -static struct word_entry *build_word_entries(pp2_charset_t pct, NMEM nmem, - const char **terms) +void relevance_countwords(struct relevance *r, struct record_cluster *cluster, + const char *words, const char *rank, + const char *name) { - int termno = 1; /* >0 signals THERE is an entry */ - struct word_entry *entries = 0; - const char **p = terms; + int *mult = r->term_frequency_vec_tmp; + const char *norm_str; + int i, length = 0; + int lead_mult = r->lead_boost; + struct word_entry *e; + WRBUF w = cluster->relevance_explain1; - for (; *p; p++) + pp2_charset_token_first(r->prt, words, 0); + for (e = r->entries, i = 1; i < r->vec_len; i++, e = e->next) { - pp2_relevance_token_t prt = pp2_relevance_tokenize(pct, *p); - const char *norm_str; - - while ((norm_str = pp2_relevance_token_next(prt))) - add_word_entry(nmem, &entries, norm_str, termno); + mult[i] = 0; + e->follow_boost = 0; + } - pp2_relevance_token_destroy(prt); + assert(rank); + while ((norm_str = pp2_charset_token_next(r->prt))) + { + int local_mult = 0; + int res = word_entry_match(r, norm_str, rank, &local_mult); + if (res) + { + assert(res < r->vec_len); + mult[res] += local_mult + lead_mult; + } + if (lead_mult > 0) + --lead_mult; + length++; + } - termno++; + for (e = r->entries, i = 1; i < r->vec_len; i++, e = e->next) + { + if (length == 0 || mult[i] == 0) + continue; + wrbuf_printf(w, "%s: field=%s vecf[%d] += mult(%d)", + e->display_str, name, i, mult[i]); + switch (r->length_divide) + { + case 0: + wrbuf_printf(w, ";\n"); + cluster->term_frequency_vecf[i] += (double) mult[i]; + break; + case 1: + wrbuf_printf(w, " / log2(1+length(%d));\n", length); + cluster->term_frequency_vecf[i] += + (double) mult[i] / log2(1 + length); + break; + case 2: + wrbuf_printf(w, " / length(%d);\n", length); + cluster->term_frequency_vecf[i] += (double) mult[i] / length; + } + cluster->term_frequency_vec[i] += mult[i]; } - return entries; + + cluster->term_frequency_vec[0] += length; } -void relevance_countwords(struct relevance *r, struct record_cluster *cluster, - const char *words, int multiplier) +static void pull_terms(struct relevance *res, struct ccl_rpn_node *n) { - pp2_relevance_token_t prt = pp2_relevance_tokenize(r->pct, words); - - const char *norm_str; - - while ((norm_str = pp2_relevance_token_next(prt))) + char **words; + int numwords; + char *ccl_field; + int i; + + switch (n->kind) { - int res = word_entry_match(r->entries, norm_str); - if (res) - cluster->term_frequency_vec[res] += multiplier; - cluster->term_frequency_vec[0]++; + case CCL_RPN_AND: + case CCL_RPN_OR: + case CCL_RPN_NOT: + case CCL_RPN_PROX: + pull_terms(res, n->u.p[0]); + pull_terms(res, n->u.p[1]); + break; + case CCL_RPN_TERM: + nmem_strsplit(res->nmem, " ", n->u.t.term, &words, &numwords); + for (i = 0; i < numwords; i++) + { + const char *norm_str; + + ccl_field = nmem_strdup_null(res->nmem, n->u.t.qual); + + pp2_charset_token_first(res->prt, words[i], 0); + while ((norm_str = pp2_charset_token_next(res->prt))) + { + struct word_entry **e = &res->entries; + while (*e) + e = &(*e)->next; + *e = nmem_malloc(res->nmem, sizeof(**e)); + (*e)->norm_str = nmem_strdup(res->nmem, norm_str); + (*e)->ccl_field = ccl_field; + (*e)->termno = res->vec_len++; + (*e)->display_str = nmem_strdup(res->nmem, words[i]); + (*e)->next = 0; + } + } + break; + default: + break; } - pp2_relevance_token_destroy(prt); } -struct relevance *relevance_create(pp2_charset_t pct, - NMEM nmem, const char **terms) +struct relevance *relevance_create_ccl(pp2_charset_fact_t pft, + struct ccl_rpn_node *query, + int rank_cluster, + int follow_boost, int lead_boost, + int length_divide) { - struct relevance *res = nmem_malloc(nmem, sizeof(struct relevance)); - const char **p; + NMEM nmem = nmem_create(); + struct relevance *res = nmem_malloc(nmem, sizeof(*res)); int i; - for (p = terms, i = 0; *p; p++, i++) - ; - res->vec_len = ++i; - res->doc_frequency_vec = nmem_malloc(nmem, res->vec_len * sizeof(int)); - memset(res->doc_frequency_vec, 0, res->vec_len * sizeof(int)); res->nmem = nmem; - res->entries = build_word_entries(pct, nmem, terms); - res->pct = pct; + res->entries = 0; + res->vec_len = 1; + res->rank_cluster = rank_cluster; + res->follow_boost = follow_boost; + res->lead_boost = lead_boost; + res->length_divide = length_divide; + res->prt = pp2_charset_token_create(pft, "relevance"); + + pull_terms(res, query); + + res->doc_frequency_vec = nmem_malloc(nmem, res->vec_len * sizeof(int)); + for (i = 0; i < res->vec_len; i++) + res->doc_frequency_vec[i] = 0; + + // worker array + res->term_frequency_vec_tmp = + nmem_malloc(res->nmem, + res->vec_len * sizeof(*res->term_frequency_vec_tmp)); return res; } +void relevance_destroy(struct relevance **rp) +{ + if (*rp) + { + pp2_charset_token_destroy((*rp)->prt); + nmem_destroy((*rp)->nmem); + *rp = 0; + } +} + void relevance_newrec(struct relevance *r, struct record_cluster *rec) { if (!rec->term_frequency_vec) { - rec->term_frequency_vec = nmem_malloc(r->nmem, r->vec_len * sizeof(int)); - memset(rec->term_frequency_vec, 0, r->vec_len * sizeof(int)); + int i; + + // term frequency [1,..] . [0] is total length of all fields + rec->term_frequency_vec = + nmem_malloc(r->nmem, + r->vec_len * sizeof(*rec->term_frequency_vec)); + for (i = 0; i < r->vec_len; i++) + rec->term_frequency_vec[i] = 0; + + // term frequency divided by length of field [1,...] + rec->term_frequency_vecf = + nmem_malloc(r->nmem, + r->vec_len * sizeof(*rec->term_frequency_vecf)); + for (i = 0; i < r->vec_len; i++) + rec->term_frequency_vecf[i] = 0.0; } } - void relevance_donerecord(struct relevance *r, struct record_cluster *cluster) { int i; @@ -151,7 +271,7 @@ void relevance_prepare_read(struct relevance *rel, struct reclist *reclist) int i; float *idfvec = xmalloc(rel->vec_len * sizeof(float)); - reclist_rewind(reclist); + reclist_enter(reclist); // Calculate document frequency vector for each term. for (i = 1; i < rel->vec_len; i++) { @@ -159,37 +279,55 @@ void relevance_prepare_read(struct relevance *rel, struct reclist *reclist) idfvec[i] = 0; else { - // This conditional may be terribly wrong - // It was there to address the situation where vec[0] == vec[i] - // which leads to idfvec[i] == 0... not sure about this - // Traditional TF-IDF may assume that a word that occurs in every - // record is irrelevant, but this is actually something we will - // see a lot - if ((idfvec[i] = log((float) rel->doc_frequency_vec[0] / - rel->doc_frequency_vec[i])) < 0.0000001) - idfvec[i] = 1; + /* add one to nominator idf(t,D) to ensure a value > 0 */ + idfvec[i] = log((float) (1 + rel->doc_frequency_vec[0]) / + rel->doc_frequency_vec[i]); } } // Calculate relevance for each document - while (1) { - int t; int relevance = 0; + WRBUF w; + struct word_entry *e = rel->entries; struct record_cluster *rec = reclist_read_record(reclist); if (!rec) break; - for (t = 1; t < rel->vec_len; t++) + w = rec->relevance_explain2; + wrbuf_rewind(w); + for (i = 1; i < rel->vec_len; i++) + { + float termfreq = (float) rec->term_frequency_vecf[i]; + int add = 100000 * termfreq * idfvec[i]; + + wrbuf_printf(w, "idf[%d] = log(((1 + total(%d))/termoccur(%d));\n", + i, rel->doc_frequency_vec[0], + rel->doc_frequency_vec[i]); + wrbuf_printf(w, "%s: relevance += 100000 * vecf[%d](%f) * " + "idf[%d](%f) (%d);\n", + e->display_str, i, termfreq, i, idfvec[i], add); + relevance += add; + e = e->next; + } + if (!rel->rank_cluster) + { + struct record *record; + int cluster_size = 0; + + for (record = rec->records; record; record = record->next) + cluster_size++; + + wrbuf_printf(w, "score = relevance(%d)/cluster_size(%d);\n", + relevance, cluster_size); + relevance /= cluster_size; + } + else { - float termfreq; - if (!rec->term_frequency_vec[0]) - break; - termfreq = (float) rec->term_frequency_vec[t] / rec->term_frequency_vec[0]; - relevance += 100000 * (termfreq * idfvec[t] + 0.0000005); + wrbuf_printf(w, "score = relevance(%d);\n", relevance); } - rec->relevance = relevance; + rec->relevance_score = relevance; } - reclist_rewind(reclist); + reclist_leave(reclist); xfree(idfvec); }