-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
87 lines (76 loc) · 2.3 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
var async = require('async'),
_ = require('underscore');
/**
* A Redis based minimal search indexer.
*/
var RedisIndex = module.exports = function(options) {
this.redisClient = options.redisClient;
this.keyPrefix = options.keyPrefix || 'ri:';
this.buildKeywordKey = function(keyword) {
return this.keyPrefix + 'kw:' + keyword;
};
this.buildDocKey = function(docId) {
return this.keyPrefix + 'doc:' + docId;
};
};
/**
* Indexes a document. If the document exists, remove it before adding the new content.
*/
RedisIndex.prototype.index = function(docId, docContent, callback) {
var indexer = this;
async.series([
function(cb) {
indexer.remove(docId, cb);
},
function(cb) {
indexer.add(docId, docContent, cb);
}
], callback);
};
/**
* Adds/appends a document to the index. If the document exists, new content will be appended.
*/
RedisIndex.prototype.add = function(docId, docContent, callback) {
var indexer = this;
var keywords = indexer.tokenize(docContent);
var multi = indexer.redisClient.multi();
keywords.forEach(function(kw) {
multi.sadd(indexer.buildKeywordKey(kw), docId)
.sadd(indexer.buildDocKey(docId), kw);
});
multi.exec(callback);
};
/**
* Removes a document from index.
*/
RedisIndex.prototype.remove = function(docId, callback) {
var indexer = this;
indexer.redisClient.smembers(indexer.buildDocKey(docId), function(err, keywords) {
if (err) return callback(err);
if (!keywords) return callback();
var multi = indexer.redisClient.multi();
keywords.forEach(function(kw) {
multi.srem(indexer.buildKeywordKey(kw), docId);
});
multi.exec(callback);
});
};
/**
* Returns an array of all document IDs matching the given query.
*/
RedisIndex.prototype.search = function(query, callback) {
var indexer = this;
var keywords = this.tokenize(query);
if (keywords.length === 0) {
return callback(null, []);
}
this.redisClient.sinter(
_.map(keywords, function(kw) {
return indexer.buildKeywordKey(kw);
}),
callback);
};
RedisIndex.prototype.tokenize = function(str) {
return _.compact(str.toLowerCase()
.split(/[\\'!"#$%&()*+,\-.\/:;<=>?@\[\]^_`{|}~ ~·!@#¥%…&×-—=+、,。|?《》;:{}()“”‘’【】]/));
};