Mention CentOS 7 repo in documentation
[mp-xquery-moved-to-github.git] / src / metaproxy_filter_xquery.cpp
index 76067e3..4864ce4 100644 (file)
@@ -19,12 +19,26 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 #include <metaproxy/package.hpp>
 #include <metaproxy/util.hpp>
 #include <yaz/log.h>
+#include <yaz/oid_db.h>
+#include <yaz/diagbib1.h>
 #include <map>
+#include <string>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+
+#include <zorba/zorba.h>
+#include <zorba/store_manager.h>
+#include <zorba/serializer.h>
+#include <zorba/singleton_item_sequence.h>
+#include <zorba/zorba_exception.h>
+
 
 namespace mp = metaproxy_1;
 namespace yf = mp::filter;
 namespace mp_util = metaproxy_1::util;
 using namespace mp;
+using namespace zorba;
 
 namespace metaproxy_1 {
     namespace filter {
@@ -38,24 +52,32 @@ namespace metaproxy_1 {
             void start() const;
             void stop(int signo) const;
         private:
+            bool convert_one_record(const char *input_buf,
+                                    size_t input_len,
+                                    std::string &result) const;
             std::map<std::string, std::string> zorba_variables;
             std::string zorba_filename;
             std::string zorba_script;
             std::string zorba_record_variable;
+            std::string elementset_input;
+            std::string elementset_output;
+            Zorba *lZorba;
+            XQuery_t lQuery;
         };
     }
 }
 
 yf::XQuery::XQuery()
 {
+    lZorba = 0;
 }
 
 yf::XQuery::~XQuery()
 {
-
+    if (lZorba)
+        lZorba->shutdown();
 }
 
-
 void yf::XQuery::start() const
 {
 }
@@ -64,6 +86,37 @@ void yf::XQuery::stop(int signo) const
 {
 }
 
+bool yf::XQuery::convert_one_record(const char *input_buf,
+                                    size_t input_len,
+                                    std::string &result) const
+{
+    XQuery_t tQuery = lQuery->clone();
+
+    zorba::DynamicContext* lDynamicContext = tQuery->getDynamicContext();
+
+    zorba::Item lItem;
+    std::map<std::string, std::string>::const_iterator it;
+    for (it = zorba_variables.begin(); it != zorba_variables.end(); it++)
+    {
+        lItem = lZorba->getItemFactory()->createString(it->second);
+        lDynamicContext->setVariable(it->first, lItem);
+    }
+    std::string rec_content = "raw:" + std::string(input_buf, input_len);
+    lItem = lZorba->getItemFactory()->createString(rec_content);
+    lDynamicContext->setVariable(zorba_record_variable, lItem);
+
+    try {
+        std::stringstream ss;
+        tQuery->execute(ss);
+        result = ss.str();
+        return true;
+    } catch ( ZorbaException &e) {
+        result = e.what();
+        yaz_log(YLOG_WARN, "XQuery execute: %s", result.c_str());
+        return false;
+    }
+}
+
 void yf::XQuery::process(Package &package) const
 {
     Z_GDU *gdu_req = package.request().get();
@@ -125,7 +178,140 @@ void yf::XQuery::process(Package &package) const
         package.move();
         return;
     }
+
+    mp::odr odr_en(ODR_ENCODE);
+
+    const char *backend_schema = 0;
+    const Odr_oid *backend_syntax = 0;
+
+    if (input_schema && !strcmp(input_schema, elementset_input.c_str()) &&
+        (!input_syntax || !oid_oidcmp(input_syntax, yaz_oid_recsyn_xml)))
+    {
+        backend_schema = elementset_output.c_str();
+        backend_syntax = yaz_oid_recsyn_xml;
+    }
+    else
+    {
+        package.move();
+        return;
+    }
+
+    if (sr_req)
+    {
+        if (backend_syntax)
+            sr_req->preferredRecordSyntax = odr_oiddup(odr_en, backend_syntax);
+        else
+            sr_req->preferredRecordSyntax = 0;
+        if (backend_schema)
+        {
+            sr_req->smallSetElementSetNames
+                = (Z_ElementSetNames *)
+                odr_malloc(odr_en, sizeof(Z_ElementSetNames));
+            sr_req->smallSetElementSetNames->which = Z_ElementSetNames_generic;
+            sr_req->smallSetElementSetNames->u.generic
+                = odr_strdup(odr_en, backend_schema);
+            sr_req->mediumSetElementSetNames = sr_req->smallSetElementSetNames;
+        }
+        else
+        {
+            sr_req->smallSetElementSetNames = 0;
+            sr_req->mediumSetElementSetNames = 0;
+        }
+    }
+    else if (pr_req)
+    {
+        if (backend_syntax)
+            pr_req->preferredRecordSyntax = odr_oiddup(odr_en, backend_syntax);
+        else
+            pr_req->preferredRecordSyntax = 0;
+
+        if (backend_schema)
+        {
+            pr_req->recordComposition
+                = (Z_RecordComposition *)
+                odr_malloc(odr_en, sizeof(Z_RecordComposition));
+            pr_req->recordComposition->which
+                = Z_RecordComp_simple;
+            pr_req->recordComposition->u.simple
+                = (Z_ElementSetNames *)
+                odr_malloc(odr_en, sizeof(Z_ElementSetNames));
+            pr_req->recordComposition->u.simple->which = Z_ElementSetNames_generic;
+            pr_req->recordComposition->u.simple->u.generic
+                = odr_strdup(odr_en, backend_schema);
+        }
+        else
+            pr_req->recordComposition = 0;
+    }
     package.move();
+
+    Z_GDU *gdu_res = package.response().get();
+
+    // see if we have a records list to patch!
+    Z_NamePlusRecordList *records = 0;
+    if (gdu_res && gdu_res->which == Z_GDU_Z3950 &&
+        gdu_res->u.z3950->which == Z_APDU_presentResponse)
+    {
+        Z_PresentResponse * pr_res = gdu_res->u.z3950->u.presentResponse;
+
+        if (pr_res
+            && pr_res->numberOfRecordsReturned
+            && *(pr_res->numberOfRecordsReturned) > 0
+            && pr_res->records
+            && pr_res->records->which == Z_Records_DBOSD)
+        {
+            records = pr_res->records->u.databaseOrSurDiagnostics;
+        }
+    }
+    if (gdu_res && gdu_res->which == Z_GDU_Z3950 &&
+        gdu_res->u.z3950->which == Z_APDU_searchResponse)
+    {
+        Z_SearchResponse *sr_res = gdu_res->u.z3950->u.searchResponse;
+
+        if (sr_res
+            && sr_res->numberOfRecordsReturned
+            && *(sr_res->numberOfRecordsReturned) > 0
+            && sr_res->records
+            && sr_res->records->which == Z_Records_DBOSD)
+        {
+            records = sr_res->records->u.databaseOrSurDiagnostics;
+        }
+    }
+    if (records)
+    {
+        int i;
+        for (i = 0; i < records->num_records; i++)
+        {
+            Z_NamePlusRecord **npr = &records->records[i];
+            if ((*npr)->which == Z_NamePlusRecord_databaseRecord)
+            {
+                const char *details = 0;
+                Z_External *r = (*npr)->u.databaseRecord;
+                int ret_trans = -1;
+                if (r->which == Z_External_octet &&
+                    !oid_oidcmp(r->direct_reference, yaz_oid_recsyn_xml))
+                {
+                    std::string result;
+                    if (convert_one_record(
+                        r->u.octet_aligned->buf, r->u.octet_aligned->len,
+                        result))
+                    {
+                        (*npr)->u.databaseRecord =
+                            z_ext_record_oid(odr_en, yaz_oid_recsyn_xml,
+                                             result.c_str(),
+                                             result.length());
+                    }
+                    else
+                    {
+                        *npr = zget_surrogateDiagRec(
+                            odr_en, (*npr)->databaseName,
+                            YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS,
+                            result.c_str());
+                    }
+                }
+            }
+        }
+        package.response() = gdu_res;
+    }
 }
 
 void yf::XQuery::configure(const xmlNode * ptr, bool test_only,
@@ -135,61 +321,61 @@ void yf::XQuery::configure(const xmlNode * ptr, bool test_only,
     {
         if (ptr->type != XML_ELEMENT_NODE)
             continue;
-        if (!strcmp((const char *) ptr->name, "setVariable"))
+        if (!strcmp((const char *) ptr->name, "elementset"))
         {
-            std::string name;
-            std::string value;
             struct _xmlAttr *attr;
             for (attr = ptr->properties; attr; attr = attr->next)
                 if (!strcmp((const char *) attr->name, "name"))
-                    name = mp::xml::get_text(attr->children);
-                else if (!strcmp((const char *) attr->name, "value"))
-                    value = mp::xml::get_text(attr->children);
+                    elementset_input = mp::xml::get_text(attr->children);
+                else if (!strcmp((const char *) attr->name, "backend"))
+                    elementset_output = mp::xml::get_text(attr->children);
                 else
                     throw mp::filter::FilterException(
                         "Bad attribute " + std::string((const char *)
                                                        attr->name));
-            if (name.length() > 0)
-                zorba_variables[name] = value;
         }
-        else if (!strcmp((const char *) ptr->name, "filename"))
+        else if (!strcmp((const char *) ptr->name, "variable"))
         {
+            std::string name;
             std::string value;
             struct _xmlAttr *attr;
             for (attr = ptr->properties; attr; attr = attr->next)
-                if (!strcmp((const char *) attr->name, "value"))
+                if (!strcmp((const char *) attr->name, "name"))
+                    name = mp::xml::get_text(attr->children);
+                else if (!strcmp((const char *) attr->name, "value"))
                     value = mp::xml::get_text(attr->children);
                 else
                     throw mp::filter::FilterException(
                         "Bad attribute " + std::string((const char *)
                                                        attr->name));
-            zorba_filename = value;
+            if (name.length() > 0)
+                zorba_variables[name] = value;
         }
         else if (!strcmp((const char *) ptr->name, "script"))
         {
-            std::string value;
+            std::string name;
             struct _xmlAttr *attr;
             for (attr = ptr->properties; attr; attr = attr->next)
-                if (!strcmp((const char *) attr->name, "value"))
-                    value = mp::xml::get_text(attr->children);
+                if (!strcmp((const char *) attr->name, "name"))
+                    name = mp::xml::get_text(attr->children);
                 else
                     throw mp::filter::FilterException(
                         "Bad attribute " + std::string((const char *)
                                                        attr->name));
-            zorba_script = value;
+            zorba_script = name;
         }
         else if (!strcmp((const char *) ptr->name, "record"))
         {
-            std::string value;
+            std::string name;
             struct _xmlAttr *attr;
             for (attr = ptr->properties; attr; attr = attr->next)
-                if (!strcmp((const char *) attr->name, "value"))
-                    value = mp::xml::get_text(attr->children);
+                if (!strcmp((const char *) attr->name, "name"))
+                    name = mp::xml::get_text(attr->children);
                 else
                     throw mp::filter::FilterException(
                         "Bad attribute " + std::string((const char *)
                                                        attr->name));
-            zorba_record_variable = value;
+            zorba_record_variable = name;
         }
         else
         {
@@ -202,8 +388,27 @@ void yf::XQuery::configure(const xmlNode * ptr, bool test_only,
         throw mp::filter::FilterException("Missing element script");
     if (zorba_record_variable.length() == 0)
         throw mp::filter::FilterException("Missing element record");
-    if (zorba_filename.length() == 0)
-        throw mp::filter::FilterException("Missing element filename");
+    if (!test_only)
+    {
+        void* lStore = StoreManager::getStore();
+        lZorba = Zorba::getInstance(lStore);
+
+        lQuery = lZorba->createQuery();
+
+        try {
+            size_t t = zorba_script.find_last_of('/');
+            if (t != std::string::npos)
+                lQuery->setFileName(zorba_script.substr(0, t + 1));
+            std::unique_ptr<std::istream> qfile(
+                new std::ifstream(zorba_script.c_str()));
+            Zorba_CompilerHints lHints;
+            lQuery->compile(*qfile, lHints);
+        } catch ( ZorbaException &e) {
+            std::string msg = "XQuery compile: ";
+            msg += e.what();
+            throw mp::filter::FilterException(msg);
+        }
+    }
 }
 
 static yf::Base* filter_creator()