UNPKG

search-index

Version:

A network resilient, persistent full-text search library for the browser and Node.js

142 lines (135 loc) 3.94 kB
import { Count } from './Count.js' import { Hits } from './Hits.js' import { Paging } from './Paging.js' import { Facet } from './Facet.js' import { SearchInput } from './SearchInput.js' /** * Options for writing documents to the index * @typedef { Object } Options * @memberof UI * @property { SearchIndex } [index] - the search index * @property { UI.CountOptions } [count] - initialise the <code>count</code> component * @property { Array.<UI.FacetOptions> } [facets] - initialise the <code>facet</code> components * @property { UI.HitsOptions } [hits] - initialise the <code>hits</code> component * @property { UI.PagingOptions } [paging] - initialise the paging component * @property { UI.SearchInputOptions } [searchInput] - initialise the search box component * @property { UI.SuggestionsOptions } [suggestions] - initialise the suggestions component */ /** * (Beta) Class that can be used to create a simple UI for a search index. See * the <a href="https://github.com/fergiemcdowall/search-index-demo">live * demo</a> * @param { UI.Options } [ops] - initialisation options * @example * import { SearchIndex, UI } from 'search-index' * * // ... * * new UI({ * index: si, * count: { * elementId: 'count' * }, * facets: [ * { * elementId: 'year-refiner', * titleTemplate: '<p class="h6">YEAR</p>', * field: 'year', * mode: 'OR' * }, * { * elementId: 'month-refiner', * titleTemplate: '<p class="h6">MONTH</p>', * field: 'month', * mode: 'OR', * sort: (a, b) => { * const monthNumber = month => * new Date(Date.parse(month + ' 1, 2012')).getMonth() + 1 * return monthNumber(a.VALUE) - monthNumber(b.VALUE) * } * } * ], * hits: { * elementId: 'hits', * template: doc => `<p>${JSON.stringify(doc)}</p>` * }, * paging: { elementId: 'paging', pageSize: 2 }, * searchInput: { * elementId: 'searchbox', * suggestions: { * elementId: 'suggestions', * limit: 10, * threshold: 1 * } * } * }) * */ export class UI { constructor ({ index = null, count = {}, hits = {}, facets = [], searchInput = {}, paging = {}, suggestions = {} }) { this.index = index this.count = new Count(count) this.paging = new Paging(paging, this.search) this.hits = new Hits(hits) this.searchInput = new SearchInput( { // autoCompleteFunction: this.index.DICTIONARY, ...searchInput, suggestions: { autoCompleteFunction: this.index.DICTIONARY, ...searchInput.suggestions } }, this.search, this.paging ) this.facets = facets.map(r => new Facet(r, this.search)) this.search() } queryOptions = () => ({ // TODO: this should be somehow controlled by the Facet class so that its turned off when ORing FACETS: this.facets.map(r => ({ FIELD: r.field })), DOCUMENTS: true, PAGE: this.paging.page }) // treat empty search as a special case: instead of showing nothing, // show everything. emptySearchQuery = () => [ { ALL_DOCUMENTS: true }, this.queryOptions() ] searchQuery = () => { return [ [ ...this.searchInput.el.value.split(/\s+/).filter(item => item), ...this.facets.map(r => r.getActiveFilters()).flat(Infinity) ], this.queryOptions() ] } search = source => { return ( this.searchInput.el.value.length + this.facets.map(r => r.getActiveFilters()).flat(Infinity).length ? this.index.SEARCH(...this.searchQuery()) : this.index.QUERY(...this.emptySearchQuery()) ).then(res => { this.hits.update(res.RESULT) this.count.update(res.RESULT_LENGTH) this.facets.forEach(r => r.update(res.FACETS, source)) this.paging.update(res.PAGING) }) } }