noderaven
Version:
* store.<b>indexQuery</b>(db, index, whereClause, start, limit, sortBys, fetchings, cb) * store.<b>suggest</b>(db, index, term, field, cb) * store.<b>facets</b>(db, indexName, facetDoc, query, cb) * store.<b>load</b>(db, id, cb) * store.<b>update</b>(db,
414 lines (412 loc) • 12.5 kB
JavaScript
var request = require('request'),
_ = require('underscore');
module.exports = function (host) {
if (host.indexOf('/', host.length - 1) == -1)
host += '/';
return {
/** Queries index and returns results in callback(err,result : object{docs,stats}).
* @param {string} db
* @param {string} index
* @param {string} whereClause
* @param {int} start
* @param {int} limit
* @param {Array<string>} sortBys
* @param {Array<string>} fetchings
* @param {Function} cb
*/
indexQuery: function (db, index, whereClause, start, limit, sortBys, fetchings, cb) {
if (typeof whereClause === 'function') {
cb = whereClause;
whereClause = '';
start = 0;
limit = 12;
sortBys = [];
fetchings = [];
}
if (typeof start === 'function') {
cb = start;
start = 0;
limit = 12;
sortBys = [];
fetchings = [];
}
if (typeof sortBys === 'function') {
cb = sortBys;
sortBys = [];
fetchings = [];
}
if (typeof fetchings === 'function') {
cb = fetchings;
fetchings = [];
}
var url = host +
'databases/' + db +
'/indexes/' + index +
'?query=' + encodeURIComponent(whereClause) +
'&operator=AND' +
'&pageSize=' + limit +
'&start=' + start;
var sorts = sortBys.map(function (prop) {
return '&sort=' + prop;
}).join('');
var fetchs = fetchings.map(function (prop) {
return '&fetch=' + prop;
}).join('');
url = url + sorts + fetchs;
var me = this;
request(url, function (error, response, body) {
if (!error && response.statusCode === 200) {
var result = JSON.parse(body);
var stats = _.reduce(result, function (memo, value, key) {
if (key !== "Results") {
memo[key] = value;
}
return memo;
}, {});
if (stats.IsStale)
setTimeout(me.indexQuery(db, index, whereClause, start, limit, sortBys, fetchings, cb), 100);
else
cb(null, {docs: result.Results, stats: stats});
}
else {
cb(error || new Error(response.statusCode), null);
}
});
},
/** Queries index and returns all indexResults in callback(err,result : object{docs,stats}).
* @param {string} db
* @param {string} index
* @param {string} whereClause
* @param {Array<string>} sortBys
* @param {Function} cb
*/
indexQueryAll: function (db, index, whereClause, sortBys, cb) {
var query = encodeURIComponent(whereClause);
var url = host +
'databases/' + db +
'/indexes/' + index +
'?query=' + query +
'&operator=AND&pageSize=0&start=0';
var urlPartSort = sortBys.map(function (prop) {
return '&sort=' + prop;
}).join('');
request(url, function (error, response, body) {
if (!error && response.statusCode === 200) {
var result = JSON.parse(body);
var pageSize = 1024;
var stats = _.reduce(result, function (memo, value, key) {
if (key !== "Results") {
memo[key] = value;
}
return memo;
}, {});
var totalResults = stats.TotalResults;
var pageCount = parseInt(totalResults / pageSize);
var delta = totalResults - pageCount * pageSize;
if (delta > 0)
pageCount++;
var requests = [];
for (var i = 0; i < pageCount; i++) {
requests.push({
Url: '/indexes/' + index,
Query: 'start=' + i * pageSize + '&pageSize=' + pageSize + '&query=' + query + urlPartSort
});
}
var multiGetBody = JSON.stringify(requests);
var multiGetUrl = host + 'databases/' + db + '/multi_get';
request.post({
url: multiGetUrl,
body: multiGetBody
},
function (err, resp, multiGetResponseBody) {
if (!err && resp.statusCode === 200) {
var multiGetResponse = JSON.parse(multiGetResponseBody);
var results =
_.flatten(_.reduce(multiGetResponse
, function (memo, val) {
if (val.Result && val.Result.Results)
memo.push(val.Result.Results);
return memo;
}
, []));
cb(null, results);
} else {
cb(err || new Error(resp.statusCode), null);
}
});
}
else {
cb(error || new Error(response.statusCode), null);
}
});
},
/** Returns Suggestions for given query and fieldName.
* @param {string} db
* @param {string} index
* @param {string} term
* @param {string} field
* @param {Function} cb
*/
suggest: function (db, index, term, field, cb) {
var url = host +
'databases/' + db +
'/suggest/' + index +
'?term=' + encodeURIComponent(term) +
'&field=' + field +
'&max=10&distance=Default&accuracy=0.5';
request(url, function (error, response, body) {
if (!error && response.statusCode === 200) {
var result = JSON.parse(body);
cb(null, result);
}
else {
cb(error || new Error(response.statusCode), null);
}
});
},
/** Returns Facet Results.
* @param {string} db
* @param {string} indexName
* @param {string} facetDoc
* @param {string} query
* @param {Function} cb
*/
facets: function (db, indexName, facetDoc, query, cb) {
var url = host + "databases/" + db + "/facets/" + indexName + "?facetDoc=" + facetDoc + "&query=" + encodeURIComponent(query);
request(url, function (error, response, body) {
if (!error && response.statusCode === 200) {
var result = JSON.parse(body);
_.each(result.Results, function (v, k) {
if (_.filter(v.Values, function (x) {
return x.Hits > 0;
}).length < 2) {
delete result.Results[k];
return;
}
v.Values = _.chain(v.Values)
.map(function (x) {
var val = JSON.stringify(x.Range)
.replace(/^\"|\"$/gi, "")
.replace(/\:/gi, "\\:")
.replace(/\(/gi, "\\(")
.replace(/\)/gi, "\\)");
if (x.Range.indexOf(" TO ") <= 0 || x.Range.indexOf("[") !== 0) {
val = val.replace(/\ /gi, "\\ ");
}
val = encodeURIComponent(val);
x.q = k + ":" + val;
x.Range = x.Range
.replace(/^\[Dx/, "")
.replace(/ Dx/, " ")
.replace(/\]$/, "")
.replace(/ TO /, "-");
return x;
}).filter(function (x) {
return x.Hits > 0;
}).sortBy(function (x) {
return x.Range;
}).value();
});
cb(null, result.Results);
} else
cb(error || new Error(response.statusCode), null);
});
},
/** Generates and returns Dynamic Report.
* @param {string} db
* @param {string} indexName
* @param {string} whereClause
* @param {string} groupBy
* @param {Array<string>} fieldsToSum
* @param {Function} cb
*/
dynamicAggregation: function (db, indexName, whereClause, groupBy, fieldsToSum, cb) {
var url = host + 'databases/' + db + '/facets/' + indexName + '?';
url += whereClause ? '&query=' + encodeURIComponent(whereClause) : '';
url += '&facetStart=0&facetPageSize=1024';
var facets = fieldsToSum
.map(function (field) {
return {
"Mode": 0,
"Aggregation": 16,
"AggregationField": field,
"Name": groupBy,
"DisplayName": field,
"Ranges": [],
"MaxResults": null,
"TermSortMode": 0,
"IncludeRemainingTerms": false
};
});
url += '&facets=' + JSON.stringify(facets);
request(url, function (error, response, body) {
if (!error && response.statusCode === 200) {
var result = JSON.parse(body);
cb(null, result);
} else {
cb(error || new Error(response.statusCode), null);
}
});
},
/** Loads document with given id.
* @param {string} db
* @param {string} id
* @param {string} wantedPropsFromMetadata(OPTIONAL)
* @param {Function} cb
*/
load: function (db, id, wantedPropsFromMetadata, cb) {
if(Object.prototype.toString.call(wantedPropsFromMetadata) == '[object Function]'){
cb = wantedPropsFromMetadata;
wantedPropsFromMetadata = [];
}
var url = host + 'databases/' + db + '/docs/' + id;
request(url, function (error, response, body) {
if(error || response.statusCode != 200)
return cb(error || new Error(response.statusCode));
var doc = JSON.parse(body);
var meta = _.reduce(response.headers
, function (memo, val, key) {
if (key.indexOf('raven') === 0 || wantedPropsFromMetadata.indexOf(key) != -1)
memo[key] = val;
return memo;
}, {});
meta['@id'] = response.headers['__document_id'];
meta.etag = response.headers['etag'];
meta.dateCreated = response.headers['DateCreated'] || response.headers['datecreated'];
doc['@metadata'] = meta;
cb(null, doc);
});
},
/** Overwrites given document with given id.
* @param {string} db
* @param {string} id
* @param {Object} doc
* @param {Object} metadata
* @param {Function} cb
*/
update: function (db, id, doc, metadata, cb) {
var operations = [
{
Method: "PUT",
Document: doc,
Metadata: metadata,
Key: id
}
];
request.post({
url: host + 'databases/' + db + '/bulk_docs',
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
body: JSON.stringify(operations)
}, function (error, response, resBody) {
if (!error && response.statusCode === 200) {
var result = JSON.parse(resBody);
cb(null, result);
} else {
cb(error || new Error(response.statusCode), null);
}
});
},
/** Applies patch operations to document with given id.
* @param {string} db
* @param {string} id
* @param {Array<Object>} operations
* @param {Function} cb
*/
patch: function (db, id, operations, cb) {
request.patch({
url: host + 'databases/' + db + '/docs/' + id,
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
body: JSON.stringify(operations)
}, function (error, response, resBody) {
if (!error && response.statusCode === 200) {
var result = JSON.parse(resBody);
cb(null, result);
} else {
cb(error || new Error(response.statusCode), null);
}
});
},
/** Stores given document, returns raven generated id in callback.
* @param {string} db
* @param {string} entityName
* @param {Object} doc
* @param {Function} cb
*/
store: function (db, entityName, doc, cb) {
request.post({
url: host + 'databases/' + db + '/docs',
headers: {
'Content-Type': 'application/json; charset=utf-8',
'Raven-Entity-Name': entityName
},
body: JSON.stringify(doc)
}, function (error, response, resBody) {
if (!error && (response.statusCode === 201 || response.statusCode === 200)) {
var result = JSON.parse(resBody);
cb(null, result);
} else {
cb(error || new Error(response.statusCode), null);
}
});
},
save: function (db, entityName, doc, done) {
var now = new Date();
var month = (now.getMonth() + 1).toString();
month = month.length > 1 ? month : "0" + month;
var day = now.getDate().toString();
day = day.length > 1 ? day : "0" + day;
var hours = now.getHours().toString();
hours = hours.length > 1 ? hours : "0" + hours;
var minutes = now.getMinutes().toString();
minutes = minutes.length > 1 ? minutes : "0" + minutes;
var seconds = now.getSeconds().toString();
seconds = seconds.length > 1 ? seconds : "0" + seconds;
var dateTimeNow = now.getFullYear() + '-' + month + '-' + day
+ 'T' + hours + ':' + minutes
+ ':' + seconds + '.0000000+04:00';
var id = doc._id || entityName + '/';
delete doc._id;
var metadata = {
'Raven-Entity-Name': entityName,
'DateCreated': dateTimeNow
};
var cstmMetadata = doc._metadata;
for(var k in cstmMetadata){
if(cstmMetadata.hasOwnProperty(k))
metadata[k] = cstmMetadata[k];
}
delete doc._metadata;
var operations = [
{
Method: "PUT",
Document: doc,
Metadata: metadata,
Key: id
}
];
request.post({
url: host + 'databases/' + db + '/bulk_docs',
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
body: JSON.stringify(operations)
}
, function (error, response, resBody) {
if (!error && (response.statusCode === 201 || response.statusCode === 200)) {
var executedOperations = JSON.parse(resBody);
var operation = executedOperations[0];
done(null, {
Key: operation['Key'],
Etag: operation['Etag']
});
} else {
done(error || new Error(response.statusCode), null);
}
});
}
}
};