Code Monkey home page Code Monkey logo

vuex-search's Introduction

Vuex Search

Coverage Status Build Status Downloads Downloads Version License

Vuex Search is a plugin for searching collections of objects. Search algorithms powered by js-worker-search.

Vuex Search

See working example here.

Installation:

npm install --save vuex-search
# or
yarn add 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.

Please note that vuex-search depends on regenerator runtime, you need to either include transform-runtime plugin in your babel config,

{
  "plugins": [
    "transform-runtime"
  ]
}

or add babel-polyfill in your entries (assuming you are using webpack). For example

module.export = {
  entries: ['babel-polyfill', './src']
}

Examples

// store/state.js

export default {
  myResources: {
    contacts: [
      {
        // id is required for each record
        id: '1',
        address: '1 Hacker Way, Menlo Park',
        name: 'Dr. Katrina Stehr',
      },
      {
        id: '2',
        address: '06176 Georgiana Points',
        name: 'Edyth Grimes',
      },
    ],
  },
}

Vuex Search plugin

searchPlugin(options)

  • options: List of options for defining the plugin. Available options are:

    • resources: { [resourceName]: IndexOptions }

      Dictionary of resourceName and their index options. See IndexOptions.

    • [searchApi]: SearchApi

      If provided, it will be used as default searchApi across resources. See customizing search index. Default: new SearchApi()

// 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,
          // how resource should be watched
          watch: { delay: 500 },
        },
        // otherResource: { index, getter, watch, searchApi },
      },
    }),
  ],
});

IndexOptions

  • index: Array<string>

    List of fields to be indexed.

  • getter: (state) => Array<object>|object

    Getter function to access the resource from root state and to watch.

  • [watch]: boolean|WatchOptions

    Whether needs to or delay reindex if resource changes. This option is useful to avoid reindex overhead when the resource frequently changes. Reindexing can be done by mapping action reindex.

    WatchOptions

    • [delay]: number

      If provided, reindex will be debounced with specified delay.

    Default: true

  • [searchApi]: SearchApi

    Custom search index. If defined, it is used instead of the shared searchApi 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)

Similar to Vuex helper for mapping attributes, getterMap can be either an object or an array.

mapActions(resourceName, actionMap)

Similar to Vuex helper for mapping attributes, actionMap can be either an object or an array.

getterTypes

  • result

    Mapped state is an array of ids.

  • isSearching

    Mapped state indicates whether searchApi has resolved its promise of search result.

  • resourceIndex

    Full state of resource index: result, isSearching, and current search text.

actionTypes

  • search

    Mapped action's function signature: (query: string) => void.

  • reindex

    Mapped action's function signature: () => void. To be used when option watch is false. This action will reindex the resource and automatically re-search current text.

  • registerResource

    Mapped action's function signature: (options: IndexOptions) => void. This action will dynamically add resourceName with options provided. See IndexOptions.

    More about Dynamic Index Registration.

  • unregisterResource

    Mapped action's function signature: () => void. This action will unwatch and remove resourceName index.

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.

VuexSearch instance can be accessed through search attribute of store. Thus, in a Vue instance it is accessed through this.$store.search. Available methods are:

registerResource(resourceName, options: IndexOptions)

Note that this method is slightly different from registerResource from mapActions. Calling this method needs to provide resourceName. Whereas, method from mapActions has already injected resourceName as its first argument.

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
});

Changelog

Changes are tracked in the changelog.

License

vuex-search is available under the MIT License.

vuex-search's People

Contributors

albertlucianto avatar dependabot[bot] avatar timothygk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

vuex-search's Issues

[FEATURE REQUEST] Index fields weight/priority

Hi, the library does look and work very nice on the demo page, but one thing that prevents us from implementing it is that we need different weights for different fields.

In the demo page, sometimes results that match in the address appear on top of the results that match on the name. It would be good to be able to set the importance of each field so that results that match a field with more weight appear first.

image

I would like to propose an API similar to the one used by Fuse.js:

searchPlugin({
    resources: {
        contacts: {
            index: [
				{
                    name: 'name',
                    weight: 1,
                },
                {
                    name: 'address',
                    weight: 2 / 3,
                },
                {
                    name: 'description',
                    weight: 1 / 3,
                },
    		],
            // ...
        },
    },
})

Besides this specific feature, greater control over the search parameters would be appreciated.

TypeScript issues

I'm having trouble getting mapGetters and mapActions to work - with the following error:
'mapSearchGetters' only refers to a type, but is being used as a value here.

I am importing and using as per the documentation:

import {
	mapActions as mapSearchActions,
	mapGetters as mapSearchGetters,
	getterTypes,
	actionTypes
} from 'vuex-search';
...mapSearchGetters('games', {
	resultIds: getterTypes.result,
	isLoading: getterTypes.isSearching,
}),

Potential Nuxt.js Error: cannot enable cancellation after promises are in use

Hey @AlbertLucianto I'm having a difficult time getting this plugin to work after installing and setting up a basic searchPlugin(). I'm using vuex-search in a Nuxt.js environment and have done the following:

  • yarn add vuex-search which installed v2.2.2
  • In my store/index.js:
export const state = () => ({
  'pages': []
});

export const plugins = [
  searchPlugin({
    'resources': {
      'pages': {
        'index': ['title'],
        'getter': state => state.pages,
        'watch': { delay: 500 },
      }
    }
  })
];
  • But when I run yarn start to start the application, I get the error with the following stack trace:
 Error: cannot enable cancellation after promises are in use
 
 - debuggability.js:282 Function.Promise.config
   [project-name]/[bluebird]/js/release/debuggability.js:282:19
 
 - utils.js:85 cancellablePromiseWrapper
   [project-name]/[vuex-search]/dist/commonjs/utils.js:85:22
 
 - SearchApi.js:173 SubscribableSearchApi._callee$
   [project-name]/[vuex-search]/dist/commonjs/SearchApi.js:173:70
 
 - runtime.js:62 tryCatch
   [project-name]/[regenerator-runtime]/runtime.js:62:40
 
 - runtime.js:296 Generator.invoke [as _invoke]
   [project-name]/[regenerator-runtime]/runtime.js:296:22
 
 - runtime.js:114 Generator.prototype.(anonymous function) [as next]
   [project-name]/[regenerator-runtime]/runtime.js:114:21
 
 - asyncToGenerator.js:17 step
   [project-name]/[babel-runtime]/helpers/asyncToGenerator.js:17:30
 
 - asyncToGenerator.js:35 
   [project-name]/[babel-runtime]/helpers/asyncToGenerator.js:35:14
 
 - new Promise
 
 - _export.js:36 new F
   [project-name]/[core-js]/library/modules/_export.js:36:28
 
 - asyncToGenerator.js:14 SubscribableSearchApi.<anonymous>
   [project-name]/[babel-runtime]/helpers/asyncToGenerator.js:14:12
 
 - SearchApi.js:218 SubscribableSearchApi.performSearch
   [project-name]/[vuex-search]/dist/commonjs/SearchApi.js:218:22
 
 - actions.js:53 Store.<anonymous>
   [project-name]/[vuex-search]/dist/commonjs/actions.js:53:29
 
 - vuex.common.js:706 Array.wrappedActionHandler
   [project-name]/[vuex]/dist/vuex.common.js:706:23
 
 - vuex.common.js:428 Store.dispatch
   [project-name]/[vuex]/dist/vuex.common.js:428:15
 
 - vuex.common.js:334 Store.boundDispatch [as dispatch]
   [project-name]/[vuex]/dist/vuex.common.js:334:21

I don't understand why the error is coming from the plugin. This makes me feel like I've missed some very obvious setup step, any ideas?

Memory leak - server side rendering

I've implemented a Nuxt app utilizing vuex-search. The app is server-side rendered, and is experiencing a memory leak on the server. Loading any page (including pages that don't use the search function) increases the memory usage of the server until it eventually crashes.

By removing vuex-search searchPlugin from the 'plugins' section of of the store, the memory leak goes away. The searchPlugin settings are implemented just as in the example in the README (though of course for different state attributes).

Since this is occuring on loading of pages that don't have any custom implementation of vuex-search, and the only custom code that's written is the config that's passed to the searchPlugin method, it seems like the memory leak is probably something internal to vuex-search. I'll take a pass at the source code of this method and see if I can identify anything, but do you know of any implementations that use server side rendering with vuex-search that have successfully avoided this issue?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.