Show command may re-search for targets that support it
[pazpar2-moved-to-github.git] / src / session.c
index fc021a8..684cd8e 100644 (file)
@@ -111,15 +111,17 @@ static int session_use(int delta)
         no_session_total += delta;
     sessions = no_sessions;
     yaz_mutex_leave(g_session_mutex);
-    yaz_log(YLOG_DEBUG, "%s sesions=%d", delta == 0 ? "" : (delta > 0 ? "INC" : "DEC"), no_sessions);
+    yaz_log(YLOG_DEBUG, "%s sessions=%d", delta == 0 ? "" : (delta > 0 ? "INC" : "DEC"), no_sessions);
     return sessions;
 }
 
-int sessions_count(void) {
+int sessions_count(void)
+{
     return session_use(0);
 }
 
-int  session_count_total(void) {
+int session_count_total(void)
+{
     int total = 0;
     if (!g_session_mutex)
         return 0;
@@ -129,7 +131,6 @@ int  session_count_total(void) {
     return total;
 }
 
-
 static void log_xml_doc(xmlDoc *doc)
 {
     FILE *lf = yaz_log_file();
@@ -158,33 +159,6 @@ static void session_leave(struct session *s)
     yaz_mutex_leave(s->session_mutex);
 }
 
-// Recursively traverse query structure to extract terms.
-void pull_terms(NMEM nmem, struct ccl_rpn_node *n, char **termlist, int *num)
-{
-    char **words;
-    int numwords;
-    int i;
-
-    switch (n->kind)
-    {
-    case CCL_RPN_AND:
-    case CCL_RPN_OR:
-    case CCL_RPN_NOT:
-    case CCL_RPN_PROX:
-        pull_terms(nmem, n->u.p[0], termlist, num);
-        pull_terms(nmem, n->u.p[1], termlist, num);
-        break;
-    case CCL_RPN_TERM:
-        nmem_strsplit(nmem, " ", n->u.t.term, &words, &numwords);
-        for (i = 0; i < numwords; i++)
-            termlist[(*num)++] = words[i];
-        break;
-    default: // NOOP
-        break;
-    }
-}
-
-
 void add_facet(struct session *s, const char *type, const char *value, int count)
 {
     struct conf_service *service = s->service;
@@ -198,8 +172,6 @@ void add_facet(struct session *s, const char *type, const char *value, int count
     for (i = 0; i < service->num_metadata; i++)
         if (!strcmp((service->metadata + i)->name, type))
             icu_chain_id = (service->metadata + i)->facetrule;
-    yaz_log(YLOG_LOG, "icu_chain id=%s", icu_chain_id ? icu_chain_id : "null");
-
     if (!icu_chain_id)
         icu_chain_id = "facet";
     prt = pp2_charset_token_create(service->charsets, icu_chain_id);
@@ -231,8 +203,6 @@ void add_facet(struct session *s, const char *type, const char *value, int count
     }
     pp2_charset_token_destroy(prt);
  
-    yaz_log(YLOG_LOG, "facet norm=%s", wrbuf_cstr(facet_wrbuf));
-    yaz_log(YLOG_LOG, "facet display=%s", wrbuf_cstr(display_wrbuf));
     if (wrbuf_len(facet_wrbuf))
     {
         int i;
@@ -276,13 +246,13 @@ static xmlDoc *record_to_xml(struct session *se,
     if (!rdoc)
     {
         session_log(se, YLOG_FATAL, "Non-wellformed XML received from %s",
-                    db->url);
+                    db->id);
         return 0;
     }
 
     if (global_parameters.dump_records)
     {
-        session_log(se, YLOG_LOG, "Un-normalized record from %s", db->url);
+        session_log(se, YLOG_LOG, "Un-normalized record from %s", db->id);
         log_xml_doc(rdoc);
     }
 
@@ -370,7 +340,7 @@ static xmlDoc *normalize_record(struct session *se,
         if (normalize_record_transform(sdb->map, &rdoc, (const char **)parms))
         {
             session_log(se, YLOG_WARN, "Normalize failed from %s",
-                        sdb->database->url);
+                        sdb->database->id);
         }
         else
         {
@@ -379,7 +349,7 @@ static xmlDoc *normalize_record(struct session *se,
             if (global_parameters.dump_records)
             {
                 session_log(se, YLOG_LOG, "Normalized record from %s", 
-                            sdb->database->url);
+                            sdb->database->id);
                 log_xml_doc(rdoc);
             }
         }
@@ -431,7 +401,7 @@ static int prepare_map(struct session *se, struct session_database *sdb)
 
     if (!sdb->settings)
     {
-        session_log(se, YLOG_WARN, "No settings on %s", sdb->database->url);
+        session_log(se, YLOG_WARN, "No settings on %s", sdb->database->id);
         return -1;
     }
     if ((s = session_setting_oneval(sdb, PZ_XSLT)))
@@ -477,7 +447,7 @@ static int prepare_session_database(struct session *se,
     if (!sdb->settings)
     {
         session_log(se, YLOG_WARN, 
-                "No settings associated with %s", sdb->database->url);
+                "No settings associated with %s", sdb->database->id);
         return -1;
     }
     if (sdb->settings[PZ_XSLT] && !sdb->map)
@@ -551,11 +521,12 @@ void session_alert_watch(struct session *s, int what)
 }
 
 //callback for grep_databases
-static void select_targets_callback(void *context, struct session_database *db)
+static void select_targets_callback(struct session *se,
+                                    struct session_database *db)
 {
-    struct session *se = (struct session*) context;
-    struct client *cl = client_create();
+    struct client *cl = client_create(db->database->id);
     struct client_list *l;
+
     client_set_database(cl, db);
 
     client_set_session(cl, se);
@@ -620,6 +591,59 @@ int session_is_preferred_clients_ready(struct session *s)
     return res == 0;
 }
 
+void search_sort(struct session *se, const char *field, int increasing)
+{
+    struct client_list *l;
+    struct timeval tval;
+
+    session_enter(se);
+    for (l = se->clients; l; l = l->next)
+    {
+        struct client *cl = l->client;
+        struct session_database *sdb = client_get_database(cl);
+        struct setting *s;
+        const char *strategy_plus_sort = 0;
+        
+        for (s = sdb->settings[PZ_SORTMAP]; s; s = s->next)
+        {
+            char *p = strchr(s->name + 3, ':');
+            if (!p)
+            {
+                yaz_log(YLOG_WARN, "Malformed sortmap name: %s", s->name);
+                continue;
+            }
+            p++;
+            if (!strcmp(p, field))
+            {
+                strategy_plus_sort = s->value;
+                break;
+            }
+        }
+
+        if (strategy_plus_sort)
+        {
+            if (client_prep_connection(cl, se->service->z3950_operation_timeout,
+                                       se->service->z3950_session_timeout,
+                                       se->service->server->iochan_man,
+                                       &tval))
+            {
+                char **array;
+                int num;
+                nmem_strsplit(se->nmem, ":", strategy_plus_sort, &array, &num);
+
+                if (num == 2)
+                {
+                    const char *sort_spec = array[1];
+                    while (*sort_spec == ' ')
+                        sort_spec++;
+                    client_start_search(cl, array[0], sort_spec);
+                }
+            }
+        }
+    }
+    session_leave(se);
+}
+
 enum pazpar2_error_code search(struct session *se,
                                const char *query,
                                const char *startrecs, const char *maxrecs,
@@ -685,7 +709,7 @@ enum pazpar2_error_code search(struct session *se,
                                        se->service->z3950_session_timeout,
                                        se->service->server->iochan_man,
                                        &tval))
-                client_start_search(cl);
+                client_start_search(cl, 0, 0);
         }
     }
     facet_limits_destroy(facet_limits);
@@ -747,8 +771,6 @@ static struct session_database *load_session_database(struct session *se,
 {
     struct database *db = new_database(id, se->session_nmem);
 
-    resolve_database(se->service, db);
-
     session_init_databases_fun((void*) se, db);
 
     // New sdb is head of se->databases list
@@ -762,7 +784,7 @@ static struct session_database *find_session_database(struct session *se,
     struct session_database *sdb;
 
     for (sdb = se->databases; sdb; sdb = sdb->next)
-        if (!strcmp(sdb->database->url, id))
+        if (!strcmp(sdb->database->id, id))
             return sdb;
     return load_session_database(se, id);
 }
@@ -867,13 +889,13 @@ struct session *new_session(NMEM nmem, struct conf_service *service,
     return session;
 }
 
-struct hitsbytarget *hitsbytarget(struct session *se, int *count, NMEM nmem)
+static struct hitsbytarget *hitsbytarget_nb(struct session *se,
+                                            int *count, NMEM nmem)
 {
     struct hitsbytarget *res = 0;
     struct client_list *l;
     size_t sz = 0;
 
-    session_enter(se);
     for (l = se->clients; l; l = l->next)
         sz++;
 
@@ -886,7 +908,7 @@ struct hitsbytarget *hitsbytarget(struct session *se, int *count, NMEM nmem)
         const char *name = session_setting_oneval(client_get_database(cl),
                                                   PZ_NAME);
 
-        res[*count].id = client_get_database(cl)->database->url;
+        res[*count].id = client_get_id(cl);
         res[*count].name = *name ? name : "Unknown";
         res[*count].hits = client_get_hits(cl);
         res[*count].records = client_get_num_records(cl);
@@ -898,10 +920,18 @@ struct hitsbytarget *hitsbytarget(struct session *se, int *count, NMEM nmem)
         wrbuf_destroy(w);
         (*count)++;
     }
-    session_leave(se);
     return res;
 }
 
+struct hitsbytarget *get_hitsbytarget(struct session *se, int *count, NMEM nmem)
+{
+    struct hitsbytarget *p;
+    session_enter(se);
+    p = hitsbytarget_nb(se, count, nmem);
+    session_leave(se);
+    return p;
+}
+    
 struct termlist_score **get_termlist_score(struct session *se,
                                            const char *name, int *num)
 {
@@ -919,6 +949,118 @@ struct termlist_score **get_termlist_score(struct session *se,
     return tl;
 }
 
+// Compares two hitsbytarget nodes by hitcount
+static int cmp_ht(const void *p1, const void *p2)
+{
+    const struct hitsbytarget *h1 = p1;
+    const struct hitsbytarget *h2 = p2;
+    return h2->hits - h1->hits;
+}
+
+static int targets_termlist_nb(WRBUF wrbuf, struct session *se, int num,
+                               NMEM nmem)
+{
+    struct hitsbytarget *ht;
+    int count, i;
+
+    ht = hitsbytarget_nb(se, &count, nmem);
+    qsort(ht, count, sizeof(struct hitsbytarget), cmp_ht);
+    for (i = 0; i < count && i < num && ht[i].hits > 0; i++)
+    {
+
+        // do only print terms which have display names
+    
+        wrbuf_puts(wrbuf, "<term>\n");
+
+        wrbuf_puts(wrbuf, "<id>");
+        wrbuf_xmlputs(wrbuf, ht[i].id);
+        wrbuf_puts(wrbuf, "</id>\n");
+        
+        wrbuf_puts(wrbuf, "<name>");
+        if (!ht[i].name || !ht[i].name[0])
+            wrbuf_xmlputs(wrbuf, "NO TARGET NAME");
+        else
+            wrbuf_xmlputs(wrbuf, ht[i].name);
+        wrbuf_puts(wrbuf, "</name>\n");
+        
+        wrbuf_printf(wrbuf, "<frequency>" ODR_INT_PRINTF "</frequency>\n",
+                     ht[i].hits);
+        
+        wrbuf_puts(wrbuf, "<state>");
+        wrbuf_xmlputs(wrbuf, ht[i].state);
+        wrbuf_puts(wrbuf, "</state>\n");
+        
+        wrbuf_printf(wrbuf, "<diagnostic>%d</diagnostic>\n", 
+                     ht[i].diagnostic);
+        wrbuf_puts(wrbuf, "</term>\n");
+    }
+    return count;
+}
+
+void perform_termlist(struct http_channel *c, struct session *se,
+                      const char *name, int num)
+{
+    int i, j;
+    NMEM nmem_tmp = nmem_create();
+    char **names;
+    int num_names = 0;
+
+    if (name)
+        nmem_strsplit(nmem_tmp, ",", name, &names, &num_names);
+
+    session_enter(se);
+
+    for (j = 0; j < num_names; j++)
+    {
+        const char *tname;
+        for (i = 0; i < se->num_termlists; i++)
+        {
+            tname = se->termlists[i].name;
+            if (num_names > 0 && !strcmp(names[j], tname))
+            {
+                struct termlist_score **p = 0;
+                int len;
+                p = termlist_highscore(se->termlists[i].termlist, &len);
+                if (p)
+                {
+                    int i;
+                    wrbuf_puts(c->wrbuf, "<list name=\"");
+                    wrbuf_xmlputs(c->wrbuf, tname);
+                    wrbuf_puts(c->wrbuf, "\">\n");
+                    for (i = 0; i < len && i < num; i++)
+                    {
+                        // prevent sending empty term elements
+                        if (!p[i]->display_term || !p[i]->display_term[0])
+                            continue;
+                        
+                        wrbuf_puts(c->wrbuf, "<term>");
+                        wrbuf_puts(c->wrbuf, "<name>");
+                        wrbuf_xmlputs(c->wrbuf, p[i]->display_term);
+                        wrbuf_puts(c->wrbuf, "</name>");
+                        
+                        wrbuf_printf(c->wrbuf, 
+                                     "<frequency>%d</frequency>", 
+                                     p[i]->frequency);
+                        wrbuf_puts(c->wrbuf, "</term>\n");
+                    }
+                    wrbuf_puts(c->wrbuf, "</list>\n");
+                }
+            }
+        }
+        tname = "xtargets";
+        if (num_names > 0 && !strcmp(names[j], tname))
+        {
+            wrbuf_puts(c->wrbuf, "<list name=\"");
+            wrbuf_xmlputs(c->wrbuf, tname);
+            wrbuf_puts(c->wrbuf, "\">\n");
+            targets_termlist_nb(c->wrbuf, se, num, c->nmem);
+            wrbuf_puts(c->wrbuf, "</list>\n");
+        }
+    }
+    session_leave(se);
+    nmem_destroy(nmem_tmp);
+}
+
 #ifdef MISSING_HEADERS
 void report_nmem_stats(void)
 {
@@ -1216,7 +1358,7 @@ static const char *get_mergekey(xmlDoc *doc, struct client *cl, int record_no,
     if (wrbuf_len(norm_wr) == 0)
     {
         wrbuf_printf(norm_wr, "%s-%d",
-                     client_get_database(cl)->database->url, record_no);
+                     client_get_id(cl), record_no);
     }
     if (wrbuf_len(norm_wr) > 0)
         mergekey_norm = nmem_strdup(nmem, wrbuf_cstr(norm_wr));
@@ -1320,7 +1462,7 @@ int ingest_record(struct client *cl, const char *rec,
     if (!check_record_filter(root, sdb))
     {
         session_log(se, YLOG_LOG, "Filtered out record no %d from %s",
-                    record_no, sdb->database->url);
+                    record_no, sdb->database->id);
         xmlFreeDoc(xdoc);
         return -2;
     }
@@ -1380,7 +1522,7 @@ static int ingest_to_cluster(struct client *cl,
         return -1;
     if (global_parameters.dump_records)
         session_log(se, YLOG_LOG, "Cluster id %s from %s (#%d)", cluster->recid,
-                    sdb->database->url, record_no);
+                    sdb->database->id, record_no);
     relevance_newrec(se->relevance, cluster);
     
     // now parsing XML record and adding data to cluster or record metadata