From 7996365aa3db8662fd7f7aaf4c5089720522778a Mon Sep 17 00:00:00 2001 From: Adam Dickmeiss Date: Fri, 7 Nov 2014 15:18:39 +0100 Subject: [PATCH] prefix, criteria, index and field taken into account missing is "criteria.optional" --- src/sparql.c | 180 ++++++++++++++++++++++++++++++++++++++++---- test/test_sparql.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 375 insertions(+), 17 deletions(-) diff --git a/src/sparql.c b/src/sparql.c index 768b692..fb6c302 100644 --- a/src/sparql.c +++ b/src/sparql.c @@ -65,13 +65,148 @@ int yaz_sparql_from_rpn_wrbuf(yaz_sparql_t s, WRBUF addinfo, WRBUF w, return yaz_sparql_from_rpn_stream(s, addinfo, wrbuf_vp_puts, w, q); } +static Odr_int lookup_attr_numeric(Z_AttributeList *attributes, int type) +{ + int j; + for (j = 0; j < attributes->num_attributes; j++) + { + Z_AttributeElement *ae = attributes->attributes[j]; + if (*ae->attributeType == type) + { + if (ae->which == Z_AttributeValue_numeric) + return *ae->value.numeric; + } + } + return 0; +} + +static const char *lookup_attr_string(Z_AttributeList *attributes, int type) +{ + int j; + for (j = 0; j < attributes->num_attributes; j++) + { + Z_AttributeElement *ae = attributes->attributes[j]; + if (*ae->attributeType == type) + { + if (ae->which == Z_AttributeValue_complex) + { + Z_ComplexAttribute *ca = ae->value.complex; + int i; + for (i = 0; i < ca->num_list; i++) + { + Z_StringOrNumeric *son = ca->list[i]; + if (son->which == Z_StringOrNumeric_string) + return son->u.string; + } + } + } + } + return 0; +} static int apt(yaz_sparql_t s, WRBUF addinfo, void (*pr)(const char *buf, void *client_data), void *client_data, - Z_AttributesPlusTerm *q) + Z_AttributesPlusTerm *q, int indent) { + Z_Term *term = q->term; + Odr_int v = lookup_attr_numeric(q->attributes, 1); + struct sparql_entry *e = 0; + const char *cp; + int i; + + pr(" ", client_data); + for (i = 0; i < indent; i++) + pr(" ", client_data); + + if (v) + { + for (e = s->conf; e; e = e->next) + { + if (!strncmp(e->pattern, "index.", 6)) + { + char *end = 0; + Odr_int w = odr_strtol(e->pattern + 6, &end, 10); + + if (end && *end == '\0' && v == w) + break; + } + } + if (!e) + { + wrbuf_printf(addinfo, ODR_INT_PRINTF, v); + return YAZ_BIB1_UNSUPP_USE_ATTRIBUTE; + } + } + else + { + const char *index_name = lookup_attr_string(q->attributes, 1); + if (!index_name) + index_name = "any"; + for (e = s->conf; e; e = e->next) + { + if (!strncmp(e->pattern, "index.", 6)) + { + if (!strcmp(e->pattern + 6, index_name)) + break; + } + } + if (!e) + { + wrbuf_puts(addinfo, index_name); + return YAZ_BIB1_UNSUPP_USE_ATTRIBUTE; + } + } + assert(e); + wrbuf_rewind(addinfo); + for (cp = e->value; *cp; cp++) + { + if (*cp == '%') + { + switch (*++cp) + { + case 's': + wrbuf_puts(addinfo, "\""); + switch (term->which) + { + case Z_Term_general: + wrbuf_json_write(addinfo, + term->u.general->buf, term->u.general->len); + break; + case Z_Term_numeric: + wrbuf_printf(addinfo, ODR_INT_PRINTF, *term->u.numeric); + break; + case Z_Term_characterString: + wrbuf_json_puts(addinfo, term->u.characterString); + break; + } + wrbuf_puts(addinfo, "\""); + break; + case 'd': + switch (term->which) + { + case Z_Term_general: + wrbuf_write(addinfo, + term->u.general->buf, term->u.general->len); + break; + case Z_Term_numeric: + wrbuf_printf(addinfo, ODR_INT_PRINTF, *term->u.numeric); + break; + case Z_Term_characterString: + wrbuf_puts(addinfo, term->u.characterString); + break; + } + break; + case '%': + wrbuf_putc(addinfo, '%'); + break; + } + } + else + wrbuf_putc(addinfo, *cp); + } + pr(wrbuf_cstr(addinfo), client_data); return 0; } @@ -81,8 +216,9 @@ static int rpn_structure(yaz_sparql_t s, void (*pr)(const char *buf, void *client_data), void *client_data, - Z_RPNStructure *q) + Z_RPNStructure *q, int indent) { + int i; if (q->which == Z_RPNStructure_complex) { int r; @@ -90,21 +226,33 @@ static int rpn_structure(yaz_sparql_t s, Z_Operator *op = c->roperator; if (op->which == Z_Operator_and) { - r = rpn_structure(s, addinfo, pr, client_data, c->s1); + r = rpn_structure(s, addinfo, pr, client_data, c->s1, + indent); if (r) return r; pr(" .\n", client_data); - return rpn_structure(s, addinfo, pr, client_data, c->s2); + return rpn_structure(s, addinfo, pr, client_data, c->s2, + indent); } else if (op->which == Z_Operator_or) { - pr("{\n", client_data); - r = rpn_structure(s, addinfo, pr, client_data, c->s1); + for (i = 0; i < indent; i++) + pr(" ", client_data); + pr(" {\n", client_data); + r = rpn_structure(s, addinfo, pr, client_data, c->s1, + indent + 1); if (r) return r; - pr("} UNION {\n", client_data); - r = rpn_structure(s, addinfo, pr, client_data, c->s1); - pr("}\n", client_data); + pr("\n", client_data); + for (i = 0; i < indent; i++) + pr(" ", client_data); + pr(" } UNION {\n", client_data); + r = rpn_structure(s, addinfo, pr, client_data, c->s2, + indent + 1); + pr("\n", client_data); + for (i = 0; i < indent; i++) + pr(" ", client_data); + pr(" }", client_data); return r; } else @@ -116,7 +264,8 @@ static int rpn_structure(yaz_sparql_t s, { Z_Operand *op = q->u.simple; if (op->which == Z_Operand_APT) - return apt(s, addinfo, pr, client_data, op->u.attributesPlusTerm); + return apt(s, addinfo, pr, client_data, op->u.attributesPlusTerm, + indent); else return YAZ_BIB1_RESULT_SET_UNSUPP_AS_A_SEARCH_TERM; } @@ -132,7 +281,7 @@ int yaz_sparql_from_rpn_stream(yaz_sparql_t s, { struct sparql_entry *e; yaz_tok_cfg_t cfg = yaz_tok_cfg_create(); - int errors = 0; + int r = 0, errors = 0; for (e = s->conf; e; e = e->next) { @@ -168,7 +317,7 @@ int yaz_sparql_from_rpn_stream(yaz_sparql_t s, { ; } - else if (!strncmp(e->pattern, "index", 5)) + else if (!strncmp(e->pattern, "index.", 6)) { ; } @@ -202,12 +351,13 @@ int yaz_sparql_from_rpn_stream(yaz_sparql_t s, pr(" .\n", client_data); } } - rpn_structure(s, addinfo, pr, client_data, q->RPNStructure); + if (!errors) + r = rpn_structure(s, addinfo, pr, client_data, q->RPNStructure, 0); - pr("}\n", client_data); + pr("\n}\n", client_data); yaz_tok_cfg_destroy(cfg); - return errors ? -1 : 0; + return errors ? -1 : r; } /* diff --git a/test/test_sparql.c b/test/test_sparql.c index b25792a..6ead793 100644 --- a/test/test_sparql.c +++ b/test/test_sparql.c @@ -71,6 +71,8 @@ static void tst1(void) "rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns"); yaz_sparql_add_pattern(s, "prefix", "bf: "); + yaz_sparql_add_pattern(s, "prefix", + "gs: http://gs.com/panorama/domain-model"); yaz_sparql_add_pattern(s, "field.title", "?title"); yaz_sparql_add_pattern(s, "field.author", "?author"); yaz_sparql_add_pattern(s, "field.description", "?description"); @@ -81,10 +83,33 @@ static void tst1(void) yaz_sparql_add_pattern(s, "criteria", "?work bf:note ?description"); yaz_sparql_add_pattern(s, "criteria", "?inst bf:instanceOf ?work"); yaz_sparql_add_pattern(s, "criteria", "?inst bf:instanceTitle/bf:titleValue ?ititle"); + yaz_sparql_add_pattern(s, "index.bf.title", + "?work bf:workTitle/bf:titleValue ?o1 " + "FILTER(contains(?o1, %s))"); + yaz_sparql_add_pattern(s, "index.bf.creator", + "?work bf:creator/bf:label ?o2 " + "FILTER(contains(?o2, %s))"); + yaz_sparql_add_pattern(s, "index.bf.authorityCreator", + "?work bf:author %s"); + yaz_sparql_add_pattern(s, "index.bf.type", + "?inst rdf:type %s"); + yaz_sparql_add_pattern(s, "index.bf.format", + "?inst bf:format ?o5 FILTER(contains(?o5, %s))"); + yaz_sparql_add_pattern(s, "index.bf.nearby", "?lib gs:nearby (%d)"); + yaz_sparql_add_pattern(s, "index.bf.baseTitle", + "?work bf:derivativeOf/bf:workTitle/bf:titleValue " + "?o6 FILTER(contains(?o6, %s))"); + yaz_sparql_add_pattern(s, "index.bf.baseCreator", + "?work bf:derivativeOf/bf:creator/bf:label " + "?o7 FILTER(contains(?o7, %s))"); + yaz_sparql_add_pattern(s, "index.bf.targetAudience", + "?work bf:targetAudience %s"); + yaz_sparql_add_pattern(s, "index.bf.isbn", "?inst bf:ISBN %s"); YAZ_CHECK(test_query( - s, "computer", + s, "@attr 1=bf.title computer", "PREFIX rdf: \n" "PREFIX bf: \n" + "PREFIX gs: \n" "\n" "SELECT ?title ?author ?description ?ititle\n" "WHERE {\n" @@ -94,8 +119,191 @@ static void tst1(void) " ?work bf:note ?description .\n" " ?inst bf:instanceOf ?work .\n" " ?inst bf:instanceTitle/bf:titleValue ?ititle .\n" + " ?work bf:workTitle/bf:titleValue ?o1 " + "FILTER(contains(?o1, \"computer\"))\n" "}\n" -)); + )); + + YAZ_CHECK(test_query( + s, "@attr 1=bf.creator london", + "PREFIX rdf: \n" + "PREFIX bf: \n" + "PREFIX gs: \n" + "\n" + "SELECT ?title ?author ?description ?ititle\n" + "WHERE {\n" + " ?work a bf:Work .\n" + " ?work bf:workTitle/bf:titleValue ?title .\n" + " ?work bf:creator/bf:label ?author .\n" + " ?work bf:note ?description .\n" + " ?inst bf:instanceOf ?work .\n" + " ?inst bf:instanceTitle/bf:titleValue ?ititle .\n" + " ?work bf:creator/bf:label ?o2 " + "FILTER(contains(?o2, \"london\"))\n" + "}\n")); + + + YAZ_CHECK(test_query( + s, "@and @attr 1=bf.creator london @attr 1=bf.title computer", + "PREFIX rdf: \n" + "PREFIX bf: \n" + "PREFIX gs: \n" + "\n" + "SELECT ?title ?author ?description ?ititle\n" + "WHERE {\n" + " ?work a bf:Work .\n" + " ?work bf:workTitle/bf:titleValue ?title .\n" + " ?work bf:creator/bf:label ?author .\n" + " ?work bf:note ?description .\n" + " ?inst bf:instanceOf ?work .\n" + " ?inst bf:instanceTitle/bf:titleValue ?ititle .\n" + " ?work bf:creator/bf:label ?o2 " + "FILTER(contains(?o2, \"london\")) .\n" + " ?work bf:workTitle/bf:titleValue ?o1 " + "FILTER(contains(?o1, \"computer\"))\n" + "}\n")); + + YAZ_CHECK(test_query( + s, "@or @attr 1=bf.creator london @attr 1=bf.title computer", + "PREFIX rdf: \n" + "PREFIX bf: \n" + "PREFIX gs: \n" + "\n" + "SELECT ?title ?author ?description ?ititle\n" + "WHERE {\n" + " ?work a bf:Work .\n" + " ?work bf:workTitle/bf:titleValue ?title .\n" + " ?work bf:creator/bf:label ?author .\n" + " ?work bf:note ?description .\n" + " ?inst bf:instanceOf ?work .\n" + " ?inst bf:instanceTitle/bf:titleValue ?ititle .\n" + " {\n" + " ?work bf:creator/bf:label ?o2 " + "FILTER(contains(?o2, \"london\"))\n" + " } UNION {\n" + " ?work bf:workTitle/bf:titleValue ?o1 " + "FILTER(contains(?o1, \"computer\"))\n" + " }\n" + "}\n" + )); + + YAZ_CHECK(test_query( + s, "@or @or @attr 1=bf.creator a @attr 1=bf.title b @attr 1=bf.title c", + "PREFIX rdf: \n" + "PREFIX bf: \n" + "PREFIX gs: \n" + "\n" + "SELECT ?title ?author ?description ?ititle\n" + "WHERE {\n" + " ?work a bf:Work .\n" + " ?work bf:workTitle/bf:titleValue ?title .\n" + " ?work bf:creator/bf:label ?author .\n" + " ?work bf:note ?description .\n" + " ?inst bf:instanceOf ?work .\n" + " ?inst bf:instanceTitle/bf:titleValue ?ititle .\n" + " {\n" + " {\n" + " ?work bf:creator/bf:label ?o2 " + "FILTER(contains(?o2, \"a\"))\n" + " } UNION {\n" + " ?work bf:workTitle/bf:titleValue ?o1 " + "FILTER(contains(?o1, \"b\"))\n" + " }\n" + " } UNION {\n" + " ?work bf:workTitle/bf:titleValue ?o1 " + "FILTER(contains(?o1, \"c\"))\n" + " }\n" + "}\n" + )); + + YAZ_CHECK(test_query( + s, "@or @and @attr 1=bf.creator a @attr 1=bf.title b @attr 1=bf.title c", + "PREFIX rdf: \n" + "PREFIX bf: \n" + "PREFIX gs: \n" + "\n" + "SELECT ?title ?author ?description ?ititle\n" + "WHERE {\n" + " ?work a bf:Work .\n" + " ?work bf:workTitle/bf:titleValue ?title .\n" + " ?work bf:creator/bf:label ?author .\n" + " ?work bf:note ?description .\n" + " ?inst bf:instanceOf ?work .\n" + " ?inst bf:instanceTitle/bf:titleValue ?ititle .\n" + " {\n" + " ?work bf:creator/bf:label ?o2 " + "FILTER(contains(?o2, \"a\")) .\n" + " ?work bf:workTitle/bf:titleValue ?o1 " + "FILTER(contains(?o1, \"b\"))\n" + " } UNION {\n" + " ?work bf:workTitle/bf:titleValue ?o1 " + "FILTER(contains(?o1, \"c\"))\n" + " }\n" + "}\n" + )); + + YAZ_CHECK(test_query( + s, "@and @and @attr 1=bf.creator a @attr 1=bf.title b @attr 1=bf.title c", + "PREFIX rdf: \n" + "PREFIX bf: \n" + "PREFIX gs: \n" + "\n" + "SELECT ?title ?author ?description ?ititle\n" + "WHERE {\n" + " ?work a bf:Work .\n" + " ?work bf:workTitle/bf:titleValue ?title .\n" + " ?work bf:creator/bf:label ?author .\n" + " ?work bf:note ?description .\n" + " ?inst bf:instanceOf ?work .\n" + " ?inst bf:instanceTitle/bf:titleValue ?ititle .\n" + " ?work bf:creator/bf:label ?o2 " + "FILTER(contains(?o2, \"a\")) .\n" + " ?work bf:workTitle/bf:titleValue ?o1 " + "FILTER(contains(?o1, \"b\")) .\n" + " ?work bf:workTitle/bf:titleValue ?o1 " + "FILTER(contains(?o1, \"c\"))\n" + "}\n" + )); + + YAZ_CHECK(test_query( + s, "@and @attr 1=bf.title \"Phantom Tollbooth\" " + "@attr 1=bf.nearby \"40.1583 83.0742 30\"", + "PREFIX rdf: \n" + "PREFIX bf: \n" + "PREFIX gs: \n" + "\n" + "SELECT ?title ?author ?description ?ititle\n" + "WHERE {\n" + " ?work a bf:Work .\n" + " ?work bf:workTitle/bf:titleValue ?title .\n" + " ?work bf:creator/bf:label ?author .\n" + " ?work bf:note ?description .\n" + " ?inst bf:instanceOf ?work .\n" + " ?inst bf:instanceTitle/bf:titleValue ?ititle .\n" + " ?work bf:workTitle/bf:titleValue ?o1 " + "FILTER(contains(?o1, \"Phantom Tollbooth\")) .\n" + " ?lib gs:nearby (40.1583 83.0742 30)\n" + "}\n" + )); + + YAZ_CHECK(test_query( + s, "@attr 1=bf.isbn 9780316154697", + "PREFIX rdf: \n" + "PREFIX bf: \n" + "PREFIX gs: \n" + "\n" + "SELECT ?title ?author ?description ?ititle\n" + "WHERE {\n" + " ?work a bf:Work .\n" + " ?work bf:workTitle/bf:titleValue ?title .\n" + " ?work bf:creator/bf:label ?author .\n" + " ?work bf:note ?description .\n" + " ?inst bf:instanceOf ?work .\n" + " ?inst bf:instanceTitle/bf:titleValue ?ititle .\n" + " ?inst bf:ISBN \"9780316154697\"\n" + "}\n" + )); + yaz_sparql_destroy(s); } -- 1.7.10.4