vuex-search
Vuex Search is a plugin for searching collections of objects. Search algorithms powered by js-worker-search.
Installation:
npm i vuex-search
Overview
vuex-search searches collections of documents and returns results as an Array
of document ids. It is important to note that the documents themselves aren't returned. This is because the actual search is performed in a web-worker thread for performance reasons. In order to avoid serializing the documents and passing them back and forth, vuex-search simply passes their ids.
Because of this, each document must contain an id
attribute.
Examples
// store/state.js
export default {
myResources: {
contacts: [
{
// id is required for each record
id: '1',
address: '06176 Georgiana Points',
name: 'Dr. Katrina Stehr',
},
{
id: '2',
address: '06176 Georgiana Points',
name: 'Edyth Grimes',
},
],
},
}
Vuex Search plugin
searchPlugin(options)
-
options: Object
: List of options for defining the plugin. Available options are:resources
: Dictionary ofresourceName
and their index options.searchApi
(optional): Customizing search index.
// store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
import searchPlugin from 'vuex-search';
import state from './state';
Vue.use(Vuex);
const store = new Vuex.Store({
state,
plugins: [
searchPlugin({
resources: {
contacts: {
// what fields to index
index: ['address', 'name'],
// access the state to be watched by Vuex Search
getter: state => state.myResources.contacts,
},
// otherResource: { index, getter },
},
}),
],
});
Where each resource has options:
index: string[]
: List of fields to be indexed.getter: (state) => resource
: Getter function to access the resource from root state and to watch.searchApi: SearchApi
(optional): Custom search index. If defined, it is used instead of the sharedsearchApi
instance.
Binding with Vue Component
import {
mapActions as mapSearchActions,
mapGetters as mapSearchGetters,
getterTypes,
actionTypes,
} from 'vuex-search';
// SomeComponent.vue
data() {
return { text: '' },
},
computed: {
...mapSearchGetters('contacts', {
resultIds: getterTypes.result,
isLoading: getterTypes.isSearching,
}),
},
methods: {
...mapSearchActions('contacts', {
searchContacts: actionTypes.search,
}),
doSearch() {
this.searchContacts(this.text);
},
},
mapGetters(resourceName, getterMap): mappedGetters
Similar to Vuex helper for mapping attributes, getterMap
can be either an object or an array.
mapActions(resourceName, actionMap): mappedActions
Similar to Vuex helper for mapping attributes, actionMap
can be either an object or an array.
getterTypes
result
isSearching
resourceIndex
: full state of resource index (includingresult
,isSearching
, and currentsearch
)
actionTypes
search
: mapped action has its first argument the text to search.
Customizing Search Index
By default, vuex-search builds an index to match all substrings.
You can override this behavior by providing your own, pre-configured searchApi
param to the plugin like so:
import searchPlugin, { SearchApi, INDEX_MODES } from 'vuex-search';
// all-substrings match by default; same as current
// eg "c", "ca", "a", "at", "cat" match "cat"
const allSubstringsSearchApi = new SearchApi();
// prefix matching (eg "c", "ca", "cat" match "cat")
const prefixSearchApi = new SearchApi({
indexMode: INDEX_MODES.PREFIXES,
});
// exact words matching (eg only "cat" matches "cat")
const exactWordsSearchApi = new SearchApi({
indexMode: INDEX_MODES.EXACT_WORDS,
});
const store = new Vuex.Store({
state,
plugins: [
searchPlugin({
resources: {
contacts: {
index: ['address', 'name'],
getter: state => state.myResources.contacts,
},
},
searchApi: exactWordsSearchApi, // or allSubstringSearchApi; or prefixSearchApi
}),
],
});
Custom word boundaries (tokenization) and case-sensitivity
You can also pass parameters to the SearchApi constructor that customize the way the
search splits up the text into words (tokenizes) and change the search from the default
case-insensitive to case-sensitive:
import searchPlugin, { SearchApi } from 'vuex-search';
const store = new Vuex.Store({
state,
plugins: [
searchPlugin({
resources: {
contacts: {
index: ['address', 'name'],
getter: state => state.myResources.contacts,
},
},
searchApi: new SearchApi({
// split on all non-alphanumeric characters,
// so this/that gets split to ['this','that'], for example
tokenizePattern: /[^a-z0-9]+/,
// make the search case-sensitive
caseSensitive: true,
}),
}),
],
});
Dynamic Index Registration
When a module needs to be loaded or registered dynamically, statically defined plugin can be a problem. The solution is to use vuex-search dynamic index registration.
Vuex Search can be accessed through store.search
or this.$store.search
in a Vue instance and available methods are:
registerResource(resourceName, config)
config: Object
: A list of options for indexing resource. Currently available options are:index: string[]
: List of fields to be indexed.getter: (state) => resource
: Getter function to access the resource from root state and to watch.searchApi: SearchApi
(optional): Custom search index. If defined, it is used instead of the sharedsearchApi
instance.
unregisterResource(resourceName)
Remove outdated resource indexes, and unwatch/unsubscribe any watchers/subscriptions related to resourceName
.
Changing Base
By default, vuex-search will register its module in vuexSearch
from root state. To avoid possible clash naming, you can change its base name before defining the plugin in the store through
import { VuexSearch } from 'vuex-search';
VuexSearch.base = 'vuexSearchNew';
const store = new Vuex.Store({
// ... store options
});