Use multiple meta-data elements.
[pazpar2-moved-to-github.git] / src / logic.c
index 8abdcae..4f811a4 100644 (file)
@@ -1,5 +1,5 @@
 /* This file is part of Pazpar2.
-   Copyright (C) 2006-2008 Index Data
+   Copyright (C) 2006-2009 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,6 +21,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     \brief high-level logic; mostly user sessions and settings
 */
 
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -30,12 +34,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 #if HAVE_UNISTD_H
 #include <unistd.h>
 #endif
-#if HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
-#if HAVE_NETDB_H
-#include <netdb.h>
-#endif
 #include <signal.h>
 #include <ctype.h>
 #include <assert.h>
@@ -54,18 +52,11 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 #include <yaz/oid_db.h>
 #include <yaz/snprintf.h>
 
-#if HAVE_CONFIG_H
-#include <config.h>
-#endif
-
 #define USE_TIMING 0
 #if USE_TIMING
 #include <yaz/timing.h>
 #endif
 
-#if HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
 
 #include "pazpar2.h"
 #include "eventl.h"
@@ -104,6 +95,24 @@ struct parameters global_parameters =
     15   // Connect timeout
 };
 
+static void log_xml_doc(xmlDoc *doc)
+{
+    FILE *lf = yaz_log_file();
+    xmlChar *result = 0;
+    int len = 0;
+#if LIBXML_VERSION >= 20600
+    xmlDocDumpFormatMemory(doc, &result, &len, 1);
+#else
+    xmlDocDumpMemory(doc, &result, &len);
+#endif
+    if (lf && len)
+    {
+        fwrite(result, 1, len, lf);
+        fprintf(lf, "\n");
+    }
+    xmlFree(result);
+}
+
 // Recursively traverse query structure to extract terms.
 void pull_terms(NMEM nmem, struct ccl_rpn_node *n, char **termlist, int *num)
 {
@@ -158,123 +167,26 @@ static void add_facet(struct session *s, const char *type, const char *value)
     termlist_insert(s->termlists[i].termlist, value);
 }
 
-xmlDoc *record_to_xml(struct session_database *sdb, Z_External *rec)
+xmlDoc *record_to_xml(struct session_database *sdb, const char *rec)
 {
     struct database *db = sdb->database;
     xmlDoc *rdoc = 0;
-    const Odr_oid *oid = rec->direct_reference;
 
-    /* convert response record to XML somehow */
-    if (rec->which == Z_External_octet && oid
-        && !oid_oidcmp(oid, yaz_oid_recsyn_xml))
-    {
-        /* xml already */
-        rdoc = xmlParseMemory((char*) rec->u.octet_aligned->buf,
-                              rec->u.octet_aligned->len);
-        if (!rdoc)
-        {
-            yaz_log(YLOG_FATAL, "Non-wellformed XML received from %s",
-                    db->url);
-            return 0;
-        }
-    }
-    else if (rec->which == Z_External_OPAC)
-    {
-        if (!sdb->yaz_marc)
-        {
-            yaz_log(YLOG_WARN, "MARC decoding not configured");
-            return 0;
-        }
-        else
-        {
-            /* OPAC gets converted to XML too */
-            WRBUF wrbuf_opac = wrbuf_alloc();
-            /* MARCXML inside the OPAC XML. Charset is in effect because we
-               use the yaz_marc handle */
-            yaz_marc_xml(sdb->yaz_marc, YAZ_MARC_MARCXML);
-            yaz_opac_decode_wrbuf(sdb->yaz_marc, rec->u.opac, wrbuf_opac);
-            
-            rdoc = xmlParseMemory((char*) wrbuf_buf(wrbuf_opac),
-                                  wrbuf_len(wrbuf_opac));
-            if (!rdoc)
-            {
-                yaz_log(YLOG_WARN, "Unable to parse OPAC XML");
-                /* Was used to debug bug #1348 */
-#if 0
-                FILE *f = fopen("/tmp/opac.xml.txt", "wb");
-                if (f)
-                {
-                    fwrite(wrbuf_buf(wrbuf_opac), 1, wrbuf_len(wrbuf_opac), f);
-                    fclose(f);
-                }
-#endif
-            }
-            wrbuf_destroy(wrbuf_opac);
-        }
-    }
-    else if (oid && yaz_oid_is_iso2709(oid))
-    {
-        /* ISO2709 gets converted to MARCXML */
-        if (!sdb->yaz_marc)
-        {
-            yaz_log(YLOG_WARN, "MARC decoding not configured");
-            return 0;
-        }
-        else
-        {
-            xmlNode *res;
-            char *buf;
-            int len;
-            
-            if (rec->which != Z_External_octet)
-            {
-                yaz_log(YLOG_WARN, "Unexpected external branch, probably BER %s",
-                        db->url);
-                return 0;
-            }
-            buf = (char*) rec->u.octet_aligned->buf;
-            len = rec->u.octet_aligned->len;
-            if (yaz_marc_read_iso2709(sdb->yaz_marc, buf, len) < 0)
-            {
-                yaz_log(YLOG_WARN, "Failed to decode MARC %s", db->url);
-                return 0;
-            }
-            
-            if (yaz_marc_write_xml(sdb->yaz_marc, &res,
-                                   "http://www.loc.gov/MARC21/slim", 0, 0) < 0)
-            {
-                yaz_log(YLOG_WARN, "Failed to encode as XML %s",
-                        db->url);
-                return 0;
-            }
-            rdoc = xmlNewDoc((xmlChar *) "1.0");
-            xmlDocSetRootElement(rdoc, res);
-        }
-    }
-    else
+    rdoc = xmlParseMemory(rec, strlen(rec));
+
+    if (!rdoc)
     {
-        char oid_name_buf[OID_STR_MAX];
-        const char *oid_name = yaz_oid_to_string_buf(oid, 0, oid_name_buf);
-        yaz_log(YLOG_FATAL, 
-                "Unable to handle record of type %s from %s", 
-                oid_name, db->url);
+        yaz_log(YLOG_FATAL, "Non-wellformed XML received from %s",
+                db->url);
         return 0;
     }
-    
+
     if (global_parameters.dump_records)
     {
-        FILE *lf = yaz_log_file();
-        if (lf)
-        {
-            yaz_log(YLOG_LOG, "Un-normalized record from %s", db->url);
-#if LIBXML_VERSION >= 20600
-            xmlDocFormatDump(lf, rdoc, 1);
-#else
-            xmlDocDump(lf, rdoc);
-#endif
-            fprintf(lf, "\n");
-        }
+        yaz_log(YLOG_LOG, "Un-normalized record from %s", db->url);
+        log_xml_doc(rdoc);
     }
+
     return rdoc;
 }
 
@@ -343,7 +255,7 @@ static void insert_settings_values(struct session_database *sdb, xmlDoc *doc)
 }
 
 xmlDoc *normalize_record(struct session_database *sdb, struct session *se,
-                         Z_External *rec)
+                         const char *rec)
 {
     struct database_retrievalmap *m;
     xmlDoc *rdoc = record_to_xml(sdb, rec);
@@ -379,19 +291,9 @@ xmlDoc *normalize_record(struct session_database *sdb, struct session *se,
 
         if (global_parameters.dump_records)
         {
-            FILE *lf = yaz_log_file();
-            
-            if (lf)
-            {
-                yaz_log(YLOG_LOG, "Normalized record from %s", 
-                        sdb->database->url);
-#if LIBXML_VERSION >= 20600
-                xmlDocFormatDump(lf, rdoc, 1);
-#else
-                xmlDocDump(lf, rdoc);
-#endif
-                fprintf(lf, "\n");
-            }
+            yaz_log(YLOG_LOG, "Normalized record from %s", 
+                    sdb->database->url);
+            log_xml_doc(rdoc);
         }
     }
     return rdoc;
@@ -406,44 +308,6 @@ const char *session_setting_oneval(struct session_database *db, int offset)
     return db->settings[offset]->value;
 }
 
-
-
-// Initialize YAZ Map structures for MARC-based targets
-static int prepare_yazmarc(struct session_database *sdb)
-{
-    const char *s;
-
-    if (!sdb->settings)
-    {
-        yaz_log(YLOG_WARN, "No settings for %s", sdb->database->url);
-        return -1;
-    }
-    if ((s = session_setting_oneval(sdb, PZ_NATIVESYNTAX)) 
-        && !strncmp(s, "iso2709", 7))
-    {
-        char *encoding = "marc-8s", *e;
-        yaz_iconv_t cm;
-
-        // See if a native encoding is specified
-        if ((e = strchr(s, ';')))
-            encoding = e + 1;
-
-        sdb->yaz_marc = yaz_marc_create();
-        yaz_marc_subfield_str(sdb->yaz_marc, "\t");
-        
-        cm = yaz_iconv_open("utf-8", encoding);
-        if (!cm)
-        {
-            yaz_log(YLOG_FATAL, 
-                    "Unable to map from %s to UTF-8 for target %s", 
-                    encoding, sdb->database->url);
-            return -1;
-        }
-        yaz_marc_iconv(sdb->yaz_marc, cm);
-    }
-    return 0;
-}
-
 // Prepare XSLT stylesheets for record normalization
 // Structures are allocated on the session_wide nmem to avoid having
 // to recompute this for every search. This would lead
@@ -518,11 +382,6 @@ static int prepare_session_database(struct session *se,
                 "No settings associated with %s", sdb->database->url);
         return -1;
     }
-    if (sdb->settings[PZ_NATIVESYNTAX] && !sdb->yaz_marc)
-    {
-        if (prepare_yazmarc(sdb) < 0)
-            return -1;
-    }
     if (sdb->settings[PZ_XSLT] && !sdb->map)
     {
         if (prepare_map(se, sdb) < 0)
@@ -670,7 +529,6 @@ enum pazpar2_error_code search(struct session *se,
     se->reclist = 0;
     se->num_termlists = 0;
     criteria = parse_filter(se->nmem, filter);
-    se->requestid++;
     live_channels = select_targets(se, criteria);
     if (live_channels)
     {
@@ -694,7 +552,8 @@ enum pazpar2_error_code search(struct session *se,
         else
         {
             no_working++;
-            client_prep_connection(cl);
+            if (client_prep_connection(cl))
+                client_start_search(cl);
         }
     }
 
@@ -716,7 +575,6 @@ static void session_init_databases_fun(void *context, struct database *db)
     int i;
 
     new->database = db;
-    new->yaz_marc = 0;
     
     new->map = 0;
     new->settings 
@@ -739,8 +597,6 @@ static void session_database_destroy(struct session_database *sdb)
 
     for (m = sdb->map; m; m = m->next)
         xsltFreeStylesheet(m->stylesheet);
-    if (sdb->yaz_marc)
-        yaz_marc_destroy(sdb->yaz_marc);
 }
 
 // Initialize session_database list -- this represents this session's view
@@ -805,13 +661,6 @@ void session_apply_setting(struct session *se, char *dbname, char *setting,
     // (happens when a search starts and client connections are prepared)
     switch (offset)
     {
-    case PZ_NATIVESYNTAX:
-        if (sdb->yaz_marc)
-        {
-            yaz_marc_destroy(sdb->yaz_marc);
-            sdb->yaz_marc = 0;
-        }
-        break;
     case PZ_XSLT:
         if (sdb->map)
         {
@@ -847,9 +696,10 @@ struct session *new_session(NMEM nmem)
     session->relevance = 0;
     session->total_hits = 0;
     session->total_records = 0;
+    session->number_of_warnings_unknown_elements = 0;
+    session->number_of_warnings_unknown_metadata = 0;
     session->num_termlists = 0;
     session->reclist = 0;
-    session->requestid = -1;
     session->clients = 0;
     session->expected_maxrecs = 0;
     session->session_nmem = nmem;
@@ -1000,9 +850,7 @@ void statistics(struct session *se, struct statistics *stat)
         switch (client_get_state(cl))
         {
         case Client_Connecting: stat->num_connecting++; break;
-        case Client_Initializing: stat->num_initializing++; break;
-        case Client_Searching: stat->num_searching++; break;
-        case Client_Presenting: stat->num_presenting++; break;
+        case Client_Working: stat->num_working++; break;
         case Client_Idle: stat->num_idle++; break;
         case Client_Failed: stat->num_failed++; break;
         case Client_Error: stat->num_error++; break;
@@ -1016,7 +864,7 @@ void statistics(struct session *se, struct statistics *stat)
     stat->num_clients = count;
 }
 
-void start_http_listener(void)
+int start_http_listener(void)
 {
     char hp[128] = "";
     struct conf_server *ser = global_parameters.server;
@@ -1033,7 +881,7 @@ void start_http_listener(void)
             sprintf(hp + strlen(hp), "%d", ser->port);
         }
     }
-    http_init(hp);
+    return http_init(hp);
 }
 
 void start_proxy(void)
@@ -1103,7 +951,7 @@ static struct record_metadata *record_metadata_init(
     return rec_md;
 }
 
-struct record *ingest_record(struct client *cl, Z_External *rec,
+struct record *ingest_record(struct client *cl, const char *rec,
                              int record_no)
 {
     xmlDoc *xdoc = normalize_record(client_get_database(cl),
@@ -1172,125 +1020,131 @@ struct record *ingest_record(struct client *cl, Z_External *rec,
         return 0;
     }
     relevance_newrec(se->relevance, cluster);
-     
-     
-     // now parsing XML record and adding data to cluster or record metadata
-     for (n = root->children; n; n = n->next)
-     {
-         if (type)
-             xmlFree(type);
-         if (value)
-             xmlFree(value);
-         type = value = 0;
-
-         if (n->type != XML_ELEMENT_NODE)
-             continue;
-         if (!strcmp((const char *) n->name, "metadata"))
-         {
-             struct conf_metadata *ser_md = 0;
-             struct conf_sortkey *ser_sk = 0;
-             struct record_metadata **wheretoput = 0;
-             struct record_metadata *rec_md = 0;
-             int md_field_id = -1;
-             int sk_field_id = -1;
-
-             type = xmlGetProp(n, (xmlChar *) "type");
-             value = xmlNodeListGetString(xdoc, n->children, 1);
-
-             if (!type || !value || !*value)
-                 continue;
-
-             md_field_id 
-                 = conf_service_metadata_field_id(service, (const char *) type);
-             if (md_field_id < 0)
-             {
-                 yaz_log(YLOG_WARN, 
-                         "Ignoring unknown metadata element: %s", type);
-                 continue;
-             }
-
-             ser_md = &service->metadata[md_field_id];
-
-             if (ser_md->sortkey_offset >= 0){
-                 sk_field_id = ser_md->sortkey_offset;
-                 ser_sk = &service->sortkeys[sk_field_id];
-             }
-
-             // non-merged metadata
-             rec_md = record_metadata_init(se->nmem, (char *) value,
-                                           ser_md->type);
-             if (!rec_md)
-             {
-                 yaz_log(YLOG_WARN, "bad metadata data '%s' for element '%s'",
-                         value, type);
-                 continue;
-             }
-             rec_md->next = record->metadata[md_field_id];
-             record->metadata[md_field_id] = rec_md;
-
-             // merged metadata
-             rec_md = record_metadata_init(se->nmem, (char *) value,
-                                           ser_md->type);
-             wheretoput = &cluster->metadata[md_field_id];
-
-             // and polulate with data:
-             // assign cluster or record based on merge action
-             if (ser_md->merge == Metadata_merge_unique)
-             {
-                 struct record_metadata *mnode;
-                 for (mnode = *wheretoput; mnode; mnode = mnode->next)
-                     if (!strcmp((const char *) mnode->data.text.disp, 
-                                 rec_md->data.text.disp))
-                         break;
-                 if (!mnode)
-                 {
-                     rec_md->next = *wheretoput;
-                     *wheretoput = rec_md;
-                 }
-             }
-             else if (ser_md->merge == Metadata_merge_longest)
-             {
-                 if (!*wheretoput 
-                     || strlen(rec_md->data.text.disp) 
-                     > strlen((*wheretoput)->data.text.disp))
-                 {
-                     *wheretoput = rec_md;
-                     if (ser_sk)
-                     {
-                         const char *sort_str = 0;
-                         int skip_article = 
-                             ser_sk->type == Metadata_sortkey_skiparticle;
-
-                         if (!cluster->sortkeys[sk_field_id])
-                             cluster->sortkeys[sk_field_id] = 
-                                 nmem_malloc(se->nmem, 
-                                             sizeof(union data_types));
+    
+    
+    // now parsing XML record and adding data to cluster or record metadata
+    for (n = root->children; n; n = n->next)
+    {
+        if (type)
+            xmlFree(type);
+        if (value)
+            xmlFree(value);
+        type = value = 0;
+        
+        if (n->type != XML_ELEMENT_NODE)
+            continue;
+        if (!strcmp((const char *) n->name, "metadata"))
+        {
+            struct conf_metadata *ser_md = 0;
+            struct conf_sortkey *ser_sk = 0;
+            struct record_metadata **wheretoput = 0;
+            struct record_metadata *rec_md = 0;
+            int md_field_id = -1;
+            int sk_field_id = -1;
+            
+            type = xmlGetProp(n, (xmlChar *) "type");
+            value = xmlNodeListGetString(xdoc, n->children, 1);
+            
+            if (!type || !value || !*value)
+                continue;
+            
+            md_field_id 
+                = conf_service_metadata_field_id(service, (const char *) type);
+            if (md_field_id < 0)
+            {
+                if (se->number_of_warnings_unknown_metadata == 0)
+                {
+                    yaz_log(YLOG_WARN, 
+                            "Ignoring unknown metadata element: %s", type);
+                }
+                se->number_of_warnings_unknown_metadata++;
+                continue;
+            }
+            
+            ser_md = &service->metadata[md_field_id];
+            
+            if (ser_md->sortkey_offset >= 0){
+                sk_field_id = ser_md->sortkey_offset;
+                ser_sk = &service->sortkeys[sk_field_id];
+            }
+
+            // non-merged metadata
+            rec_md = record_metadata_init(se->nmem, (char *) value,
+                                          ser_md->type);
+            if (!rec_md)
+            {
+                yaz_log(YLOG_WARN, "bad metadata data '%s' for element '%s'",
+                        value, type);
+                continue;
+            }
+            wheretoput = &record->metadata[md_field_id];
+            while (*wheretoput)
+                wheretoput = &(*wheretoput)->next;
+            *wheretoput = rec_md;
+
+            // merged metadata
+            rec_md = record_metadata_init(se->nmem, (char *) value,
+                                          ser_md->type);
+            wheretoput = &cluster->metadata[md_field_id];
+
+            // and polulate with data:
+            // assign cluster or record based on merge action
+            if (ser_md->merge == Metadata_merge_unique)
+            {
+                struct record_metadata *mnode;
+                for (mnode = *wheretoput; mnode; mnode = mnode->next)
+                    if (!strcmp((const char *) mnode->data.text.disp, 
+                                rec_md->data.text.disp))
+                        break;
+                if (!mnode)
+                {
+                    rec_md->next = *wheretoput;
+                    *wheretoput = rec_md;
+                }
+            }
+            else if (ser_md->merge == Metadata_merge_longest)
+            {
+                if (!*wheretoput 
+                    || strlen(rec_md->data.text.disp) 
+                    > strlen((*wheretoput)->data.text.disp))
+                {
+                    *wheretoput = rec_md;
+                    if (ser_sk)
+                    {
+                        const char *sort_str = 0;
+                        int skip_article = 
+                            ser_sk->type == Metadata_sortkey_skiparticle;
+
+                        if (!cluster->sortkeys[sk_field_id])
+                            cluster->sortkeys[sk_field_id] = 
+                                nmem_malloc(se->nmem, 
+                                            sizeof(union data_types));
                          
-                         prt = pp2_relevance_tokenize(
-                             global_parameters.server->sort_pct,
-                             rec_md->data.text.disp);
+                        prt = pp2_relevance_tokenize(
+                            global_parameters.server->sort_pct,
+                            rec_md->data.text.disp);
 
-                         pp2_relevance_token_next(prt);
+                        pp2_relevance_token_next(prt);
                          
-                         sort_str = pp2_get_sort(prt, skip_article);
+                        sort_str = pp2_get_sort(prt, skip_article);
                          
-                         cluster->sortkeys[sk_field_id]->text.disp = 
-                             rec_md->data.text.disp;
-                         if (!sort_str)
-                         {
-                             sort_str = rec_md->data.text.disp;
-                             yaz_log(YLOG_WARN, 
-                                     "Could not make sortkey. Bug #1858");
-                         }
-                         cluster->sortkeys[sk_field_id]->text.sort = 
-                             nmem_strdup(se->nmem, sort_str);
+                        cluster->sortkeys[sk_field_id]->text.disp = 
+                            rec_md->data.text.disp;
+                        if (!sort_str)
+                        {
+                            sort_str = rec_md->data.text.disp;
+                            yaz_log(YLOG_WARN, 
+                                    "Could not make sortkey. Bug #1858");
+                        }
+                        cluster->sortkeys[sk_field_id]->text.sort = 
+                            nmem_strdup(se->nmem, sort_str);
 #if 0
-                         yaz_log(YLOG_LOG, "text disp=%s",
-                                 cluster->sortkeys[sk_field_id]->text.disp);
-                         yaz_log(YLOG_LOG, "text sort=%s",
-                                 cluster->sortkeys[sk_field_id]->text.sort);
+                        yaz_log(YLOG_LOG, "text disp=%s",
+                                cluster->sortkeys[sk_field_id]->text.disp);
+                        yaz_log(YLOG_LOG, "text sort=%s",
+                                cluster->sortkeys[sk_field_id]->text.sort);
 #endif
-                         pp2_relevance_token_destroy(prt);
+                        pp2_relevance_token_destroy(prt);
                     }
                 }
             }
@@ -1349,8 +1203,12 @@ struct record *ingest_record(struct client *cl, Z_External *rec,
             type = value = 0;
         }
         else
-            yaz_log(YLOG_WARN,
-                    "Unexpected element %s in internal record", n->name);
+        {
+            if (se->number_of_warnings_unknown_elements == 0)
+                yaz_log(YLOG_WARN,
+                        "Unexpected element in internal record: %s", n->name);
+            se->number_of_warnings_unknown_elements++;
+        }
     }
     if (type)
         xmlFree(type);
@@ -1370,7 +1228,9 @@ struct record *ingest_record(struct client *cl, Z_External *rec,
 /*
  * Local variables:
  * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
  * indent-tabs-mode: nil
  * End:
  * vim: shiftwidth=4 tabstop=8 expandtab
  */
+