pi-redredisearch v0.0.1
RedRediSearch
RedRediSearch is a Node.js wrapper library for the RediSearch Redis module. It is more-or-less syntactically compatible with Reds, another Node.js search library. RedRediSearch and RediSearch can provide full-text searching that is much faster than the original Reds library (see Benchmarks).
Upgrading
If you are upgrading from Reds, you'll need to make your createSearch
asynchronous and re-index your data. Otherwise, your app-level logic and code should be compatible.
Installation
$ npm install redredisearch
Example
The first thing you'll want to do is create a Search
instance, which allows you to pass a key
, used for namespacing within RediSearch so that you may have several searches in the same Redis database. You may specify your own node_redis instance with the redredisearch.setClient
function.
redredisearch.createSearch('pets',{}, function(err, search) {
/* ... */
});
You can then add items to the index with the Search#index
function.
var strs = [];
strs.push('Tobi wants four dollars');
strs.push('Tobi only wants $4');
strs.push('Loki is really fat');
strs.push('Loki, Jane, and Tobi are ferrets');
strs.push('Manny is a cat');
strs.push('Luna is a cat');
strs.push('Mustachio is a cat');
redredisearch.createSearch('pets',{}, function(err,search) {
strs.forEach(function(str, i){ search.index(str, i); });
});
To perform a query against the index simply invoke Search#query()
with a string, and pass a callback, which receives an array of ids when present, or an empty array otherwise.
search
.query('Tobi dollars')
.end(function(err, ids){
if (err) throw err;
console.log('Search results for "%s":', query);
ids.forEach(function(id){
console.log(' - %s', strs[id]);
});
});
By default, queries are an intersection of the search words. The previous example would yield the following output since only one string contains both "Tobi" and "dollars":
Search results for "Tobi dollars":
- Tobi wants four dollars
We can tweak the query to perform a union by passing either "union" or "or" to Search#type()
in redredisearch.search()
between Search#query()
and Search#end()
, indicating that any of the constants computed may be present for the id
to match.
search
.query('tobi dollars')
.type('or')
.end(function(err, ids){
if (err) throw err;
console.log('Search results for "%s":', query);
ids.forEach(function(id){
console.log(' - %s', strs[id]);
});
});
The union search would yield the following since three strings contain either "Tobi" or "dollars":
Search results for "tobi dollars":
- Tobi wants four dollars
- Tobi only wants $4
- Loki, Jane, and Tobi are ferrets
RediSearch has an advanced query syntax that can be used by using the 'direct' search type. See the RediSearch documentation for this syntax.
search
.query('(hello|hella) (world|werld)')
.type('direct')
.end(function(err, ids){
/* ... */
});
Also included in the package is the RediSearch Suggestion API. This has no corollary in the Reds module. The Suggestion API is ideal for auto-complete type situations and is entirely separate from the Search API.
var suggestions = redredisearch.suggestion('my-suggestion-list');
suggestions.add(
'redis', // add 'redis'
2, // with a 'score' of 2, this affects the position in the results, higher = higher up in results
function(err,sizeOfSuggestionList) { /* ... */ } // callback
);
suggestions.add(
'redisearch',
5,
function(err,sizeOfSuggestionList) { /* ... */ }
);
suggestions.add(
'reds',
1,
function(err,sizeOfSuggestionList) { /* ... */ }
);
/* ... */
sugggestions.get(
're', // prefix - will find anything starting with "re"
function(err, returnedSuggestions) {
/* returnedSuggestions is set to [ "redisearch", "redis", "reds" ] */
}
);
sugggestions.get(
'redis', // prefix - will find anything starting with "redis", so not "reds"
function(err, returnedSuggestions) {
/* returnedSuggestions is set to [ "redisearch", "redis" ] */
}
)
There is also a fuzzy
opt and maxResults
that can either be set by chaining or by passing an object in the second argument in the constructor.
API
redredisearch.createSearch(key, options, fn) : Search
redredisearch.setClient(inClient)
redredisearch.createClient()
redredisearch.confirmModule(cb)
redredisearch.words(str) : Array
redredisearch.suggestionList(key,opts) : Suggestion
Search#index(text, id[, fn])
Search#remove(id[, fn]);
Search#query(text, fn[, type]) : Query
Query#type(type)
Query#between(str)
Query#end(fn)
Suggestion#fuzzy(isFuzzy)
Suggestion#maxResults(maxResults)
Suggestion#add(str,score,fn)
Suggestion#get(prefix,fn)
Suggestion#del(str,fn)
Examples:
var search = redredisearch.createSearch('misc');
search.index('Foo bar baz', 'abc');
search.index('Foo bar', 'bcd');
search.remove('bcd');
search.query('foo bar').end(function(err, ids){});
Benchmarks
When compared to Reds, RedRediSearch is much faster at indexing and somewhat faster at query:
Indexing - documents / second
Module | Tiny | Small | Medium | Large |
---|---|---|---|---|
Reds | 122 | 75 | 10 | 0 |
RediRediSearch | 1,256 | 501 | 132 | 5 |
Query - queries / second
Module | 1 term | 2 terms / AND | 2 terms / OR | 3 terms / AND | 3 terms / OR | Long* / AND | Long* / OR |
---|---|---|---|---|---|---|---|
Reds | 8,754 | 8,765 | 8,389 | 7,622 | 7,193 | 1,649 | 1,647 |
RedRediSearch | 10,955 | 12,945 | 10,054 | 12,769 | 8,389 | 6,456 | 12,311 |
The "Long" query string is taken from the Canadian Charter of Rights and Freedoms: "Everyone has the following fundamental freedoms: (a) freedom of conscience and religion; (b) freedom of thought, belief, opinion and expression, including freedom of the press and other media of communication; (c) freedom of peaceful assembly; and (d) freedom of association." (Used because I just had it open in another tab...)
Next steps
- More coverage of RediSearch features
- Tests
- Better examples
License
(The MIT License)
5 years ago