import EntryStore from './EntryStore.js';
import List from './List.js';
import factory from './factory.js';
import Entry from './Entry.js';
/**
* @exports store/SearchList
*/
export default class SearchList {
/**
* @param {EntryStore} entryStore
* @param {Object} query
* @param {string} callType parameter provided to asyncListeners on query execution,
* assumed to be 'search' if left out
*/
constructor(entryStore, query, callType) {
this._entryStore = entryStore;
this._query = query;
this._callType = callType || 'search';
this._sortedChildren = [];
}
/**
* the query instance, e.g. an instance of SolrQuery, associated with this SearchList.
* @returns {Object}
*/
getQuery() {
return this._query;
}
setLimit(limit) {
this._query.limit(limit);
}
getLimit() {
return this._query.getLimit() || factory.getDefaultLimit();
}
/**
* Array of facets, each facet is an object which contains the following fields:
*
* name - the solr search field this facet corresponds to
* valueCount - the number of values this facet matches
* values - an array of values matching this facet, with name and count each.
*
* In addition, when asking for facets for arbitrary predicates there is two additional fields:
*
* predicate - the full URI of the predicate this facet corresponds to
* type - the type of the facet values, currently only literal, uri and integer are supported
*
* Note that you can ask for facets for solr fields like "tags", in this case the above two
* fields will be not be present as the "tags" solr field corresponds to a mix of different
* predicates and possibly different types.
*
* Here is an example of a facet for dcat:keyword:
* {
* name: "metadata.predicate.literal_s.a6424133",
* predicate: "http://www.w3.org/ns/dcat#keyword",
* type: "literal",
* valueCount: 3,
* values: [
* {name: "elektrictet", count: 1},
* {name: "finans", count: 1},
* {name: "skatt", count: 1}
* ]
* }
*
* @return {Array} never null or undefined, may be an array of length 0 though.
*/
getFacets() {
return this.facets;
}
setFacets(facetFields) {
if (!Array.isArray(facetFields) || facetFields.length === 0) {
this.facets = [];
} else {
this.facets = facetFields;
const f2p = this._query.facet2predicate;
facetFields.forEach((ff) => {
if (f2p && f2p[ff.name]) {
ff.predicate = f2p[ff.name];
}
if (ff.name.startsWith('metadata.predicate')) {
ff.type = ff.name.split('.')[2];
} else if (ff.name.startsWith('related.metadata.predicate')) {
ff.type = ff.name.split('.')[3];
}
});
}
}
/**
*
* Add an entry to the current search (result) list.
* Utilizes the this._sortedChildren of the {@see List} for the actual entries in memory.
*
* @param {Entry} entry
* @param {boolean} [first=true] add on top of the list
* @return {SearchList}
*/
addEntry(entry, first = true) {
if (entry && !(entry instanceof Entry)) {
return this;
}
const limit = this.getLimit();
if (first) {
this._sortedChildren.unshift(entry.getURI());
} else {
this._sortedChildren[this._size - 1] = entry.getURI(); // @todo perhaps there's no use case for this
}
this._size += 1; // increase list size
return this;
}
/**
* Remove an entry from the current search (result) list.
* This can cause a request for the next page load in order to maintain a consistent number
* of entries in memory (or rather in a front-end view)
* Utilizes the this._sortedChildren of the {@see List} for the actual entries in memory.
*
* @param {Entry} entry
* @return {Promise<SearchList>}
*/
async removeEntry(entry) {
if (!entry || (entry && !(entry instanceof Entry))) {
return this;
}
const limit = this.getLimit();
if (this._sortedChildren.length === limit && this._sortedChildren.length < this._size) {
const nextPageToLoad = this._sortedChildren.length / limit;
try {
await this.getEntries(nextPageToLoad);
} catch (err) {
console.warn(`Failed to load search list's next page ${nextPageToLoad}`);
console.error(err);
}
}
const sortedIndex = this._sortedChildren.indexOf(entry.getURI());
if (sortedIndex !== -1) {
this._sortedChildren.splice(sortedIndex, 1);
}
this._size -= 1; // decrease list size
return this;
}
/**
*
* @param {number} [page=0]
* @return {Promise}
* @private
*/
_forceLoadEntries(page = 0) {
const offset = page * this.getLimit();
this._query.offset(offset);
// Only prevent cache if we have turned on prevent request caching and are making a public search query.
const preventCache = this._entryStore.getRequestCachePrevention() && this._query.params.get('public') === 'true';
return this._entryStore.handleAsync(this._entryStore.getREST().get(this._query.getQuery(this._entryStore),
undefined, undefined, undefined, preventCache)
.then((data) => {
this.setFacets(data.facetFields);
return factory.extractSearchResults(data, this, this._entryStore);
}), this._callType);
}
}
/**
* Get size of list.
*
* @returns {number} the amount of entries in the list, -1 if unknown.
*/
SearchList.prototype.getSize = List.prototype.getSize;
/**
* Retrieves an array of entries contained in this list according to the current page and
* pagination settings.
*
* @param {integer} page - the page to request an array of entries for, first page is numbered 0.
* @returns {Promise.<Entry[]>} the promise will return an entry-array.
* @method
*/
SearchList.prototype.getEntries = List.prototype.getEntries;
/**
* Executes a callback on each list members in the order provided by the solr-search.
* If the provided function return false for one entry the iteration is stopped and
* the function is not called for consecutive matched entries.
*
* @param {Function} func
* @returns {Promise}
*/
SearchList.prototype.forEach = List.prototype.forEach;
/**
* Loops through with forEach and accumulates the entries in a single array.
* Note! this might be memory intensive for large lists
* @see forEach
* @returns {Promise.<Entry[]>}
*/
SearchList.prototype.getAllEntries = List.prototype.getAllEntries;
SearchList.prototype._getEntries = List.prototype._getEntries;
SearchList.prototype._update = List.prototype._update;