+static struct record_metadata *record_metadata_init(
+ NMEM nmem, char *value, enum conf_metadata_type type)
+{
+ struct record_metadata *rec_md = record_metadata_create(nmem);
+ if (type == Metadata_type_generic)
+ {
+ char * p = value;
+ p = normalize7bit_generic(p, " ,/.:([");
+
+ rec_md->data.text.disp = nmem_strdup(nmem, p);
+ rec_md->data.text.sort = 0;
+ }
+ else if (type == Metadata_type_year || type == Metadata_type_date)
+ {
+ int first, last;
+ int longdate = 0;
+
+ if (type == Metadata_type_date)
+ longdate = 1;
+ if (extract7bit_dates((char *) value, &first, &last, longdate) < 0)
+ return 0;
+
+ rec_md->data.number.min = first;
+ rec_md->data.number.max = last;
+ }
+ else
+ return 0;
+ return rec_md;
+}
+
+const char *get_mergekey(xmlDoc *doc, struct client *cl, int record_no,
+ struct conf_service *service, NMEM nmem)
+{
+ char *mergekey_norm = 0;
+ xmlNode *root = xmlDocGetRootElement(doc);
+ WRBUF norm_wr = wrbuf_alloc();
+ xmlNode *n;
+
+ /* create mergekey based on mergekey attribute from XSL (if any) */
+ xmlChar *mergekey = xmlGetProp(root, (xmlChar *) "mergekey");
+ if (mergekey)
+ {
+ const char *norm_str;
+ pp2_relevance_token_t prt =
+ pp2_relevance_tokenize(
+ global_parameters.server->mergekey_pct,
+ (const char *) mergekey);
+
+ while ((norm_str = pp2_relevance_token_next(prt)))
+ {
+ if (*norm_str)
+ {
+ if (wrbuf_len(norm_wr))
+ wrbuf_puts(norm_wr, " ");
+ wrbuf_puts(norm_wr, norm_str);
+ }
+ }
+ pp2_relevance_token_destroy(prt);
+ xmlFree(mergekey);
+ }
+ /* append (if any) mergekey=yes metadata values */
+ for (n = root->children; n; n = n->next)
+ {
+ if (n->type != XML_ELEMENT_NODE)
+ continue;
+ if (!strcmp((const char *) n->name, "metadata"))
+ {
+ struct conf_metadata *ser_md = 0;
+ int md_field_id = -1;
+
+ xmlChar *type = xmlGetProp(n, (xmlChar *) "type");
+
+ if (!type)
+ continue;
+
+ md_field_id
+ = conf_service_metadata_field_id(service,
+ (const char *) type);
+ if (md_field_id >= 0)
+ {
+ ser_md = &service->metadata[md_field_id];
+ if (ser_md->mergekey == Metadata_mergekey_yes)
+ {
+ xmlChar *value = xmlNodeListGetString(doc, n->children, 1);
+ if (value)
+ {
+ const char *norm_str;
+ pp2_relevance_token_t prt =
+ pp2_relevance_tokenize(
+ global_parameters.server->mergekey_pct,
+ (const char *) value);
+
+ while ((norm_str = pp2_relevance_token_next(prt)))
+ {
+ if (*norm_str)
+ {
+ if (wrbuf_len(norm_wr))
+ wrbuf_puts(norm_wr, " ");
+ wrbuf_puts(norm_wr, norm_str);
+ }
+ }
+ xmlFree(value);
+ pp2_relevance_token_destroy(prt);
+ }
+ }
+ }
+ xmlFree(type);
+ }
+ }
+
+ /* generate unique key if none is not generated already or is empty */
+ if (wrbuf_len(norm_wr) == 0)
+ {
+ wrbuf_printf(norm_wr, "%s-%d",
+ client_get_database(cl)->database->url, record_no);
+ }
+ if (wrbuf_len(norm_wr) > 0)
+ mergekey_norm = nmem_strdup(nmem, wrbuf_cstr(norm_wr));
+ wrbuf_destroy(norm_wr);
+ return mergekey_norm;
+}
+
+
+
+/** \brief ingest XML record
+ \param cl client holds the result set for record
+ \param rec record buffer (0 terminated)
+ \param record_no record position (1, 2, ..)
+ \returns resulting record or NULL on failure
+*/
+struct record *ingest_record(struct client *cl, const char *rec,