Skip to content

Commit d71c48e

Browse files
authored
Merge pull request #50 from stockholmux/master
version bump / 1.0
2 parents 366f30a + cf1aaff commit d71c48e

File tree

7 files changed

+533
-229
lines changed

7 files changed

+533
-229
lines changed

.gitignore

+59-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,62 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
8+
# Runtime data
9+
pids
10+
*.pid
11+
*.seed
12+
*.pid.lock
13+
14+
# Directory for instrumented libs generated by jscoverage/JSCover
15+
lib-cov
16+
17+
# Coverage directory used by tools like istanbul
18+
coverage
19+
20+
# nyc test coverage
21+
.nyc_output
22+
23+
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24+
.grunt
25+
26+
# Bower dependency directory (https://bower.io/)
27+
bower_components
28+
29+
# node-waf configuration
30+
.lock-wscript
31+
32+
# Compiled binary addons (http://nodejs.org/api/addons.html)
33+
build/Release
34+
35+
# Dependency directories
36+
node_modules/
37+
jspm_packages/
38+
39+
# Typescript v1 declaration files
40+
typings/
41+
42+
# Optional npm cache directory
43+
.npm
44+
45+
# Optional eslint cache
46+
.eslintcache
47+
48+
# Optional REPL history
49+
.node_repl_history
50+
51+
# Output of 'npm pack'
52+
*.tgz
53+
54+
# Yarn Integrity file
55+
.yarn-integrity
56+
57+
# dotenv environment variables file
58+
.env
59+
160
.DS_Store
2-
node_modules
361
*.sock
462
testing.js

History.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
1.0.0 /
2+
==================
3+
4+
* Updated tests and benchmarks to take a connection object from an command line argument
5+
* Updated creaky dependencies
6+
* Added the ability to plug in new natural language processor and options
17

28
0.2.4 / 2013-08-10
39
==================

Readme.md

+23-1
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@
22

33
reds is a light-weight Redis search for node.js. This module was originally developed to provide search capabilities for [Kue](http://automattic.github.io/kue/) a priority job queue, however it is very much a light general purpose search library that could be integrated into a blog, a documentation server, etc.
44

5+
## Upgrading
6+
7+
Version 1.0.0 is syntactically compatible with previous versions of reds (0.2.5). However, [natural](https://github.com/NaturalNode/natural) has been updated. Documents indexed with older installs of reds (using natural v0.2.0) may need to be re-indexed to avoid some edge cases.
8+
59
## Installation
610

711
$ npm install reds
812

913
## Example
1014

11-
The first thing you'll want to do is create a `Search` instance, which allows you to pass a `key`, used for namespacing within Redis so that you may have several searches in the same db.
15+
The first thing you'll want to do is create a `Search` instance, which allows you to pass a `key`, used for namespacing within Redis so that you may have several searches in the same db. You may specify your own [node_redis](https://github.com/NodeRedis/node_redis) instance with the `reds.setClient` function.
1216

1317
var search = reds.createSearch('pets');
1418

@@ -93,6 +97,24 @@ search.remove('bcd');
9397
search.query('foo bar').end(function(err, ids){});
9498
```
9599

100+
## Extending reds
101+
102+
Starting in 1.0.0, you can easily extend and expand how reds functions. When creating a new search, supply an object as the second argument. There are currently three properties that can be configured:
103+
104+
- `nlpProcess` the natural language processing function. You can alter how the words are processed (split, stemmed, and converted to metaphones) using this function.
105+
- `writeIndex` how the items are written to the index.
106+
- `removeIndex` how the items are removed from the index.
107+
108+
See the `lib/reds.js` file for the implementation of each. Please keep in mind that changing these functions may invalidate your previously stored index.
109+
110+
```js
111+
reds.createSearch('pets', {
112+
nlpProcess : yourNlpProcessingFunction,
113+
writeIndex : yourWriteIndexFunction,
114+
removeIndex : yourRemoveIndexFunction
115+
});
116+
```
117+
96118
## About
97119

98120
Currently reds strips stop words and applies the metaphone and porter stemmer algorithms to the remaining words before mapping the constants in Redis sets. For example the following text:

benchmarks/index.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,20 @@
22
/**
33
* Module dependencies.
44
*/
5-
6-
var reds = require('../').createSearch('reds');
5+
var argv = require('yargs')
6+
.demand('connection')
7+
.argv;
8+
var redis = require('redis');
9+
var connectionObj = require(argv.connection);
10+
var reds;
711
var fs = require('fs');
812

13+
14+
reds = require('../');
15+
16+
reds.setClient(redis.createClient(connectionObj));
17+
18+
reds = reds.createSearch('reds');
919
// test data
1020

1121
var tiny = fs.readFileSync('package.json', 'utf8');

lib/reds.js

+99-26
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
/*!
32
* reds
43
* Copyright(c) 2011 TJ Holowaychuk <[email protected]>
@@ -20,7 +19,7 @@ function noop(){};
2019
* Library version.
2120
*/
2221

23-
exports.version = '0.1.4';
22+
exports.version = '1.0.0';
2423

2524
/**
2625
* Expose `Search`.
@@ -45,6 +44,19 @@ var types = {
4544
or: 'zunionstore'
4645
};
4746

47+
/**
48+
* Alternate way to set client
49+
* provide your own behaviour.
50+
*
51+
* @param {RedisClient} inClient
52+
* @return {RedisClient}
53+
* @api public
54+
*/
55+
56+
exports.setClient = function(inClient) {
57+
return exports.client = inClient;
58+
}
59+
4860
/**
4961
* Create a redis client, override to
5062
* provide your own behaviour.
@@ -58,17 +70,89 @@ exports.createClient = function(){
5870
|| (exports.client = redis.createClient());
5971
};
6072

73+
/**
74+
* Process indexing string into NLP components (words, counts, map, keys)
75+
*
76+
* @param {String} str
77+
* @param {String} [key]
78+
*/
79+
exports.nlpProcess = function(str, key) {
80+
var words = exports.stem(exports.stripStopWords(exports.words(str)));
81+
var counts = exports.countWords(words);
82+
var map = exports.metaphoneMap(words);
83+
var keys = Object.keys(map);
84+
//the key argument is only needed for removing items from the index
85+
//by not executing the `metaphoneKeys` function, it speeds up indexing a bit
86+
var metaphoneKeys = !key ? null : exports.metaphoneKeys(key, words)
87+
88+
89+
return {
90+
words : words,
91+
counts : counts,
92+
map : map,
93+
keys : keys,
94+
metaphoneKeys
95+
: metaphoneKeys
96+
};
97+
}
98+
99+
/**
100+
* Writes index to Redis
101+
*
102+
* @param {Object} db Redis Client
103+
* @param {Number|String} id
104+
* @param {String} key
105+
* @param {Object} nlpObj Object with word map, counts, and keys
106+
* @param {Function} fn
107+
*
108+
*/
109+
exports.writeIndex = function(db, id, key, nlpObj, fn) {
110+
var cmds = [];
111+
nlpObj.keys.forEach(function(word, i){
112+
cmds.push(['zadd', key + ':word:' + nlpObj.map[word], nlpObj.counts[word], id]);
113+
cmds.push(['zadd', key + ':object:' + id, nlpObj.counts[word], nlpObj.map[word]]);
114+
});
115+
db.multi(cmds).exec(fn || noop);
116+
}
117+
118+
/**
119+
* Removes index from Redis
120+
*
121+
* @param {Object} db Redis Client
122+
* @param {Number|String} id
123+
* @param {String} key
124+
* @param {Function} fn
125+
*
126+
*/
127+
exports.removeIndex = function(db, id, key, fn) {
128+
db.zrevrangebyscore(key + ':object:' + id, '+inf', 0, function(err, constants){
129+
if (err) return fn(err);
130+
var multi = db.multi().del(key + ':object:' + id);
131+
constants.forEach(function(c){
132+
multi.zrem(key + ':word:' + c, id);
133+
});
134+
multi.exec(fn);
135+
});
136+
}
137+
138+
61139
/**
62140
* Return a new reds `Search` with the given `key`.
63-
*
141+
* @param {Object} opts
64142
* @param {String} key
65143
* @return {Search}
66144
* @api public
67145
*/
68146

69-
exports.createSearch = function(key){
147+
exports.createSearch = function(key,opts){
70148
if (!key) throw new Error('createSearch() requires a redis key for namespacing');
71-
return new Search(key);
149+
150+
opts = !opts ? {} : opts;
151+
opts.nlpProcess = !opts.nlpProcess ? exports.nlpProcess : opts.nlpProcess;
152+
opts.writeIndex = !opts.writeIndex ? exports.writeIndex : opts.writeIndex;
153+
opts.removeIndex = !opts.removeIndex ? exports.removeIndex : opts.removeIndex;
154+
155+
return new Search(key, opts.nlpProcess, opts.writeIndex, opts.removeIndex);
72156
};
73157

74158
/**
@@ -256,8 +340,8 @@ Query.prototype.end = function(fn){
256340
var key = this.search.key;
257341
var db = this.search.client;
258342
var query = this.str;
259-
var words = exports.stem(exports.stripStopWords(exports.words(query)));
260-
var keys = exports.metaphoneKeys(key, words);
343+
var nlpObj = this.search.nlpProcess(query, key);
344+
var keys = nlpObj.metaphoneKeys;
261345
var type = this._type;
262346
var start = this._start || 0;
263347
var stop = this._stop || -1;
@@ -287,9 +371,13 @@ Query.prototype.end = function(fn){
287371
* @api public
288372
*/
289373

290-
function Search(key) {
374+
function Search(key, nlpProcess, writeIndex, removeIndex) {
291375
this.key = key;
292376
this.client = exports.createClient();
377+
378+
this.nlpProcess = nlpProcess;
379+
this.writeIndex = writeIndex;
380+
this.removeIndex = removeIndex;
293381
}
294382

295383
/**
@@ -304,17 +392,9 @@ function Search(key) {
304392
Search.prototype.index = function(str, id, fn){
305393
var key = this.key;
306394
var db = this.client;
307-
var words = exports.stem(exports.stripStopWords(exports.words(str)));
308-
var counts = exports.countWords(words);
309-
var map = exports.metaphoneMap(words);
310-
var keys = Object.keys(map);
395+
var nlpObj = this.nlpProcess(str);
311396

312-
var cmds = [];
313-
keys.forEach(function(word, i){
314-
cmds.push(['zadd', key + ':word:' + map[word], counts[word], id]);
315-
cmds.push(['zadd', key + ':object:' + id, counts[word], map[word]]);
316-
});
317-
db.multi(cmds).exec(fn || noop);
397+
this.writeIndex(db, id, key, nlpObj, fn);
318398

319399
return this;
320400
};
@@ -331,14 +411,7 @@ Search.prototype.remove = function(id, fn){
331411
var key = this.key;
332412
var db = this.client;
333413

334-
db.zrevrangebyscore(key + ':object:' + id, '+inf', 0, function(err, constants){
335-
if (err) return fn(err);
336-
var multi = db.multi().del(key + ':object:' + id);
337-
constants.forEach(function(c){
338-
multi.zrem(key + ':word:' + c, id);
339-
});
340-
multi.exec(fn);
341-
});
414+
this.removeIndex(db, id, key, fn);
342415

343416
return this;
344417
};

package.json

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "reds",
3-
"version": "0.2.5",
3+
"version": "1.0.0",
44
"description": "Redis search for node.js",
55
"keywords": [
66
"redis",
@@ -11,10 +11,12 @@
1111
],
1212
"author": "TJ Holowaychuk <[email protected]>",
1313
"dependencies": {
14-
"natural": "^0.5.0",
15-
"redis": "^0.12.1"
14+
"natural": "0.5.0",
15+
"redis": "^2.7.1"
1616
},
1717
"devDependencies": {
18+
"yargs": "^7.0.2",
19+
"async": "^2.3.0",
1820
"matcha": "^0.6.0",
1921
"should": "^3.3.2",
2022
"superagent": "^0.21.0"

0 commit comments

Comments
 (0)