+static void append_merge_keys(struct record_metadata_attr **p,
+ const struct record_metadata_attr *a,
+ NMEM nmem)
+{
+ for (; a; a = a->next)
+ {
+ struct record_metadata_attr **pi = p;
+ for (; *pi; pi = &(*pi)->next)
+ if (!strcmp((*pi)->value, a->value))
+ break;
+ if (!*pi)
+ {
+ *pi = (struct record_metadata_attr *) nmem_malloc(nmem, sizeof(**p));
+ (*pi)->name = nmem_strdup_null(nmem, a->name);
+ (*pi)->value = nmem_strdup_null(nmem, a->value);
+ (*pi)->next = 0;
+ }
+ }
+}
+
+struct reclist_sortparms *reclist_parse_sortparms(NMEM nmem, const char *parms,
+ struct conf_service *service)
+{
+ struct reclist_sortparms *res = 0;
+ struct reclist_sortparms **rp = &res;
+
+ if (strlen(parms) > 256)
+ return 0;
+ while (*parms)
+ {
+ char parm[256];
+ char *pp;
+ const char *cpp;
+ int increasing = 0;
+ int i;
+ int offset = 0;
+ enum conf_metadata_type type = Metadata_type_generic;
+ struct reclist_sortparms *new;
+
+ if (!(cpp = strchr(parms, ',')))
+ cpp = parms + strlen(parms);
+ strncpy(parm, parms, cpp - parms);
+ parm[cpp-parms] = '\0';
+
+ if ((pp = strchr(parm, ':')))
+ {
+ if (pp[1] == '1')
+ increasing = 1;
+ else if (pp[1] == '0')
+ increasing = 0;
+ else
+ {
+ yaz_log(YLOG_FATAL, "Bad sortkey modifier: %s", parm);
+ return 0;
+ }
+
+ if (pp[2])
+ {
+ if (pp[2] == 'p')
+ type = Metadata_type_position;
+ else
+ yaz_log(YLOG_FATAL, "Bad sortkey modifier: %s", parm);
+ }
+ *pp = '\0';
+ }
+ if (type != Metadata_type_position)
+ {
+ if (!strcmp(parm, "relevance"))
+ {
+ type = Metadata_type_relevance;
+ }
+ else if (!strcmp(parm, "position"))
+ {
+ type = Metadata_type_position;
+ }
+ else
+ {
+ for (i = 0; i < service->num_sortkeys; i++)
+ {
+ struct conf_sortkey *sk = &service->sortkeys[i];
+ if (!strcmp(sk->name, parm))
+ {
+ type = sk->type;
+ break;
+ }
+ }
+ if (i >= service->num_sortkeys)
+ {
+ yaz_log(YLOG_FATAL, "Sortkey not defined in service: %s",
+ parm);
+ return 0;
+ }
+ offset = i;
+ }
+ }
+ new = *rp = nmem_malloc(nmem, sizeof(struct reclist_sortparms));
+ new->next = 0;
+ new->offset = offset;
+ new->type = type;
+ new->increasing = increasing;
+ new->name = nmem_strdup(nmem, parm);
+ rp = &new->next;
+ if (*(parms = cpp))
+ parms++;
+ }
+ return res;
+}
+
+static int reclist_cmp(const void *p1, const void *p2)
+{
+ struct record_cluster *r1 = (*(struct record_cluster**) p1);
+ struct record_cluster *r2 = (*(struct record_cluster**) p2);
+ struct reclist_sortparms *sortparms = r1->sort_parms;
+ struct reclist_sortparms *s;
+ int res = 0;
+
+ for (s = sortparms; s && res == 0; s = s->next)
+ {
+ union data_types *ut1 = r1->sortkeys[s->offset];
+ union data_types *ut2 = r2->sortkeys[s->offset];
+ const char *s1, *s2;
+ switch (s->type)
+ {
+ case Metadata_type_relevance:
+ res = r1->relevance_score - r2->relevance_score;
+ break;
+ case Metadata_type_generic:
+ case Metadata_type_skiparticle:
+ s1 = ut1 ? ut1->text.sort : "";
+ s2 = ut2 ? ut2->text.sort : "";
+ res = strcmp(s1, s2);
+ break;
+ case Metadata_type_year:
+ case Metadata_type_date:
+ if (ut1 && ut2)
+ {
+ if (s->increasing)
+ res = ut1->number.min - ut2->number.min;
+ else
+ res = ut1->number.max - ut2->number.max;
+ }
+ else if (ut1 && !ut2)
+ {
+ res = -1; /* without date/year: last! */
+ continue;
+ }
+ else if (!ut1 && ut2)
+ {
+ res = 1; /* without date/year: last! */
+ continue;
+ }
+ else
+ res = 0;
+ break;
+ case Metadata_type_position:
+ if (r1->records && r2->records)
+ {
+ int pos1 = 0, pos2 = 0;
+ struct record *rec;
+ for (rec = r1->records; rec; rec = rec->next)
+ if (pos1 == 0 || rec->position < pos1)
+ pos1 = rec->position;
+ for (rec = r2->records; rec; rec = rec->next)
+ if (pos2 == 0 || rec->position < pos2)
+ pos2 = rec->position;
+ res = pos1 - pos2;
+ continue;
+ }
+ break;
+ case Metadata_type_float:
+ if (ut1 && ut2)
+ {
+ if (ut1->fnumber == ut2->fnumber)
+ res = 0;
+ else if (ut1->fnumber > ut2->fnumber)
+ res = 1;
+ else
+ res = -1;
+ }
+ else if (ut1)
+ res = 1;
+ else if (ut2)
+ res = -1;
+ else
+ res = 0;
+ break;
+ }
+ if (res && !s->increasing)
+ res *= -1;
+ }
+ if (res == 0)
+ res = strcmp(r1->recid, r2->recid);
+ return res;
+}
+
+void reclist_limit(struct reclist *l, struct session *se, int lazy)
+{
+ unsigned i;
+ int num = 0;
+ struct record_cluster **pp = &l->sorted_list;
+
+ reclist_enter(l);
+
+ if (!lazy || !*pp)
+ {
+ for (i = 0; i < l->hash_size; i++)
+ {
+ struct reclist_bucket *p;
+ for (p = l->hashtable[i]; p; p = p->hash_next)
+ p->record->sorted_next = 0;
+ }
+ for (i = 0; i < l->hash_size; i++)
+ {
+ struct reclist_bucket *p;
+ for (p = l->hashtable[i]; p; p = p->hash_next)
+ {
+ if (session_check_cluster_limit(se, p->record))
+ {
+ if (!p->record->sorted_next)
+ {
+ *pp = p->record;
+ pp = &p->record->sorted_next;
+ *pp = p->record; /* signal already in use */
+ num++;
+ }
+ }
+ }
+ }
+ *pp = 0;
+ }
+ l->num_records = num;
+ reclist_leave(l);
+}
+
+void reclist_sort(struct reclist *l, struct reclist_sortparms *parms)
+{
+ struct record_cluster **flatlist = xmalloc(sizeof(*flatlist) * l->num_records);
+ struct record_cluster *ptr;
+ struct record_cluster **prev;
+ int i = 0;
+
+ reclist_enter(l);
+
+ ptr = l->sorted_list;
+ prev = &l->sorted_list;
+ while (ptr)
+ {
+ ptr->sort_parms = parms;
+ flatlist[i] = ptr;
+ ptr = ptr->sorted_next;
+ i++;
+ }
+ assert(i == l->num_records);
+
+ qsort(flatlist, l->num_records, sizeof(*flatlist), reclist_cmp);
+ for (i = 0; i < l->num_records; i++)
+ {
+ *prev = flatlist[i];
+ prev = &flatlist[i]->sorted_next;
+ }
+ *prev = 0;
+
+ xfree(flatlist);
+
+ reclist_leave(l);
+}
+