Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Riak 2.0. Ability to use special indices $bucket, $key. Ability to provide max_results and continuation in db.query and get continuation back. #187

Closed
wants to merge 32 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
18b419b
fix for https://github.com/mostlyserious/riak-js/issues/180 - allow u…
Sep 4, 2013
08385df
fix for https://github.com/mostlyserious/riak-js/issues/180 - allow u…
Sep 4, 2013
5e2c3cf
tests for special indexes , . test for max_results and continuation
Sep 4, 2013
edbb778
parse index structure from response
sideshowcoder Oct 21, 2013
dc6f75f
better error handling in case of buffer is JSON. Show message instead…
Nov 12, 2013
22e8c8b
added .jshintrc to keep a certain coding style
sideshowcoder Nov 26, 2013
8e50767
fix missing semicolons
sideshowcoder Nov 26, 2013
bd9d006
missing ;
sideshowcoder Nov 30, 2013
4738bb2
make the tests cleanup after themself, comment out search tests
sideshowcoder Nov 30, 2013
35ceeff
some style fixes
sideshowcoder Nov 30, 2013
02119f1
Merge branch 'fix-repositories-to-repository' into riak-2.0
sideshowcoder Nov 30, 2013
1785296
Merge branch 'reading-2ii-to-meta' into riak-2.0
sideshowcoder Nov 30, 2013
a8bb95c
remove the luwak tests
sideshowcoder Dec 3, 2013
4b7d486
move the 1.4 specific tests to their own directory
sideshowcoder Dec 3, 2013
6b8c0d1
prepare version specific search tests
sideshowcoder Dec 3, 2013
63a1214
first basic search via yokozuna passing
sideshowcoder Dec 4, 2013
c690219
Add matrix entry for Riak 2.0.0pre5.
roidrage Dec 15, 2013
b6c47fd
Remove master exclusion for now.
roidrage Dec 15, 2013
eb18bd9
Escape if clause.
roidrage Dec 15, 2013
60ae860
Merge branch 'master' into mm-riak-2.0
roidrage Dec 15, 2013
95b64d4
fix test for protocol buffer key fetch
sideshowcoder Dec 17, 2013
207dfe1
it's enough to clean after all tests are done
sideshowcoder Dec 17, 2013
f2b8b7b
Merge branch 'mm-riak-2.0' into riak-2.0
sideshowcoder Dec 17, 2013
f3c51ea
run 2.0 or 1.4 tests depending on the environment
sideshowcoder Dec 17, 2013
1cdfc74
make sure yokozuna is enabled for 2.0
sideshowcoder Dec 17, 2013
b68301b
make sure the key stream tests does not interfer
sideshowcoder Dec 17, 2013
73ed95b
make sure the config files from riak are removed
sideshowcoder Dec 17, 2013
0ac9d6c
wait for yokozuna index to propagate
sideshowcoder Dec 17, 2013
7683743
is the travis vm that much slower?
sideshowcoder Dec 17, 2013
7f0efbd
fix error handling
Sep 24, 2014
b4b19c9
Merge pull request #1 from sideshowcoder/riak-2.0
mogadanez Sep 24, 2014
02742bf
support for Yokozuna, and some Solr features
Nov 20, 2014
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
3 changes: 3 additions & 0 deletions .jshintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"es5": true
}
10 changes: 6 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
language: node_js
env:
- RIAK_VERSION=default
- RIAK_VERSION=2.0.0pre5 RIAK_RELEASE=2.0
before_install:
- ./install_riak.sh
node_js:
- "0.8"
- "0.10"
services:
- riak
script:
- make test
branches:
only:
- master

13 changes: 10 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
MOCHA_OPTS=-t 20000 test/*-test.js
MOCHA_OPTS = -t 20000
REPORTER = spec

ifeq ($(RIAK_VERSION),default)
TESTS = test/*-test.js test/1.4-specific/*-test.js
else
TESTS = test/*-test.js test/2.0-specific/*-test.js
endif

check: test

test: test-unit

test-unit:
@NODE_ENV=test ./node_modules/.bin/mocha \
--reporter $(REPORTER) \
$(MOCHA_OPTS)
$(MOCHA_OPTS) \
$(TESTS)

.PHONY: test
.PHONY: test
22 changes: 22 additions & 0 deletions install_riak.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/sh

if [ $RIAK_VERSION != "default" ]
then
echo "Stopping Riak service"
sudo service riak stop
sudo apt-get purge riak
riak_file="riak_$RIAK_VERSION-1_amd64.deb"
cd /tmp
echo "Downloading Riak $RIAK_VERSION"
wget http://s3.amazonaws.com/downloads.basho.com/riak/$RIAK_RELEASE/$RIAK_VERSION/ubuntu/precise/$riak_file
echo "Installing Riak $RIAK_VERSION"
sudo dpkg -i $riak_file
echo "Enabling Yokozuna"
sudo sed -i 's/yokozuna = off/yokozuna = on/' /etc/riak/riak.conf
echo "Use leveldb as the backend"
sudo sed -i 's/storage_backend = bitcask/storage_backend = leveldb/' /etc/riak/riak.conf
echo "Set ulimit"
sudo sh -c 'echo "ulimit -n 65536" > /etc/default/riak'
echo "Starting Riak..."
sudo service riak start
fi
12 changes: 9 additions & 3 deletions lib/http-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ var http = require('http'),
HttpMeta = require('./http-meta'),
HttpRequest = require('./http-request'),
HttpSearchClient = require('./http-search-client'),
HttpYokozunaClient = require('./http-yokozuna-client'),
Mapper = require('./mapper'),
HttpMapReduceClient = require('./http-mapreduce-client'),
Pool = require('poolee');
Expand All @@ -29,6 +30,7 @@ var HttpClient = function HttpClient(options) {
this._execute(meta)
}.bind(this);
this.search = new HttpSearchClient(this._defaults, execute);
this.yokozuna = new HttpYokozunaClient(this._defaults, execute);
this.mapreduce = new HttpMapReduceClient(this._defaults, execute);
var pool = this._defaults.pool;
if (pool) {
Expand Down Expand Up @@ -357,14 +359,18 @@ HttpClient.prototype.query = function(bucket, query, options, callback) {
end = value[1];
value = value[0];
}

var type = typeof value == 'number' ? 'int' : 'bin',
key = "index/" + field + "_" + type + "/" + encodeURIComponent(value);


var typedIndex = field != "$bucket" && field != "$key";
var key = "index/" + field + ( typedIndex? ( "_" + (typeof value == 'number' ? 'int' : 'bin') ): "" ) + "/" + encodeURIComponent(value);

if (end) key += "/" + encodeURIComponent(end);

this.get(bucket, key, meta, function(err, data, _meta) {
var continuation = data && data.continuation;
data = data ? data.keys : undefined;
if ( continuation && data )
data.continuation = continuation;
meta.callback(err, data, _meta);
});

Expand Down
56 changes: 54 additions & 2 deletions lib/http-meta.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,15 @@ HttpMeta.prototype.loadResponse = function(response) {
this.links = linkUtils.stringToLinks(response.client._httpMessage._headers.link);
}

// 2ii
var indices = indexUtils.extract(headers);
if (indices) {
this.index = indices;
}

// etag -- replace any quotes in the string
if (headers.etag) this.etag = headers.etag.replace(/"/g, '');

// location
if (headers.location) {
var match = headers.location.match(/^\/([^\/]+)(?:\/([^\/]+))?\/([^\/]+)$/);
Expand Down Expand Up @@ -222,7 +228,10 @@ HttpMeta.queryProperties = [
'vtag',
'returnbody',
'chunked',
'buckets'
'buckets',
'max_results',
'return_terms',
'continuation',
]

module.exports = HttpMeta;
Expand All @@ -247,6 +256,49 @@ var _encodeUri = function(component, encodeUri) {
return encodeURIComponent(component.replace(/\+/g, "%20"));
}

var indexUtils = {
Matcher: /x-riak-index-(.*)_(.*)/,

/**
* Parses indices from a response
* @param {Object} headers
* @return {Object} with indices in `Meta` representation.
* @api private
*/
extract: function(headers) {
var rawIndices;
if (rawIndices = indexUtils.propsAndMatchesByRegex(indexUtils.Matcher, headers)) {
var indices = {};
rawIndices.forEach(function(index) {
var name = index.match[1];
var type = index.match[2];
var value = index.value;
if (type == "int") value = parseInt(value);
indices[name] = value;
})
return indices;
} else {
return false;
}
},

propsAndMatchesByRegex: function(r, obj) {
var m;
var key;
var res = [];
for (key in obj) {
if (m = r.exec(key)) {
res.push({ match: m, value: obj[key] });
}
}
if (res.length > 0) {
return res;
} else {
return false;
}
}
}

var linkUtils = {

/**
Expand Down
10 changes: 9 additions & 1 deletion lib/http-request.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ HttpRequest.prototype.execute = function() {

this.client.emit('riak.request.start', this.event)



var request = httpClient.request(meta, function(err, response) {
var normalstatus = [200,201,204,300,304];
if(self.event.method == 'delete') normalstatus.push(404);
Expand Down Expand Up @@ -236,7 +238,13 @@ HttpRequest.prototype.handleRequestEnd = function(buffer) {
// deal with errors
if (this.meta.statusCode >= 400) {
var err = new Error();
err.message = buffer && buffer.toString().trim();
if ( buffer ){
if ( Array.isArray( buffer ) || buffer.length && Object.prototype.toString.call(buffer[0]) == '[object Number]' )
for ( var i = 0; i< buffer.length; i++ )
err.message += String.fromCharCode(buffer[i])
else
err.message = ( typeof buffer == "string" ? buffer : JSON.stringify( buffer ) ).trim();
}
err.statusCode = this.meta.statusCode;
buffer = err;
}
Expand Down
4 changes: 2 additions & 2 deletions lib/http-search-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ var XML = require('xml'),
*/
var HttpSearchClient = function HttpSearchClient(options, callback) {
this._execute = callback;
this._defaults = {resource: 'solr', method: 'get'};
this._defaults = {resource: 'search/query', method: 'get'};

// need to add the options to the defaults object so as to not overwrite them
// for the calling http-client
Expand Down Expand Up @@ -78,7 +78,7 @@ HttpSearchClient.prototype.remove = function(index, documents, callback) {
* @param {Function} callback(err, data, meta
*/
HttpSearchClient.prototype.find = function(index, query, options, callback) {
var meta = new HttpSearchMeta(this._defaults, options, {callback: callback, index: index, operation: 'select', q: query, wt: 'json', });
var meta = new HttpSearchMeta(this._defaults, options, {callback: callback, index: index, operation: '', q: query, wt: 'json' });
this._run(meta);
}

Expand Down
23 changes: 21 additions & 2 deletions lib/http-search-meta.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,16 @@ Object.defineProperty(HttpSearchMeta.prototype, 'path', {
}.bind(this));

var queryString = querystring.stringify(queryParameters),
operation = "/" + this.operation,
index = "/" + this.index,
operation,
qs = queryString ? "?" + queryString : '';

if(this.operation) {
operation = "/" + this.operation;
} else {
operation = "";
}

return "/" + this.resource + index + operation + qs;
},
enumerable: true
Expand Down Expand Up @@ -84,13 +90,26 @@ var responseMappings = {

HttpSearchMeta.queryProperties = [
'q',
'fq',
'start',
'rows',
'wt',
'sort',
'presort',
'filter',
'fl'
'fl',
'df',
'facet', //TODO: SOLR have huge ammount of options. nightmare to specify it all
'facet.field',
'facet.query',
'stats',
'stats.field',
'group',
'group.field',
'group.main',
'group.sort',
'group.limit'

];

module.exports = HttpSearchMeta;
109 changes: 109 additions & 0 deletions lib/http-yokozuna-client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
var HttpSearchMeta = require('./http-search-meta');

/**
* Initialize a Riak Yokozuna Search client.
* Expects only a callback that's call to execute a search query, e.g.
* in the HttpClient, therefore doesn't have any coupling to the
* underlying transport.
*
* @param {Object} options
* @param {Function} callback(meta)
*/
var HttpYokozunaClient = function HttpYokozunaClient(options, callback) {
this._execute = callback;
this._defaults = {resource: 'search/query', method: 'get'};

// need to add the options to the defaults object so as to not overwrite them
// for the calling http-client

if (options.host) {
this._defaults.host = options.host;
}

if (options.port) {
this._defaults.port = options.port;
}

if (options.client) {
this._defaults.client = options.client;
}
};

/**
* Add a search index to yokozuna
*
* @param {String} index
* @param {String} schema
* @param {Function} callback(err)
*
* @api public
*/
HttpYokozunaClient.prototype.createIndex = function(index, schema, callback) {
if (typeof schema === 'function') {
callback = schema;
schema = null;
schema = null;
}

var meta = new HttpSearchMeta(this._defaults, {resource: 'yz'}, {method: 'put', callback: callback, index: 'index', operation: index});

this._execute(meta);
};

/**
* Remove a search index from yokozuna
*
* @param {String} index
* @param {String} schema
* @param {Function} callback(err)
*
* @api public
*/
HttpYokozunaClient.prototype.getIndex = function(index, callback) {
var meta = new HttpSearchMeta(this._defaults, {resource: 'yz'}, {callback: callback, index: 'index', operation: index});

this._execute(meta);
};

/**
* Find a set of documents in Riak Yokozuna in the specified index.
* The query must be a valid query string as described in the Riak Yokozuna
* documentation.
*
* The options can include all query options supported by Riak Yokozuna.
*
* @param {String} index
* @param {String} query
* @param {Object} options
* @param {Function} callback(err, data, meta
*/
HttpYokozunaClient.prototype.find = function(index, query, options, callback) {
var meta = new HttpSearchMeta(this._defaults, options, {callback: callback, index: index, q: query, wt: 'json' });
this._run(meta);
};


HttpYokozunaClient.prototype._run = function(meta) {
var callback = meta.callback;
meta.callback = function(err, data, meta) {
if (err) {
return callback(err, data, meta);
}

try {
data = JSON.parse(data.toString());
} catch (e) {
return callback(e, e, meta);
}
if ( !data.response) data.response = {};

data.response.facet_counts = data.facet_counts;
data.response.stats = data.stats;
data.response.grouped = data.grouped;

callback(err, data.response, meta);
};
this._execute(meta);
};

module.exports = HttpYokozunaClient;
Loading