1 /* This file is part of Pazpar2.
2 Copyright (C) 2006-2011 Index Data
4 Pazpar2 is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
9 Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
39 #include <yaz/marcdisp.h>
40 #include <yaz/comstack.h>
41 #include <yaz/tcpip.h>
42 #include <yaz/proto.h>
43 #include <yaz/readconf.h>
44 #include <yaz/pquery.h>
45 #include <yaz/otherinfo.h>
46 #include <yaz/yaz-util.h>
48 #include <yaz/query-charset.h>
49 #include <yaz/querytowrbuf.h>
50 #include <yaz/oid_db.h>
51 #include <yaz/diagbib1.h>
52 #include <yaz/snprintf.h>
53 #include <yaz/rpn2cql.h>
54 #include <yaz/rpn2solr.h>
58 #include <yaz/timing.h>
63 #include "parameters.h"
65 #include "connection.h"
67 #include "relevance.h"
70 static YAZ_MUTEX g_mutex = 0;
71 static int no_clients = 0;
72 static int no_clients_total = 0;
74 static int client_use(int delta)
78 yaz_mutex_create(&g_mutex);
79 yaz_mutex_enter(g_mutex);
82 no_clients_total += delta;
84 yaz_mutex_leave(g_mutex);
85 yaz_log(YLOG_DEBUG, "%s clients=%d", delta == 0 ? "" : (delta > 0 ? "INC" : "DEC"), clients);
89 int clients_count(void) {
93 int clients_count_total(void) {
97 yaz_mutex_enter(g_mutex);
98 total = no_clients_total;
99 yaz_mutex_leave(g_mutex);
104 /** \brief Represents client state for a connection to one search target */
106 struct session_database *database;
107 struct connection *connection;
108 struct session *session;
109 char *pquery; // Current search
110 char *cqlquery; // used for SRU targets only
111 char *addinfo; // diagnostic info for most resent error
118 struct suggestions *suggestions;
119 enum client_state state;
120 struct show_raw *show_raw;
121 ZOOM_resultset resultset;
125 facet_limits_t facet_limits;
137 int active; // whether this request has been sent to the server
142 void (*error_handler)(void *data, const char *addinfo);
143 void (*record_handler)(void *data, const char *buf, size_t sz);
145 struct show_raw *next;
148 static const char *client_states[] = {
154 "Client_Disconnected"
157 const char *client_get_state_str(struct client *cl)
159 return client_states[cl->state];
162 enum client_state client_get_state(struct client *cl)
167 void client_set_state_nb(struct client *cl, enum client_state st)
172 void client_set_state(struct client *cl, enum client_state st)
175 if (client_is_active(cl))
178 /* If client is going from being active to inactive and all clients
179 are now idle we fire a watch for the session . The assumption is
180 that session is not mutex locked if client is already active */
181 if (was_active && !client_is_active(cl) && cl->session)
184 int no_active = session_active_clients(cl->session);
185 yaz_log(YLOG_DEBUG, "%s: releasing watches on zero active: %d",
186 client_get_id(cl), no_active);
187 if (no_active == 0) {
188 session_alert_watch(cl->session, SESSION_WATCH_SHOW);
189 session_alert_watch(cl->session, SESSION_WATCH_BYTARGET);
190 session_alert_watch(cl->session, SESSION_WATCH_TERMLIST);
191 session_alert_watch(cl->session, SESSION_WATCH_SHOW_PREF);
196 static void client_show_raw_error(struct client *cl, const char *addinfo);
198 struct connection *client_get_connection(struct client *cl)
200 return cl->connection;
203 struct session_database *client_get_database(struct client *cl)
208 struct session *client_get_session(struct client *cl)
213 const char *client_get_pquery(struct client *cl)
218 static void client_send_raw_present(struct client *cl);
219 static int nativesyntax_to_type(struct session_database *sdb, char *type,
222 static void client_show_immediate(
223 ZOOM_resultset resultset, struct session_database *sdb, int position,
225 void (*error_handler)(void *data, const char *addinfo),
226 void (*record_handler)(void *data, const char *buf, size_t sz),
236 error_handler(data, "no resultset");
239 rec = ZOOM_resultset_record(resultset, position-1);
242 error_handler(data, "no record");
248 nativesyntax_to_type(sdb, type, rec);
249 buf = ZOOM_record_get(rec, type, &len);
252 error_handler(data, "no record");
255 record_handler(data, buf, len);
259 int client_show_raw_begin(struct client *cl, int position,
260 const char *syntax, const char *esn,
262 void (*error_handler)(void *data, const char *addinfo),
263 void (*record_handler)(void *data, const char *buf,
267 if (syntax == 0 && esn == 0)
268 client_show_immediate(cl->resultset, client_get_database(cl),
270 error_handler, record_handler,
274 struct show_raw *rr, **rrp;
280 rr = xmalloc(sizeof(*rr));
281 rr->position = position;
284 rr->error_handler = error_handler;
285 rr->record_handler = record_handler;
288 rr->syntax = xstrdup(syntax);
292 rr->esn = xstrdup(esn);
297 for (rrp = &cl->show_raw; *rrp; rrp = &(*rrp)->next)
301 if (cl->state == Client_Failed)
303 client_show_raw_error(cl, "client failed");
305 else if (cl->state == Client_Disconnected)
307 client_show_raw_error(cl, "client disconnected");
311 client_send_raw_present(cl);
317 static void client_show_raw_delete(struct show_raw *r)
324 void client_show_raw_remove(struct client *cl, void *data)
326 struct show_raw *rr = data;
327 struct show_raw **rrp = &cl->show_raw;
333 client_show_raw_delete(rr);
337 void client_show_raw_dequeue(struct client *cl)
339 struct show_raw *rr = cl->show_raw;
341 cl->show_raw = rr->next;
342 client_show_raw_delete(rr);
345 static void client_show_raw_error(struct client *cl, const char *addinfo)
349 cl->show_raw->error_handler(cl->show_raw->data, addinfo);
350 client_show_raw_dequeue(cl);
354 static void client_send_raw_present(struct client *cl)
356 struct session_database *sdb = client_get_database(cl);
357 struct connection *co = client_get_connection(cl);
358 ZOOM_resultset set = cl->resultset;
360 int offset = cl->show_raw->position;
361 const char *syntax = 0;
362 const char *elements = 0;
364 assert(cl->show_raw);
367 yaz_log(YLOG_DEBUG, "%s: trying to present %d record(s) from %d",
368 client_get_id(cl), 1, offset);
370 if (cl->show_raw->syntax)
371 syntax = cl->show_raw->syntax;
373 syntax = session_setting_oneval(sdb, PZ_REQUESTSYNTAX);
374 ZOOM_resultset_option_set(set, "preferredRecordSyntax", syntax);
376 if (cl->show_raw->esn)
377 elements = cl->show_raw->esn;
379 elements = session_setting_oneval(sdb, PZ_ELEMENTS);
380 if (elements && *elements)
381 ZOOM_resultset_option_set(set, "elementSetName", elements);
383 ZOOM_resultset_records(set, 0, offset-1, 1);
384 cl->show_raw->active = 1;
386 connection_continue(co);
389 static int nativesyntax_to_type(struct session_database *sdb, char *type,
392 const char *s = session_setting_oneval(sdb, PZ_NATIVESYNTAX);
396 if (!strncmp(s, "iso2709", 7))
398 const char *cp = strchr(s, ';');
399 yaz_snprintf(type, 80, "xml; charset=%s", cp ? cp+1 : "marc-8s");
401 else if (!strncmp(s, "xml", 3))
405 else if (!strncmp(s, "txml", 4))
407 const char *cp = strchr(s, ';');
408 yaz_snprintf(type, 80, "txml; charset=%s", cp ? cp+1 : "marc-8s");
414 else /* attempt to deduce structure */
416 const char *syntax = ZOOM_record_get(rec, "syntax", NULL);
419 if (!strcmp(syntax, "XML"))
424 else if (!strcmp(syntax, "USmarc") || !strcmp(syntax, "MARC21"))
426 strcpy(type, "xml; charset=marc8-s");
436 * TODO Consider thread safety!!!
439 void client_report_facets(struct client *cl, ZOOM_resultset rs)
441 struct session_database *sdb = client_get_database(cl);
442 ZOOM_facet_field *facets = ZOOM_resultset_facets(rs);
446 struct session *se = client_get_session(cl);
447 int facet_num = ZOOM_resultset_facets_size(rs);
450 for (s = sdb->settings[PZ_FACETMAP]; s; s = s->next)
452 const char *p = strchr(s->name + 3, ':');
453 if (p && p[1] && s->value && s->value[0])
456 p++; /* p now holds logical facet name */
457 for (facet_idx = 0; facet_idx < facet_num; facet_idx++)
459 const char *native_name =
460 ZOOM_facet_field_name(facets[facet_idx]);
461 if (native_name && !strcmp(s->value, native_name))
465 ZOOM_facet_field_term_count(facets[facet_idx]);
466 for (term_idx = 0; term_idx < term_num; term_idx++ )
470 ZOOM_facet_field_get_term(facets[facet_idx],
473 add_facet(se, p, term, freq);
483 static void ingest_raw_record(struct client *cl, ZOOM_record rec)
489 if (cl->show_raw->binary)
493 struct session_database *sdb = client_get_database(cl);
494 nativesyntax_to_type(sdb, type, rec);
497 buf = ZOOM_record_get(rec, type, &len);
498 cl->show_raw->record_handler(cl->show_raw->data, buf, len);
499 client_show_raw_dequeue(cl);
502 void client_check_preferred_watch(struct client *cl)
504 struct session *se = cl->session;
505 yaz_log(YLOG_DEBUG, "client_check_preferred_watch: %s ", client_get_id(cl));
509 /* TODO possible threading issue. Session can have been destroyed */
510 if (session_is_preferred_clients_ready(se)) {
511 session_alert_watch(se, SESSION_WATCH_SHOW_PREF);
514 yaz_log(YLOG_DEBUG, "client_check_preferred_watch: Still locked on preferred targets.");
519 yaz_log(YLOG_WARN, "client_check_preferred_watch: %s. No session!", client_get_id(cl));
523 struct suggestions* client_suggestions_create(const char* suggestions_string);
524 static void client_suggestions_destroy(struct client *cl);
526 void client_search_response(struct client *cl)
528 struct connection *co = cl->connection;
529 ZOOM_connection link = connection_get_link(co);
530 ZOOM_resultset resultset = cl->resultset;
532 const char *error, *addinfo = 0;
534 if (ZOOM_connection_error(link, &error, &addinfo))
537 client_set_state(cl, Client_Error);
538 yaz_log(YLOG_WARN, "Search error %s (%s): %s",
539 error, addinfo, client_get_id(cl));
543 yaz_log(YLOG_DEBUG, "client_search_response: hits "
544 ODR_INT_PRINTF, cl->hits);
545 client_report_facets(cl, resultset);
546 cl->record_offset = cl->startrecs;
547 cl->hits = ZOOM_resultset_size(resultset);
549 client_suggestions_destroy(cl);
550 cl->suggestions = client_suggestions_create(ZOOM_resultset_option_get(resultset, "suggestions"));
554 void client_got_records(struct client *cl)
556 struct session *se = cl->session;
559 if (reclist_get_num_records(se->reclist) > 0)
562 session_alert_watch(se, SESSION_WATCH_SHOW);
563 session_alert_watch(se, SESSION_WATCH_BYTARGET);
564 session_alert_watch(se, SESSION_WATCH_TERMLIST);
565 session_alert_watch(se, SESSION_WATCH_RECORD);
571 static void client_record_ingest(struct client *cl)
573 const char *msg, *addinfo;
575 ZOOM_resultset resultset = cl->resultset;
576 int offset = cl->record_offset;
577 if ((rec = ZOOM_resultset_record(resultset, offset)))
580 if (cl->session == 0)
582 else if (ZOOM_record_error(rec, &msg, &addinfo, 0))
584 yaz_log(YLOG_WARN, "Record error %s (%s): %s (rec #%d)",
585 msg, addinfo, client_get_id(cl),
590 struct session_database *sdb = client_get_database(cl);
591 NMEM nmem = nmem_create();
595 if (nativesyntax_to_type(sdb, type, rec))
596 yaz_log(YLOG_WARN, "Failed to determine record type");
597 xmlrec = ZOOM_record_get(rec, type, NULL);
599 yaz_log(YLOG_WARN, "ZOOM_record_get failed from %s",
603 /* OK = 0, -1 = failure, -2 = Filtered */
604 if (ingest_record(cl, xmlrec, cl->record_offset, nmem) == -1)
605 yaz_log(YLOG_WARN, "Failed to ingest from %s", client_get_id(cl));
612 yaz_log(YLOG_WARN, "Expected record, but got NULL, offset=%d",
617 void client_record_response(struct client *cl)
619 struct connection *co = cl->connection;
620 ZOOM_connection link = connection_get_link(co);
621 ZOOM_resultset resultset = cl->resultset;
622 const char *error, *addinfo;
624 if (ZOOM_connection_error(link, &error, &addinfo))
626 client_set_state(cl, Client_Error);
627 yaz_log(YLOG_WARN, "Search error %s (%s): %s",
628 error, addinfo, client_get_id(cl));
632 if (cl->show_raw && cl->show_raw->active)
635 if ((rec = ZOOM_resultset_record(resultset,
636 cl->show_raw->position-1)))
638 cl->show_raw->active = 0;
639 ingest_raw_record(cl, rec);
643 yaz_log(YLOG_WARN, "Expected record, but got NULL, offset=%d",
644 cl->show_raw->position-1);
649 client_record_ingest(cl);
654 void client_reingest(struct client *cl)
656 int i = cl->startrecs;
657 int to = cl->record_offset;
659 cl->record_offset = i;
661 client_record_ingest(cl);
664 static void client_set_facets_request(struct client *cl, ZOOM_connection link)
666 struct session_database *sdb = client_get_database(cl);
668 WRBUF w = wrbuf_alloc();
672 for (s = sdb->settings[PZ_FACETMAP]; s; s = s->next)
674 const char *p = strchr(s->name + 3, ':');
677 yaz_log(YLOG_WARN, "Malformed facetmap name: %s", s->name);
679 else if (s->value && s->value[0])
681 wrbuf_puts(w, "@attr 1=");
682 yaz_encode_pqf_term(w, s->value, strlen(s->value));
687 yaz_log(YLOG_LOG, "using facets str: %s", wrbuf_cstr(w));
688 ZOOM_connection_option_set(link, "facets",
689 wrbuf_len(w) ? wrbuf_cstr(w) : 0);
693 int client_has_facet(struct client *cl, const char *name)
695 struct session_database *sdb = client_get_database(cl);
698 for (s = sdb->settings[PZ_FACETMAP]; s; s = s->next)
700 const char *p = strchr(s->name + 3, ':');
701 if (p && !strcmp(name, p + 1))
707 static const char *get_strategy_plus_sort(struct client *l, const char *field)
709 struct session_database *sdb = client_get_database(l);
712 const char *strategy_plus_sort = 0;
714 for (s = sdb->settings[PZ_SORTMAP]; s; s = s->next)
716 char *p = strchr(s->name + 3, ':');
719 yaz_log(YLOG_WARN, "Malformed sortmap name: %s", s->name);
723 if (!strcmp(p, field))
725 strategy_plus_sort = s->value;
729 return strategy_plus_sort;
732 void client_start_search(struct client *cl)
734 struct session_database *sdb = client_get_database(cl);
735 struct connection *co = client_get_connection(cl);
736 ZOOM_connection link = connection_get_link(co);
737 struct session *se = client_get_session(cl);
739 const char *opt_piggyback = session_setting_oneval(sdb, PZ_PIGGYBACK);
740 const char *opt_queryenc = session_setting_oneval(sdb, PZ_QUERYENCODING);
741 const char *opt_elements = session_setting_oneval(sdb, PZ_ELEMENTS);
742 const char *opt_requestsyn = session_setting_oneval(sdb, PZ_REQUESTSYNTAX);
743 const char *opt_maxrecs = session_setting_oneval(sdb, PZ_MAXRECS);
744 const char *opt_sru = session_setting_oneval(sdb, PZ_SRU);
745 const char *opt_sort = session_setting_oneval(sdb, PZ_SORT);
746 const char *opt_preferred = session_setting_oneval(sdb, PZ_PREFERRED);
747 const char *extra_args = session_setting_oneval(sdb, PZ_EXTRA_ARGS);
748 char maxrecs_str[24], startrecs_str[24];
755 if (extra_args && *extra_args)
756 ZOOM_connection_option_set(link, "extraArgs", extra_args);
759 cl->preferred = atoi(opt_preferred);
761 yaz_log(YLOG_LOG, "Target %s has preferred status: %d",
762 client_get_id(cl), cl->preferred);
764 client_set_state(cl, Client_Working);
767 ZOOM_connection_option_set(link, "piggyback", opt_piggyback);
769 ZOOM_connection_option_set(link, "piggyback", "1");
771 ZOOM_connection_option_set(link, "rpnCharset", opt_queryenc);
772 if (*opt_sru && *opt_elements)
773 ZOOM_connection_option_set(link, "schema", opt_elements);
774 else if (*opt_elements)
775 ZOOM_connection_option_set(link, "elementSetName", opt_elements);
777 ZOOM_connection_option_set(link, "preferredRecordSyntax", opt_requestsyn);
779 if (opt_maxrecs && *opt_maxrecs)
781 cl->maxrecs = atoi(opt_maxrecs);
784 /* convert back to string representation used in ZOOM API */
785 sprintf(maxrecs_str, "%d", cl->maxrecs);
786 ZOOM_connection_option_set(link, "count", maxrecs_str);
788 if (cl->maxrecs > 20)
789 ZOOM_connection_option_set(link, "presentChunk", "20");
791 ZOOM_connection_option_set(link, "presentChunk", maxrecs_str);
793 sprintf(startrecs_str, "%d", cl->startrecs);
794 ZOOM_connection_option_set(link, "start", startrecs_str);
796 /* TODO Verify does it break something for CQL targets(non-SOLR) ? */
797 /* facets definition is in PQF */
798 client_set_facets_request(cl, link);
800 q = ZOOM_query_create();
803 yaz_log(YLOG_LOG, "Search %s CQL: %s", client_get_id(cl),
805 ZOOM_query_cql(q, cl->cqlquery);
807 ZOOM_query_sortby(q, opt_sort);
811 yaz_log(YLOG_LOG, "Search %s PQF: %s", client_get_id(cl), cl->pquery);
813 ZOOM_query_prefix(q, cl->pquery);
815 if (se->sorted_results)
816 { /* first entry is current sorting ! */
817 const char *sort_strategy_and_spec =
818 get_strategy_plus_sort(cl, se->sorted_results->field);
819 int increasing = se->sorted_results->increasing;
820 if (sort_strategy_and_spec && strlen(sort_strategy_and_spec) < 40)
823 strcpy(spec, sort_strategy_and_spec);
824 p = strchr(spec, ':');
827 *p++ = '\0'; /* cut the string in two */
834 yaz_log(YLOG_LOG, "applying %s %s", spec, p);
835 ZOOM_query_sortby2(q, spec, p);
840 /* no native sorting.. If this is not the first search, then
842 if (se->sorted_results->next)
844 ZOOM_query_destroy(q);
845 client_set_state_nb(cl, Client_Idle);
851 cl->record_offset = 0;
852 rs = ZOOM_connection_search(link, q);
853 ZOOM_query_destroy(q);
854 ZOOM_resultset_destroy(cl->resultset);
856 connection_continue(co);
859 struct client *client_create(const char *id)
861 struct client *cl = xmalloc(sizeof(*cl));
871 cl->record_offset = 0;
873 cl->state = Client_Disconnected;
878 pazpar2_mutex_create(&cl->mutex, "client");
881 cl->facet_limits = 0;
883 cl->id = xstrdup(id);
889 void client_lock(struct client *c)
891 yaz_mutex_enter(c->mutex);
894 void client_unlock(struct client *c)
896 yaz_mutex_leave(c->mutex);
899 void client_incref(struct client *c)
901 pazpar2_incref(&c->ref_count, c->mutex);
902 yaz_log(YLOG_DEBUG, "client_incref c=%p %s cnt=%d",
903 c, client_get_id(c), c->ref_count);
906 int client_destroy(struct client *c)
910 yaz_log(YLOG_DEBUG, "client_destroy c=%p %s cnt=%d",
911 c, client_get_id(c), c->ref_count);
912 if (!pazpar2_decref(&c->ref_count, c->mutex))
921 assert(!c->connection);
922 facet_limits_destroy(c->facet_limits);
926 ZOOM_resultset_destroy(c->resultset);
928 yaz_mutex_destroy(&c->mutex);
937 void client_set_connection(struct client *cl, struct connection *con)
940 ZOOM_resultset_release(cl->resultset);
943 assert(cl->connection == 0);
944 cl->connection = con;
949 cl->connection = con;
954 void client_disconnect(struct client *cl)
956 if (cl->state != Client_Idle)
957 client_set_state(cl, Client_Disconnected);
958 client_set_connection(cl, 0);
962 // Initialize CCL map for a target
963 static CCL_bibset prepare_cclmap(struct client *cl)
965 struct session_database *sdb = client_get_database(cl);
972 for (s = sdb->settings[PZ_CCLMAP]; s; s = s->next)
974 char *p = strchr(s->name + 3, ':');
977 yaz_log(YLOG_WARN, "Malformed cclmap name: %s", s->name);
982 ccl_qual_fitem(res, s->value, p);
987 // returns a xmalloced CQL query corresponding to the pquery in client
988 static char *make_cqlquery(struct client *cl)
990 cql_transform_t cqlt = cql_transform_create();
993 WRBUF wrb = wrbuf_alloc();
995 ODR odr_out = odr_createmem(ODR_ENCODE);
997 zquery = p_query_rpn(odr_out, cl->pquery);
998 yaz_log(YLOG_LOG, "PQF: %s", cl->pquery);
999 if ((status = cql_transform_rpn2cql_wrbuf(cqlt, wrb, zquery)))
1001 yaz_log(YLOG_WARN, "Failed to generate CQL query, code=%d", status);
1006 r = xstrdup(wrbuf_cstr(wrb));
1009 odr_destroy(odr_out);
1010 cql_transform_close(cqlt);
1014 // returns a xmalloced SOLR query corresponding to the pquery in client
1015 // TODO Could prob. be merge with the similar make_cqlquery
1016 static char *make_solrquery(struct client *cl)
1018 solr_transform_t sqlt = solr_transform_create();
1021 WRBUF wrb = wrbuf_alloc();
1023 ODR odr_out = odr_createmem(ODR_ENCODE);
1025 zquery = p_query_rpn(odr_out, cl->pquery);
1027 yaz_log(YLOG_WARN, "Failed to generate RPN from PQF: %s", cl->pquery);
1030 yaz_log(YLOG_LOG, "PQF: %s", cl->pquery);
1031 if ((status = solr_transform_rpn2solr_wrbuf(sqlt, wrb, zquery)))
1033 yaz_log(YLOG_WARN, "Failed to generate SOLR query from PQF %s, code=%d", cl->pquery, status);
1038 r = xstrdup(wrbuf_cstr(wrb));
1041 odr_destroy(odr_out);
1042 solr_transform_close(sqlt);
1046 const char *client_get_facet_limit_local(struct client *cl,
1047 struct session_database *sdb,
1049 NMEM nmem, int *num, char ***values)
1051 const char *name = 0;
1052 const char *value = 0;
1053 for (; (name = facet_limits_get(cl->facet_limits, *l, &value)); (*l)++)
1055 struct setting *s = 0;
1057 for (s = sdb->settings[PZ_LIMITMAP]; s; s = s->next)
1059 const char *p = strchr(s->name + 3, ':');
1060 if (p && !strcmp(p + 1, name) && s->value &&
1061 !strncmp(s->value, "local:", 6))
1063 nmem_strsplit_escape2(nmem, "|", value, values,
1073 static int apply_limit(struct session_database *sdb,
1074 facet_limits_t facet_limits,
1075 WRBUF w_pqf, WRBUF w_ccl)
1081 NMEM nmem_tmp = nmem_create();
1082 for (i = 0; (name = facet_limits_get(facet_limits, i, &value)); i++)
1084 struct setting *s = 0;
1086 for (s = sdb->settings[PZ_LIMITMAP]; s; s = s->next)
1088 const char *p = strchr(s->name + 3, ':');
1089 if (p && !strcmp(p + 1, name) && s->value)
1093 nmem_strsplit_escape2(nmem_tmp, "|", value, &values,
1096 if (!strncmp(s->value, "rpn:", 4))
1098 const char *pqf = s->value + 4;
1100 wrbuf_puts(w_pqf, "@and ");
1101 wrbuf_puts(w_pqf, pqf);
1102 wrbuf_puts(w_pqf, " ");
1103 for (i = 0; i < num; i++)
1106 wrbuf_puts(w_pqf, "@or ");
1107 yaz_encode_pqf_term(w_pqf, values[i],
1111 else if (!strncmp(s->value, "ccl:", 4))
1113 const char *ccl = s->value + 4;
1115 wrbuf_puts(w_ccl, " and (");
1117 for (i = 0; i < num; i++)
1120 wrbuf_puts(w_ccl, " or ");
1121 wrbuf_puts(w_ccl, ccl);
1122 wrbuf_puts(w_ccl, "=\"");
1123 wrbuf_puts(w_ccl, values[i]);
1124 wrbuf_puts(w_ccl, "\"");
1126 wrbuf_puts(w_ccl, ")");
1129 else if (!strncmp(s->value, "local:", 6))
1133 yaz_log(YLOG_WARN, "Target %s: Bad limitmap '%s'",
1134 sdb->database->id, s->value);
1135 ret = -1; /* bad limitmap */
1140 nmem_reset(nmem_tmp);
1143 yaz_log(YLOG_WARN, "Target %s: limit %s used, but no limitmap defined",
1144 (sdb->database ? sdb->database->id : "<no id>"), name);
1147 nmem_destroy(nmem_tmp);
1151 // Parse the query given the settings specific to this client
1152 int client_parse_query(struct client *cl, const char *query,
1153 facet_limits_t facet_limits,
1154 const char *startrecs, const char *maxrecs)
1156 struct session *se = client_get_session(cl);
1157 struct session_database *sdb = client_get_database(cl);
1158 struct ccl_rpn_node *cn;
1160 CCL_bibset ccl_map = prepare_cclmap(cl);
1161 const char *sru = session_setting_oneval(sdb, PZ_SRU);
1162 const char *pqf_prefix = session_setting_oneval(sdb, PZ_PQF_PREFIX);
1163 const char *pqf_strftime = session_setting_oneval(sdb, PZ_PQF_STRFTIME);
1164 const char *query_syntax = session_setting_oneval(sdb, PZ_QUERY_SYNTAX);
1172 if (maxrecs && atoi(maxrecs) != cl->maxrecs)
1175 cl->maxrecs = atoi(maxrecs);
1178 if (startrecs && atoi(startrecs) != cl->startrecs)
1181 cl->startrecs = atoi(startrecs);
1184 w_ccl = wrbuf_alloc();
1185 wrbuf_puts(w_ccl, query);
1187 w_pqf = wrbuf_alloc();
1190 wrbuf_puts(w_pqf, pqf_prefix);
1191 wrbuf_puts(w_pqf, " ");
1194 if (apply_limit(sdb, facet_limits, w_pqf, w_ccl))
1197 facet_limits_destroy(cl->facet_limits);
1198 cl->facet_limits = facet_limits_dup(facet_limits);
1200 yaz_log(YLOG_LOG, "CCL query: %s", wrbuf_cstr(w_ccl));
1201 cn = ccl_find_str(ccl_map, wrbuf_cstr(w_ccl), &cerror, &cpos);
1202 ccl_qual_rm(&ccl_map);
1205 client_set_state(cl, Client_Error);
1206 session_log(se, YLOG_WARN, "Failed to parse CCL query '%s' for %s",
1209 wrbuf_destroy(w_ccl);
1210 wrbuf_destroy(w_pqf);
1213 wrbuf_destroy(w_ccl);
1215 if (!pqf_strftime || !*pqf_strftime)
1216 ccl_pquery(w_pqf, cn);
1219 time_t cur_time = time(0);
1220 struct tm *tm = localtime(&cur_time);
1222 const char *cp = tmp_str;
1224 /* see man strftime(3) for things .. In particular %% gets converted
1225 to %.. And That's our original query .. */
1226 strftime(tmp_str, sizeof(tmp_str)-1, pqf_strftime, tm);
1230 ccl_pquery(w_pqf, cn);
1232 wrbuf_putc(w_pqf, cp[0]);
1235 if (!cl->pquery || strcmp(cl->pquery, wrbuf_cstr(w_pqf)))
1238 cl->pquery = xstrdup(wrbuf_cstr(w_pqf));
1241 wrbuf_destroy(w_pqf);
1243 yaz_log(YLOG_LOG, "PQF query: %s", cl->pquery);
1245 xfree(cl->cqlquery);
1247 /* Support for PQF on SRU targets. */
1249 yaz_log(YLOG_DEBUG, "Query syntax: %s", query_syntax);
1250 if (strcmp(query_syntax, "pqf") != 0 && *sru)
1252 if (!strcmp(sru, "solr")) {
1253 if (!(cl->cqlquery = make_solrquery(cl)))
1257 if (!(cl->cqlquery = make_cqlquery(cl)))
1264 /* TODO FIX Not thread safe */
1267 // Initialize relevance structure with query terms
1268 se->relevance = relevance_create_ccl(
1269 se->service->charsets, se->nmem, cn);
1276 void client_set_session(struct client *cl, struct session *se)
1281 int client_is_active(struct client *cl)
1283 if (cl->connection && (cl->state == Client_Connecting ||
1284 cl->state == Client_Working))
1289 int client_is_active_preferred(struct client *cl)
1291 /* only count if this is a preferred target. */
1294 /* TODO No sure this the condition that Seb wants */
1295 if (cl->connection && (cl->state == Client_Connecting ||
1296 cl->state == Client_Working))
1301 Odr_int client_get_hits(struct client *cl)
1306 int client_get_num_records(struct client *cl)
1308 return cl->record_offset;
1311 void client_set_diagnostic(struct client *cl, int diagnostic,
1312 const char *addinfo)
1314 cl->diagnostic = diagnostic;
1318 cl->addinfo = xstrdup(addinfo);
1321 int client_get_diagnostic(struct client *cl, const char **addinfo)
1324 *addinfo = cl->addinfo;
1325 return cl->diagnostic;
1328 const char * client_get_suggestions_xml(struct client *cl, WRBUF wrbuf)
1331 struct suggestions *suggestions = cl->suggestions;
1334 //yaz_log(YLOG_DEBUG, "No suggestions found");
1337 if (suggestions->passthrough) {
1338 yaz_log(YLOG_DEBUG, "Passthrough Suggestions: \n%s\n", suggestions->passthrough);
1339 return suggestions->passthrough;
1341 if (suggestions->num == 0) {
1345 for (idx = 0; idx < suggestions->num; idx++) {
1346 wrbuf_printf(wrbuf, "<suggest term=\"%s\"", suggestions->suggest[idx]);
1347 if (suggestions->misspelled[idx] && suggestions->misspelled[idx]) {
1348 wrbuf_puts(wrbuf, suggestions->misspelled[idx]);
1349 wrbuf_puts(wrbuf, "</suggest>\n");
1352 wrbuf_puts(wrbuf, "/>\n");
1355 return wrbuf_cstr(wrbuf);
1359 void client_set_database(struct client *cl, struct session_database *db)
1364 const char *client_get_id(struct client *cl)
1369 int client_get_maxrecs(struct client *cl)
1374 void client_set_preferred(struct client *cl, int v)
1380 struct suggestions* client_suggestions_create(const char* suggestions_string)
1384 struct suggestions *suggestions;
1385 if (suggestions_string == 0)
1387 nmem = nmem_create();
1388 suggestions = nmem_malloc(nmem, sizeof(*suggestions));
1389 yaz_log(YLOG_DEBUG, "client target suggestions: %s", suggestions_string);
1391 suggestions->nmem = nmem;
1392 suggestions->num = 0;
1393 suggestions->misspelled = 0;
1394 suggestions->suggest = 0;
1395 suggestions->passthrough = nmem_strdup_null(nmem, suggestions_string);
1397 if (suggestions_string)
1398 nmem_strsplit_escape2(suggestions->nmem, "\n", suggestions_string, &suggestions->suggest,
1399 &suggestions->num, 1, '\\', 0);
1400 /* Set up misspelled array */
1401 suggestions->misspelled = (char **) nmem_malloc(nmem, suggestions->num * sizeof(**suggestions->misspelled));
1402 /* replace = with \0 .. for each item */
1403 for (i = 0; i < suggestions->num; i++)
1405 char *cp = strchr(suggestions->suggest[i], '=');
1408 suggestions->misspelled[i] = cp+1;
1414 static void client_suggestions_destroy(struct client *cl)
1416 NMEM nmem = cl->suggestions->nmem;
1417 cl->suggestions = 0;
1424 * c-file-style: "Stroustrup"
1425 * indent-tabs-mode: nil
1427 * vim: shiftwidth=4 tabstop=8 expandtab