Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions lib/objects/statistics.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@ var Statistics = module.exports = (function () {
function Statistics (stats) {
if (stats) {
this.total = stats.TotalResults ? stats.TotalResults : stats.Results.length;
this.skipped = stats.SkippedResults ? stats.SkippedResults : 0;

if (stats.Stale) {
this.stale = stats.Stale;
}

if (stats.SkippedResults) {
this.skipped = stats.SkippedResults;
}

if (stats.IndexName) {
this.index = stats.IndexName;
}
Expand Down
118 changes: 85 additions & 33 deletions lib/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ var Session = module.exports = (function () {
document: '/docs/%s',
queries: '/queries',
bulk: '/bulk_docs',
dynamic: '/indexes/dynamic/%s?query=%s',
index: '/indexes/%s?query=%s'
dynamic: '/indexes/dynamic/%s',
index: '/indexes/%s'
};

Session.prototype._request = function (method, query, callback, handlers, writeable, headers) {
Expand Down Expand Up @@ -83,8 +83,13 @@ var Session = module.exports = (function () {
var json = JSON.parse(data);

if (response.statusCode > 299 || json.Error) {
error = new Error(json.Error.match(/: (.*)\r\n/)[1] || 'An error occured.');
error.statusCode = response.statusCode;
var errorDetail = new Error(json.Error || 'An error occured.');
errorDetail.statusCode = response.statusCode;
if (error) {
error.inner = errorDetail;
} else {
error = errorDetail;
}
} else if (typeof handlers.json === 'function') {
args = args.concat(handlers.json(json, response.headers));
}
Expand Down Expand Up @@ -249,36 +254,86 @@ var Session = module.exports = (function () {
}
};

Session.prototype._buildSpecifications = function (specifications) {
query = '';
Session.prototype._parseQueryParameters = function (parameters, parentKey) {
if (Array.isArray(parameters)) {
var statements = [];
for (var i=0; i<parameters.length; i++) {
statements.push(this._parseQueryParameters(parameters[i], parentKey));
}
if (statements.length === 1) {
return statements[0];
} else if (statements.length > 1) {
return '(' + statements.join(' OR ') + ')';
}
} else if (typeof parameters === 'object') {
var statements = [];
for (var name in parameters) {
if (parameters.hasOwnProperty(name)) {
statements.push(this._parseQueryParameters(parameters[name], name));
}
}
if (statements.length === 1) {
return statements[0];
} else if (statements.length > 1) {
return '(' + statements.join(' AND ') + ')';
}
} else {
var statement = '(' + parameters + ')'
if (typeof parentKey === 'string') {
statement = parentKey + ':' + statement
}
return statement;
}
return ''
}

Session.prototype._buildQuery = function (urlFormat, urlTypeName, specifications) {
if (!urlFormat || !urlTypeName) {
return;
}

var query = util.format(urlFormat, urlTypeName);
if (specifications !== null && typeof specifications === 'object') {
var queryParameters = {};

if (typeof specifications === 'object') {
if (Array.isArray(specifications.projections)) {
query += '&' + qs.stringify({ fetch: specifications.projections }, '&', '=');
} else if (typeof specifications.projections === 'string') {
query += '&fetch=' + specifications.projections;
if (typeof specifications.query === 'string' && specifications.query.length > 0) {
queryParameters.query = specifications.query;
} else {
if (typeof specifications.parameters === 'object') {
queryParameters.query = this._parseQueryParameters(specifications.parameters);
}
}

if (Array.isArray(specifications.sort)) {
query += '&' + qs.stringify({ sort: specifications.sort }, '&', '=');
} else if (typeof specifications.sort === 'string') {
query += '&sort=' + specifications.sort;
if (Array.isArray(specifications.projections) || typeof specifications.projections === 'string') {
queryParameters.fetch = specifications.projections;
}

if (typeof specifications.start === 'number') {
query += '&start=' + specifications.start;
if (Array.isArray(specifications.sort) || typeof specifications.sort === 'string') {
queryParameters.sort = specifications.sort
}

if (typeof specifications.pageSize === 'number') {
query += '&pageSize=' + specifications.pageSize;
if (typeof specifications.skip === 'number') {
if (specifications.skip > 0) {
queryParameters.start = specifications.skip;
}
}

if (typeof specifications.take === 'number') {
if (specifications.take >= 0) {
queryParameters.pageSize = specifications.take;
}
}
}

var sQueryParameters = qs.stringify(queryParameters);
if (sQueryParameters.length > 0) {
query += '?' + sQueryParameters;
}
}
return query;
}

Session.prototype.query = function (type, parameters, specifications, callback) {
if (!type || !parameters) {
Session.prototype.query = function (type, specifications, callback, singular) {
if (!type) {
return;
}

Expand All @@ -291,19 +346,17 @@ var Session = module.exports = (function () {
specifications = undefined;
}

var plural = inflector.pluralize(type);
var query = util.format(masks.dynamic, (plural.charAt(0).toUpperCase() + plural.slice(1)), qs.stringify(parameters, '%20AND%20', ':'));

query += this._buildSpecifications(specifications);
var typeName = singular ? type : inflector.pluralize(type);
var query = this._buildQuery(masks.dynamic, (typeName.charAt(0).toUpperCase() + typeName.slice(1)), specifications);

this._request('GET', query, callback, { json: function (json) {
return [ new Queryable(json.Results), new Statistics(json) ];
}
});
};

Session.prototype.index = function (index, parameters, specifications, callback) {
if (!index || !parameters) {
Session.prototype.index = function (index, specifications, callback) {
if (!index) {
return;
}

Expand All @@ -312,9 +365,7 @@ var Session = module.exports = (function () {
specifications = undefined;
}

var query = util.format(masks.index, index, qs.stringify(parameters, '%20AND%20', ':'));

query += this._buildSpecifications(specifications);
var query = this._buildQuery(masks.index, index, specifications);

this._request('GET', query, callback, { json: function (json) {
return [ new Queryable(json.Results), new Statistics(json) ];
Expand Down Expand Up @@ -356,7 +407,7 @@ var Session = module.exports = (function () {

Session.prototype.save = function (callback) {
if (Object.keys(this._store).length === 0 && Object.keys(this._changes).length === 0) {
return;
return false;
}

var session = this,
Expand Down Expand Up @@ -399,7 +450,7 @@ var Session = module.exports = (function () {
}

if (batch.length === 0) {
return;
return false;
}

handlers.json = function (json) {
Expand All @@ -417,6 +468,7 @@ var Session = module.exports = (function () {
};

session._request('POST', masks.bulk, callback, handlers, batch);
return true;
};

Session.prototype.delete = function (document, etag, callback) {
Expand Down
1 change: 1 addition & 0 deletions lib/util/inflector.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ var inflector = {

while (i--) {
var rule = rules[i][0];
rule.lastIndex = 0 //Prevent cached Rexex from starting the search at the previous location in the string

if(rule.test(word)) {
return word.replace(rule, rules[i][1]);
Expand Down
26 changes: 26 additions & 0 deletions test/session.test.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,29 @@ describe 'session ->', ->
it 'when passed an array containing both valid and invalid objects, store should only add valid documents to the tracker', ->
session.store([ rudolph, 2, 3 ])
Object.keys(session._store).length.should.equal(1)

describe 'building a query ->', ->
it 'should build simple AND query', ->
queryParams =
name: 'max'
type: 'dog'
expect = '(name:(max) AND type:(dog))'
query = session._parseQueryParameters queryParams
query.should.equal expect
it 'should build simple OR query', ->
queryParams =
_or: [
name: 'max'
,
type: 'dog'
]
expect = '(name:(max) OR type:(dog))'
query = session._parseQueryParameters queryParams
query.should.equal expect
it 'should build complex AND and OR query parameters', ->
queryParams =
name: ['max', { name: 'rex' }, { _special_type: [{ sex: 'female', breed: 'boxer' }] } ]
type: ['dog', 'cat']
expect = '((name:(max) OR name:(rex) OR (sex:(female) AND breed:(boxer))) AND (type:(dog) OR type:(cat)))'
query = session._parseQueryParameters queryParams
query.should.equal expect