├── .gitignore
├── lib
├── literal.js
├── minval.js
├── maxval.js
├── constants.js
├── asc.js
├── desc.js
├── node.js
├── error.js
├── database.js
├── missing_doc.js
├── local_connection.js
├── index.js
├── protodef.js
├── group.js
├── date.js
├── connection.js
├── geo.js
├── document.js
├── selection.js
└── table.js
├── config.js
├── test.js
├── .travis.yml
├── browser
├── index.html
└── example.js
├── tool
└── generateTests.js
├── bin
└── reqlite
├── browserify.js
├── test
├── coverage.js
├── meta.js
├── util.js
├── manipulating-databases.js
├── datum.js
├── extra.js
├── string-manipulation.js
├── manipulating-tables.js
├── selecting-data.js
└── joins.js
├── LICENSE
├── package.json
├── CONTRIBUTING.md
├── README.md
└── .eslintrc
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | npm-debug.log
3 | node_modules
4 | test.js
5 | rethinkdb_data
6 | test/new.js
7 | repos
8 | browser/reqlite.js
9 | browser/rethinkdb.js
10 | browser/rethinkdbdash.js
11 |
--------------------------------------------------------------------------------
/lib/literal.js:
--------------------------------------------------------------------------------
1 | function Literal(value) {
2 | this.value = value;
3 | this.literal = true; //TODO Remove when done debugging
4 | }
5 | module.exports = Literal;
6 |
7 | Literal.prototype.toDatum = function() {
8 | return this.value;
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/config.js:
--------------------------------------------------------------------------------
1 | module.exports = {};
2 |
3 | module.exports.reqlite = {
4 | host: 'localhost',
5 | port: 28016,
6 | db: 'reqlitetest',
7 | authKey: ''
8 | }
9 |
10 | module.exports.rethinkdb = {
11 | host: 'localhost',
12 | port: 28015,
13 | db: 'reqlitetest',
14 | authKey: ''
15 | }
16 |
--------------------------------------------------------------------------------
/test.js:
--------------------------------------------------------------------------------
1 | var Promise = require('bluebird');
2 |
3 | Promise.map([100,200,150,400,50], function(value) {
4 | return new Promise(function(r, rr) {
5 | setTimeout(function() { return r(value) }, value)
6 | })
7 | }, {concurrency: 10}).then(function(result) {
8 | console.log(result);
9 | })
10 |
11 |
--------------------------------------------------------------------------------
/lib/minval.js:
--------------------------------------------------------------------------------
1 | function Minval() {
2 | this.minval = true; //TODO Remove when done debugging
3 | }
4 | module.exports = Minval;
5 |
6 | var Error = require(__dirname+"/error.js");
7 |
8 | Minval.prototype.toJSON = function() {
9 | throw new Error.ReqlRuntimeError('Cannot convert `r.minval` to JSON' /* ?? , query.frames */);
10 | };
11 |
--------------------------------------------------------------------------------
/lib/maxval.js:
--------------------------------------------------------------------------------
1 | function Maxval() {
2 | this.maxval = true; //TODO Remove when done debugging
3 | }
4 | module.exports = Maxval;
5 |
6 | var Error = require(__dirname+"/error.js");
7 |
8 | Maxval.prototype.toJSON = function() {
9 | throw new Error.ReqlRuntimeError('Cannot convert `r.maxval` to JSON' /* ?? , query.frames */);
10 | };
11 |
12 |
--------------------------------------------------------------------------------
/lib/constants.js:
--------------------------------------------------------------------------------
1 | // Number of rows per batch for streams (currently r.range() only)
2 | module.exports.ROW_PER_BATCH = 40;
3 |
4 | // Print debug output
5 | module.exports.DEBUG = false;
6 |
7 | // Enable r.js
8 | module.exports.ENABLE_JS = true;
9 |
10 | // Print RethinkDB's info
11 | module.exports.SILENT = false;
12 |
13 | // Namespace for uuid v5
14 | module.exports.UUID_NAMESPACE = '91461c99-f89d-49d2-af96-d8e2e14e9b58';
15 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | sudo: false
3 | node_js:
4 | - "node"
5 | before_install:
6 | - wget http://download.rethinkdb.com/apt/pool/precise/main/r/rethinkdb/rethinkdb_2.2.1~0precise_amd64.deb
7 | - ar x *.deb
8 | - tar xvzf data.tar.gz
9 | before_script:
10 | - ls -al
11 | - ./usr/bin/rethinkdb --daemon
12 | - npm install
13 | - ./bin/reqlite --port-offset 1 &
14 | - sleep 10
15 | after_script:
16 | - rethinkdb
17 | notifications:
18 | email: false
19 |
--------------------------------------------------------------------------------
/lib/asc.js:
--------------------------------------------------------------------------------
1 | function Asc(value) {
2 | this.value = value;
3 | }
4 | module.exports = Asc;
5 |
6 | var Error = require(__dirname+"/error.js");
7 |
8 | Asc.prototype.toJSON = function() {
9 | throw new Error.ReqlRuntimeError('ASC may only be used as an argument to ORDER_BY', []);
10 | };
11 |
12 | Asc.prototype.toDatum = function(query) {
13 | //TODO check everywhere that an instance of ASC is not returned.
14 | throw new Error.ReqlRuntimeError('ASC may only be used as an argument to ORDER_BY', query.frames);
15 | };
16 |
17 |
--------------------------------------------------------------------------------
/lib/desc.js:
--------------------------------------------------------------------------------
1 | function Desc(value) {
2 | this.value = value;
3 | }
4 | module.exports = Desc;
5 |
6 | var Error = require(__dirname+"/error.js");
7 |
8 | Desc.prototype.toJSON = function() {
9 | throw new Error.ReqlRuntimeError('DESC may only be used as an argument to ORDER_BY', []);
10 | };
11 |
12 | Desc.prototype.toDatum = function(query) {
13 | //TODO check everywhere that an instance of DESC is not returned.
14 | throw new Error.ReqlRuntimeError('DESC may only be used as an argument to ORDER_BY', query.frames);
15 | };
16 |
17 |
--------------------------------------------------------------------------------
/lib/node.js:
--------------------------------------------------------------------------------
1 | // For testing
2 | var Server = require(__dirname+"/index.js");
3 | var argv = require('minimist')(process.argv.slice(2));
4 | var options = {};
5 | for(var key in argv) {
6 | switch(key) {
7 | case "o":
8 | options['driver-port'] = parseInt(argv[key], 10)+28015;
9 | break;
10 | case "port-offset":
11 | options['driver-port'] = parseInt(argv[key], 10)+28015;
12 | break;
13 | //case "debug"
14 | //case "silent"
15 | default:
16 | options[key] = argv[key];
17 | }
18 | }
19 |
20 | // Start a new server
21 | new Server(options);
22 |
23 |
--------------------------------------------------------------------------------
/browser/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | title
4 |
5 |
6 |
7 |
8 |
9 | Quick run
10 |
11 | - Run `node browserify.js` in the reqlite repository
12 | - Run `npm run browserify` in the rethinkdbdash repository
13 | - Copy `rethinkdbdash.js` in browser/
14 | - Run `python -m SimpleHTTPServer`
15 | - Open http://localhost:8000
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/tool/generateTests.js:
--------------------------------------------------------------------------------
1 | var values = [
2 | '"foo"',
3 | '""',
4 | '0',
5 | '1',
6 | '-1',
7 | '10000',
8 | 'true',
9 | 'false',
10 | '[]',
11 | '[1,2,3]',
12 | '[["foo", 2]]',
13 | '[["foo", 2], ["bar", 1]]',
14 | '{}',
15 | '{foo: "bar"}',
16 | '{foo: "bar", buzz: 1}',
17 | 'null',
18 | 'r.now()',
19 | 'function() { return 1 }',
20 | 'r.db(TEST_DB).table(TEST_TABLE)',
21 | 'r.binary(new Buffer("hello"))',
22 | ]
23 | var types = [
24 | '"array"',
25 | '"string"',
26 | '"object"',
27 | '"array"',
28 | '"binary"'
29 | ]
30 |
31 | var count = 1;
32 | for(var i in values) {
33 | var value1 = values[i]
34 | console.log(" it('info - "+count+"', function(done) {");
35 | console.log(" var query = r.expr("+value1+").info()");
36 | console.log(" compare(query, done)");
37 | console.log(" })");
38 | console.log("");
39 | count++;
40 | }
41 |
--------------------------------------------------------------------------------
/bin/reqlite:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var Server = require("./../lib/index.js");
4 | var argv = require('minimist')(process.argv.slice(2));
5 | var options = {};
6 | for(var key in argv) {
7 | switch(key) {
8 | case "_":
9 | break;
10 | case "driver-port":
11 | options['driver-port'] = parseInt(argv[key], 10)+28015;
12 | break;
13 | case "port-offset":
14 | options['driver-port'] = parseInt(argv[key], 10)+28015;
15 | break;
16 | case "debug":
17 | options.debug = argv[key];
18 | break;
19 | default:
20 | console.log([
21 | "usage: reqlite [options]",
22 | "",
23 | "options:",
24 | " --driver-port port Driver port for reqlite, default 28015",
25 | " --port-offset port Port offset for the driver, default 0"
26 | ].join('\n'));
27 | process.exit();
28 | }
29 | }
30 | var server = new Server(options);
31 |
--------------------------------------------------------------------------------
/browserify.js:
--------------------------------------------------------------------------------
1 | var browserify = require('browserify');
2 | var fs = require('fs');
3 |
4 | var REQUIRE_FILES = [
5 | 'asc.js',
6 | 'changes.js',
7 | 'connection.js',
8 | 'constants.js',
9 | 'database.js',
10 | 'date.js',
11 | 'desc.js',
12 | 'document.js',
13 | 'error.js',
14 | 'geo.js',
15 | 'group.js',
16 | 'literal.js',
17 | 'local_connection.js',
18 | 'maxval.js',
19 | 'minval.js',
20 | 'missing_doc.js',
21 | 'node.js',
22 | 'protodef.js',
23 | 'query.js',
24 | 'selection.js',
25 | 'sequence.js',
26 | 'table.js',
27 | 'utils.js'
28 | ];
29 |
30 | var b = browserify('./lib/')
31 | b.add('./lib/index.js')
32 | for(var i=0; i 0) {
26 | done(new Error('Some terms were not found: '+JSON.stringify(missing)));
27 | }
28 | else {
29 | done();
30 | }
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013-2015 Michel Tu
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
4 | software and associated documentation files (the 'Software'), to deal in the Software
5 | without restriction, including without limitation the rights to use, copy, modify, merge,
6 | publish, distribute, sublicense, and/or sell copies of the Software, and to permit
7 | persons to whom the Software is furnished to do so, subject to the following conditions:
8 |
9 | The above copyright notice and this permission notice shall be included in all copies or
10 | substantial portions of the Software.
11 |
12 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17 | DEALINGS IN THE SOFTWARE.
18 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "reqlite",
3 | "version": "2.3.0",
4 | "description": "",
5 | "main": "lib/index.js",
6 | "bin": {
7 | "reqlite": "bin/reqlite"
8 | },
9 | "directories": {
10 | "test": "test"
11 | },
12 | "scripts": {
13 | "test": "mocha --check-leaks -t 20000",
14 | "lint": "eslint lib --quiet && eslint test --quiet",
15 | "browserify": "node browserify.js"
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "git://github.com/neumino/reqlite"
20 | },
21 | "keywords": [],
22 | "author": "Michel Tu (http://justonepixel.com/)",
23 | "license": "MIT",
24 | "readmeFilename": "README.md",
25 | "bugs": {
26 | "url": "https://github.com/neumino/reqlite/issues"
27 | },
28 | "dependencies": {
29 | "bluebird": ">= 2.0.0",
30 | "geographiclib": "^1.46.0-beta1",
31 | "geolib": "2.0.14",
32 | "minimist": "0.2.0",
33 | "moment": "2.10.3",
34 | "request": "2.55.0",
35 | "uuid-1345": "0.99.6"
36 | },
37 | "devDependencies": {
38 | "eslint": "^1.6.0",
39 | "mocha": ">= 1.20.0",
40 | "rethinkdb": "~ 2.3.0",
41 | "rethinkdbdash": "~ 2.3.0",
42 | "browserify": "~ 12.0.0"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/lib/database.js:
--------------------------------------------------------------------------------
1 | function Database(name) {
2 | this.name = name;
3 | this.tables = {};
4 | this.id = util.uuid();
5 | }
6 | module.exports = Database;
7 |
8 | var Table = require(__dirname+"/table.js");
9 | var util = require(__dirname+"/utils.js");
10 |
11 | Database.prototype.table = function(name) {
12 | return this.tables[name];
13 | };
14 |
15 | Database.prototype.tableDrop = function(name) {
16 | delete this.tables[name];
17 | };
18 |
19 | Database.prototype.tableCreate = function(name, options) {
20 | var table = new Table(name, this.name, options);
21 | this.tables[name] = table;
22 | return {
23 | config_changes: [{
24 | new_val: {
25 | db: this.name,
26 | durability: "hard", //TODO Handle optarg
27 | write_acks: "majority", //TODO Handle optarg
28 | id: table.id,
29 | indexes: [],
30 | name: table.name,
31 | primary_key: table.options.primaryKey,
32 | shards: [{
33 | primary_replica: "reqlite",
34 | replicas: ["reqlite"]
35 | }]
36 | },
37 | old_val: null
38 | }],
39 | tables_created: 1
40 | };
41 |
42 | };
43 |
44 | Database.prototype.typeOf = function() {
45 | return "DB";
46 | };
47 |
48 |
--------------------------------------------------------------------------------
/lib/missing_doc.js:
--------------------------------------------------------------------------------
1 | function MissingDoc(primaryKeyValue, table) {
2 | this.primaryKeyValue = primaryKeyValue;
3 | this.table = table;
4 | }
5 | module.exports = MissingDoc;
6 |
7 | var Sequence = require(__dirname+"/sequence.js");
8 |
9 | MissingDoc.prototype.delete = function(options, query) {
10 | var result = {
11 | deleted: 0,
12 | errors: 0,
13 | inserted: 0,
14 | replaced: 0,
15 | skipped: 1,
16 | unchanged: 0
17 | };
18 | if (options.return_changes === true) {
19 | result.changes = new Sequence();
20 | }
21 | return result;
22 | };
23 |
24 | MissingDoc.prototype.replace = function(newValue, options, query, internalOptions) {
25 | var result = {
26 | deleted: 0,
27 | errors: 0,
28 | inserted: 0,
29 | replaced: 0,
30 | skipped: 1,
31 | unchanged: 0
32 | };
33 | if (options.return_changes === true) {
34 | result.changes = new Sequence();
35 | }
36 | return result;
37 | };
38 |
39 | MissingDoc.prototype.update = function(newValue, options, query, internalOptions) {
40 | var result = {
41 | deleted: 0,
42 | errors: 0,
43 | inserted: 0,
44 | replaced: 0,
45 | skipped: 1,
46 | unchanged: 0
47 | };
48 | if (options.return_changes === true) {
49 | result.changes = new Sequence();
50 | }
51 | return result;
52 |
53 | };
54 |
55 | MissingDoc.prototype.toDatum = function() {
56 | return null;
57 | };
58 |
59 |
--------------------------------------------------------------------------------
/browser/example.js:
--------------------------------------------------------------------------------
1 | var Reqlite = require('reqlite');
2 | var r = require('rethinkdbdash')({pool: false});
3 |
4 | var server = new Reqlite();
5 | var fakeTcpConnection = server.createConnection();
6 |
7 | r.connect({
8 | connection: fakeTcpConnection
9 | }).bind({}).then(function(connection) {
10 | this.connection = connection;
11 | console.log('Connected');
12 | return r.expr('Ok').run(this.connection);
13 | }).then(function(result) {
14 | console.log(JSON.stringify(result, null, 2));
15 | return r.dbCreate('blog').run(this.connection);
16 | }).then(function(result) {
17 | console.log(JSON.stringify(result, null, 2));
18 | return r.db('blog').tableCreate('post').run(this.connection);
19 | }).then(function(result) {
20 | console.log(JSON.stringify(result, null, 2));
21 | return r.db('blog').table('post').insert([{
22 | title: 'First post',
23 | author: 'Michel'
24 | },
25 | {
26 | title: 'Second post',
27 | author: 'Daniel'
28 | },
29 | ]).run(this.connection);
30 | }).then(function(result) {
31 | console.log(JSON.stringify(result, null, 2));
32 | return r.db('blog').table('post').run(this.connection);
33 | }).then(function(result) {
34 | console.log(JSON.stringify(result, null, 2));
35 | return r.db('blog').table('post').filter({title: 'First post'}).update({
36 | updated: r.now()
37 | }).run(this.connection);
38 | }).then(function(result) {
39 | console.log(JSON.stringify(result, null, 2));
40 | return r.db('blog').table('post').run(this.connection);
41 | }).then(function(result) {
42 | console.log(JSON.stringify(result, null, 2));
43 | return r.http('http://httpbin.org').run(this.connection);
44 | }).then(function(result) {
45 | console.log(JSON.stringify(result, null, 2));
46 | return this.connection.close();
47 | }).then(function() {
48 | console.log('Done');
49 | }).catch(function(error) {
50 | console.log(error);
51 | });
52 |
--------------------------------------------------------------------------------
/lib/local_connection.js:
--------------------------------------------------------------------------------
1 | var _util = require('util');
2 | var Connection = require(__dirname+"/connection.js");
3 | var EventEmitter = require('events').EventEmitter;
4 |
5 | function LocalConnection(server) {
6 | var self = this;
7 | this.server = server;
8 | this.clientSocket = new Socket();
9 | this.serverSocket = new Socket();
10 |
11 | this.clientSocket._setOther(this.serverSocket);
12 | this.serverSocket._setOther(this.clientSocket);
13 |
14 | this.connection = new Connection(this.serverSocket, this.server, {
15 | version: this.server.protocolVersion,
16 | authKey: this.server.authKey,
17 | protocol: this.server.protocol
18 | });
19 | //TODO Bind listener to destroy the connection
20 |
21 | setTimeout(function() {
22 | self.clientSocket.emit('connect');
23 | }, 0);
24 | }
25 |
26 | LocalConnection.prototype.getClientSocket = function() {
27 | return this.clientSocket;
28 | }
29 |
30 | LocalConnection.prototype.getServerSocket = function() {
31 | return this.serverSocket;
32 | }
33 |
34 | function Socket() {
35 | this.open = true;
36 | }
37 | _util.inherits(Socket, EventEmitter);
38 |
39 | Socket.prototype._setOther = function(socket) {
40 | this.other = socket;
41 | }
42 | Socket.prototype.write = function(buffer) {
43 | this.other.emit('data', buffer);
44 | }
45 |
46 | //TODO Implement these as needed
47 | Socket.prototype.connect = function(buffer) {}
48 | Socket.prototype.end = function(buffer) {}
49 | Socket.prototype.pause = function(buffer) {}
50 | Socket.prototype.ref = function(buffer) {}
51 | Socket.prototype.resume = function(buffer) {}
52 | Socket.prototype.setEncoding = function(buffer) {}
53 | Socket.prototype.setKeepAlive = function(buffer) {}
54 | Socket.prototype.setNoDelay = function(buffer) {}
55 | Socket.prototype.setTimeout = function(buffer) {}
56 | Socket.prototype.unref = function(buffer) {}
57 |
58 | module.exports = LocalConnection;
59 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Contributing
2 |
3 | You can contribute in different ways:
4 |
5 | - Open an issue. If you do, please provide enough information for us to be
6 | able to reproduce the bug:
7 |
8 | - The query that failed.
9 | - The expected output.
10 | - The data in your table if your query involve accessing data from a table.
11 |
12 | - Submit a pull requests. Before submiting a pull request:
13 |
14 | - Make sure that the tests pass.
15 | - Follow the existing coding style, there are no strong rules at the moment, but please
16 | try to keep a similar style.
17 | - If you fix a bug, add a test. This make merging the request easier since with Travis, we
18 | won't have to locally test your code
19 |
20 |
21 | ## How Reqlite works?
22 |
23 | You first need to understand how queries are represented in ReQL. [This document](http://rethinkdb.com/docs/writing-drivers/)
24 | should give a good introduction - you can skip the begining about opening a TCP connection and the token/identification
25 | mechanism.
26 |
27 | Given a query, we evaluate it by recursing as needed.
28 | The main function you are probably interested is `Query.prototype.evaluate`.
29 |
30 | ## How the tests work?
31 |
32 | You can add a test by adding the following code:
33 |
34 | ```js
35 | it(' - ', function(done) {
36 | var query = r.expr(1) // Write your query here
37 | compare(query, done);
38 | });
39 | ```
40 |
41 | The method `compare` will execute the query against Reqlite and RethinkDB and compare the output.
42 | If your output includes generated keys, random numbers, you can provide a third argument to `compare`
43 | to alter the results:
44 |
45 | ```js
46 | it(' - ', function(done) {
47 | var query = r.table('test').insert({}) // Write your query here
48 | compare(query, done, function(result) {
49 | delete result.generated_keys;
50 | return result;
51 | });
52 | });
53 | ```
54 |
55 | ## Have more questions?
56 |
57 | Open an issue such that we can improve the docs, the code and everything else.
58 |
59 | ## Have fun!
60 |
61 | It's the most important part :-)
62 |
--------------------------------------------------------------------------------
/test/meta.js:
--------------------------------------------------------------------------------
1 | var config = require('./../config.js');
2 |
3 | var r = require('rethinkdb');
4 | //var r = require('rethinkdbdash')({pool: false});
5 |
6 | var connections = {};
7 | var TEST_DB = 'reqlitetest';
8 | var TEST_TABLE = 'reqlitetestmeta';
9 | var MISSING_ID = 'nonExistingId';
10 | var MISSING_FIELD = 'nonExistingField';
11 | var MISSING_INDEX = 'nonExistingIndex';
12 | var COMPLEX_OBJECT = {
13 | id: 1,
14 | foo: 2,
15 | buzz: {
16 | hello: [10, 11, 12],
17 | world: {
18 | nested: 100,
19 | field: 200
20 | },
21 | bonjour: [
22 | {monde: 1000, ciao: 4000},
23 | {monde: 2000, ciao: 5000},
24 | {monde: 3000, ciao: 6000},
25 | ]
26 | },
27 | };
28 |
29 | var compare = require('./util.js').generateCompare(connections);
30 |
31 | describe('meta.js', function(){
32 | before(function(done) {
33 | setTimeout(function() { // Delay for nodemon to restart the server
34 | r.connect(config.rethinkdb).bind({}).then(function(conn) {
35 | connections.rethinkdb = conn;
36 | return r.connect(config.reqlite);
37 | }).then(function(conn) {
38 | connections.reqlite = conn;
39 | this.query = r.dbCreate(TEST_DB);
40 | return this.query.run(connections.rethinkdb);
41 | }).catch(function(e) { // ignore errors
42 | }).finally(function() {
43 | return this.query.run(connections.reqlite);
44 | }).catch(function() { // ignore errors
45 | }).finally(function() {
46 | this.query = r.db(TEST_DB).tableDrop(TEST_TABLE);
47 | return this.query.run(connections.rethinkdb);
48 | }).catch(function() { // ignore errors
49 | }).finally(function() {
50 | return this.query.run(connections.reqlite);
51 | }).catch(function() { // ignore errors
52 | }).finally(function() {
53 | done();
54 | });
55 | }, 300);
56 | });
57 |
58 |
59 | //Test playground
60 |
61 |
62 | it('cluster_config - 1', function(done) {
63 | var query = r.db('rethinkdb').table('cluster_config');
64 | compare(query, done);
65 | });
66 |
67 | it('cluster_config - 2', function(done) {
68 | var query = r.db('rethinkdb').table('cluster_config').insert({});
69 | compare(query, done, function(result) {
70 | delete result.generated_keys;
71 | return result;
72 | });
73 | });
74 |
75 | it('cluster_config - 3', function(done) {
76 | var query = r.db('rethinkdb').table('cluster_config').get('auth');
77 | compare(query, done);
78 | });
79 |
80 | /*
81 | */
82 | });
83 |
84 |
--------------------------------------------------------------------------------
/test/util.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 |
3 | module.exports.removeId = function(doc) {
4 | delete doc.id;
5 | return doc;
6 | };
7 |
8 | module.exports.generateCompare = function(connections) {
9 | return function(query, done, transform, raw) {
10 | if (typeof transform !== 'function') {
11 | transform = function(x) { return x; };
12 | }
13 | query.run(connections.rethinkdb).then(function(resultrethinkdb) {
14 | query.run(connections.reqlite).then(function(resultreqlite) {
15 | if ((resultrethinkdb != null) && typeof resultrethinkdb.toArray === 'function') {
16 | if (raw === true) {
17 | transform(resultreqlite, resultrethinkdb); // The user has to call done here
18 | }
19 | else {
20 | resultrethinkdb.toArray().then(function(resultrethinkdb) {
21 | try {
22 | assert.deepEqual(transform(resultreqlite), transform(resultrethinkdb));
23 | done();
24 | } catch(err) {
25 | console.log(JSON.stringify(transform(resultreqlite), null, 2));
26 | console.log(JSON.stringify(transform(resultrethinkdb), null, 2));
27 | done(err);
28 | }
29 | });
30 | }
31 | }
32 | else {
33 | try {
34 | assert.deepEqual(transform(resultreqlite), transform(resultrethinkdb));
35 | done();
36 | } catch(err) {
37 | console.log(JSON.stringify(transform(resultreqlite), null, 2));
38 | console.log(JSON.stringify(transform(resultrethinkdb), null, 2));
39 | done(err);
40 | }
41 | }
42 | }).error(function(error) {
43 | done(new Error("Reqlite failed with"+JSON.stringify(error.message)+'\n'+JSON.stringify(error.stack)));
44 | }).catch(done);
45 | }).error(function(errorrethinkdb) {
46 | query.run(connections.reqlite).then(function(resultreqlite) {
47 | //console.log(resultreqlite);
48 | done(new Error("Rethinkdb failed with"+JSON.stringify(errorrethinkdb.message)+'\n'+JSON.stringify(errorrethinkdb.stack)+'\nReqlite result'+JSON.stringify(resultreqlite, null, 2)));
49 | }).error(function(errorreqlite) {
50 | try {
51 | assert.equal(transform(errorreqlite.message), transform(errorrethinkdb.message));
52 | done();
53 | } catch(err) {
54 | console.log('=== reqlite / rethinkdb ===');
55 | console.log(transform(errorreqlite.message));
56 | console.log(transform(errorrethinkdb.message));
57 | console.log(transform(errorreqlite.frames));
58 | console.log(transform(errorrethinkdb.frames));
59 |
60 | done(err);
61 | }
62 | });
63 | }).catch(done);
64 | };
65 | };
66 |
--------------------------------------------------------------------------------
/test/manipulating-databases.js:
--------------------------------------------------------------------------------
1 | var config = require('./../config.js');
2 |
3 | var r = require('rethinkdb');
4 | var assert = require('assert');
5 | var util = require('./util.js');
6 |
7 | var connections = {};
8 | var TEST_DB = 'reqlitetestmanipdb';
9 |
10 | var compare = require('./util.js').generateCompare(connections);
11 |
12 | describe('manipulating-databases.js', function(){
13 | before(function(done) {
14 | setTimeout(function() { // Delay for nodemon to restart the server
15 | r.connect(config.rethinkdb).bind({}).then(function(conn) {
16 | connections.rethinkdb = conn;
17 | return r.connect(config.reqlite);
18 | }).then(function(conn) {
19 | connections.reqlite = conn;
20 | this.query = r.dbDrop(TEST_DB);
21 | return this.query.run(connections.rethinkdb);
22 | }).catch(function() { // ignore errors
23 | }).finally(function() {
24 | return this.query.run(connections.reqlite);
25 | }).catch(function() { // ignore errors
26 | }).finally(function() {
27 | done();
28 | });
29 | }, 400);
30 | });
31 |
32 | it('dbCreate - 1', function(done) {
33 | var query = r.dbCreate(TEST_DB);
34 | compare(query, done, function(doc) {
35 | delete doc.config_changes[0].new_val.id;
36 | return doc;
37 | });
38 | });
39 |
40 | it('dbCreate - 2', function(done) {
41 | var query = r.dbCreate('foo', 'bar');
42 | compare(query, done);
43 | });
44 |
45 | it('dbCreate - 3', function(done) {
46 | var query = r.dbCreate('fo~o');
47 | compare(query, done);
48 | });
49 |
50 | it('dbCreate - 4', function(done) {
51 | var query = r.dbCreate(1);
52 | compare(query, done);
53 | });
54 |
55 | it('dbCreate - 5', function(done) {
56 | var query = r.dbCreate('foo_bar');
57 | compare(query, done, function(doc) {
58 | delete doc.config_changes[0].new_val.id;
59 | return doc;
60 | });
61 | });
62 | it('dbCreate - 5 - follow up', function(done) {
63 | var query = r.dbDrop('foo_bar');
64 | compare(query, done, function(result) {
65 | return result.dbs_dropped;
66 | });
67 | });
68 |
69 | it('dbCreate - 6', function(done) {
70 | var query = r.dbCreate(TEST_DB); // The database exists
71 | compare(query, done);
72 | });
73 |
74 | it('dbList', function(done) { // After dbCreate test and before dbDrop test
75 | var query = r.dbList();
76 | compare(query, done, function(dbs) {
77 | var contain = false;
78 | for(var i=0; i - 1', function(done) {
137 | var query = r.db(TEST_DB).table(TEST_TABLE).orderBy('id', {index: 'id'}).typeOf();
138 | compare(query, done);
139 | });
140 |
141 | it('Datum selection - 2', function(done) {
142 | var query = r.db(TEST_DB).table(TEST_TABLE).limit(3).typeOf();
143 | compare(query, done);
144 | });
145 |
146 | it('Datum selection - 3', function(done) {
147 | var query = r.db(TEST_DB).table(TEST_TABLE).getAll(1, 2, 3, {index: 'id'}).typeOf();
148 | compare(query, done);
149 | });
150 |
151 | /*
152 | */
153 | });
154 |
--------------------------------------------------------------------------------
/test/extra.js:
--------------------------------------------------------------------------------
1 | var config = require('./../config.js');
2 |
3 | // Use rethinkdbdash as rethinkdb doesn't properly reject errors
4 | // See https://github.com/rethinkdb/rethinkdb/issues/5916
5 | var r = require('rethinkdbdash')({pool: false});
6 |
7 | var assert = require('assert');
8 |
9 | var connections = {};
10 | var TEST_DB = 'reqlitetest';
11 | var TEST_TABLE = 'reqlitetestselectingdata';
12 | var MISSING_ID = 'nonExistingId';
13 | var MISSING_FIELD = 'nonExistingField';
14 | var MISSING_INDEX = 'nonExistingIndex';
15 | var COMPLEX_OBJECT = {
16 | id: 1,
17 | foo: 2,
18 | buzz: {
19 | hello: [10, 11, 12],
20 | world: {
21 | nested: 100,
22 | field: 200
23 | },
24 | bonjour: [
25 | {monde: 1000, ciao: 4000},
26 | {monde: 2000, ciao: 5000},
27 | {monde: 3000, ciao: 6000},
28 | ]
29 | },
30 | };
31 |
32 | var compare = require('./util.js').generateCompare(connections);
33 | describe('connect', function(){
34 | it('should work', function(done) {
35 | r.connect(config.reqlite).then(function(connection) {
36 | return r.expr(1).run(connection);
37 | }).then(function(result) {
38 | assert.equal(1, result);
39 | done();
40 | }).catch(done);
41 | });
42 | });
43 |
44 | describe('new.js', function(){
45 | before(function(done) {
46 | setTimeout(function() { // Delay for nodemon to restart the server
47 | r.connect(config.rethinkdb).bind({}).then(function(conn) {
48 | connections.rethinkdb = conn;
49 | return r.connect(config.reqlite);
50 | }).then(function(conn) {
51 | connections.reqlite = conn;
52 | this.query = r.dbCreate(TEST_DB);
53 | return this.query.run(connections.rethinkdb);
54 | }).catch(function() { // ignore errors
55 | }).finally(function() {
56 | return this.query.run(connections.reqlite);
57 | }).catch(function() { // ignore errors
58 | }).finally(function() {
59 | this.query = r.db(TEST_DB).tableDrop(TEST_TABLE);
60 | return this.query.run(connections.rethinkdb);
61 | }).catch(function() { // ignore errors
62 | }).finally(function() {
63 | return this.query.run(connections.reqlite);
64 | }).catch(function() { // ignore errors
65 | }).finally(function() {
66 | this.query = r.db(TEST_DB).tableCreate(TEST_TABLE);
67 | return this.query.run(connections.rethinkdb);
68 | }).catch(function() { // ignore errors
69 | }).finally(function() {
70 | return this.query.run(connections.reqlite);
71 | }).catch(function() { // ignore errors
72 | }).finally(function() {
73 | this.query = r.db(TEST_DB).table(TEST_TABLE).insert([
74 | {id: 1, foo: 10, bar: [100, 101, 102], optional: 1},
75 | {id: 2, foo: 20, bar: [200, 201, 202], optional: 2},
76 | {id: 3, foo: 30, bar: [300, 301, 302], optional: 2},
77 | {id: 4, foo: 40, bar: [400, 401, 402]},
78 | ]);
79 | return this.query.run(connections.rethinkdb);
80 | }).catch(function() { // ignore errors
81 | }).finally(function() {
82 | return this.query.run(connections.reqlite);
83 | }).catch(function() { // ignore errors
84 | }).finally(function() {
85 | this.query = r.db(TEST_DB).table(TEST_TABLE).indexCreate('foo');
86 | return this.query.run(connections.rethinkdb);
87 | }).catch(function() { // ignore errors
88 | }).finally(function() {
89 | return this.query.run(connections.reqlite);
90 | }).catch(function() { // ignore errors
91 | }).finally(function() {
92 | this.query = r.db(TEST_DB).table(TEST_TABLE).indexCreate('bar');
93 | return this.query.run(connections.rethinkdb);
94 | }).catch(function() { // ignore errors
95 | }).finally(function() {
96 | return this.query.run(connections.reqlite);
97 | }).catch(function() { // ignore errors
98 | }).finally(function() {
99 | this.query = r.db(TEST_DB).table(TEST_TABLE).indexCreate('barmulti', r.row('bar'), {multi: true});
100 | return this.query.run(connections.rethinkdb);
101 | }).catch(function() { // ignore errors
102 | }).finally(function() {
103 | return this.query.run(connections.reqlite);
104 | }).catch(function() { // ignore errors
105 | }).finally(function() {
106 | this.query = r.db(TEST_DB).table(TEST_TABLE).indexWait();
107 | return this.query.run(connections.rethinkdb);
108 | }).catch(function(e) { // ignore errors
109 | }).finally(function() {
110 | return this.query.run(connections.reqlite);
111 | }).catch(function() { // ignore errors
112 | }).finally(function() {
113 | //TODO add and test a geo index
114 | done();
115 | });
116 | }, 300);
117 | });
118 |
119 |
120 | it('noreplyWait - 1', function(done) {
121 | connections.rethinkdb.noreplyWait().bind({}).then(function(result) {
122 | this.rethinkdbResult = result;
123 | return connections.reqlite.noreplyWait();
124 | }).then(function(result) {
125 | assert.deepEqual(this.rethinkdbResult, result);
126 | done();
127 | }).catch(done);
128 | });
129 |
130 | it('noreplyWait - 1', function(done) {
131 | connections.reqlite.server().then(function(result) {
132 | assert(result.name);
133 | assert(result.id);
134 | done();
135 | }).catch(done);
136 | });
137 |
138 |
139 | /*
140 | */
141 | });
142 |
143 |
--------------------------------------------------------------------------------
/lib/protodef.js:
--------------------------------------------------------------------------------
1 | // DO NOT EDIT
2 | // Autogenerated by convert_protofile
3 |
4 | module.exports = {
5 | VersionDummy: {
6 | Version: {
7 | V0_1: 1063369270,
8 | V0_2: 1915781601,
9 | V0_3: 1601562686,
10 | V0_4: 1074539808,
11 | V1_0: 885177795
12 | },
13 |
14 | Protocol: {
15 | PROTOBUF: 656407617,
16 | JSON: 2120839367
17 | }
18 | },
19 |
20 | Query: {
21 | QueryType: {
22 | START: 1,
23 | CONTINUE: 2,
24 | STOP: 3,
25 | NOREPLY_WAIT: 4,
26 | SERVER_INFO: 5
27 | },
28 |
29 | AssocPair: {}
30 | },
31 |
32 | Frame: {
33 | FrameType: {
34 | POS: 1,
35 | OPT: 2
36 | }
37 | },
38 |
39 | Backtrace: {},
40 |
41 | Response: {
42 | ResponseType: {
43 | SUCCESS_ATOM: 1,
44 | SUCCESS_SEQUENCE: 2,
45 | SUCCESS_PARTIAL: 3,
46 | WAIT_COMPLETE: 4,
47 | SERVER_INFO: 5,
48 | CLIENT_ERROR: 16,
49 | COMPILE_ERROR: 17,
50 | RUNTIME_ERROR: 18
51 | },
52 |
53 | ErrorType: {
54 | INTERNAL: 1000000,
55 | RESOURCE_LIMIT: 2000000,
56 | QUERY_LOGIC: 3000000,
57 | NON_EXISTENCE: 3100000,
58 | OP_FAILED: 4100000,
59 | OP_INDETERMINATE: 4200000,
60 | USER: 5000000,
61 | PERMISSION_ERROR: 6000000
62 | },
63 |
64 | ResponseNote: {
65 | SEQUENCE_FEED: 1,
66 | ATOM_FEED: 2,
67 | ORDER_BY_LIMIT_FEED: 3,
68 | UNIONED_FEED: 4,
69 | INCLUDES_STATES: 5
70 | }
71 | },
72 |
73 | Datum: {
74 | DatumType: {
75 | R_NULL: 1,
76 | R_BOOL: 2,
77 | R_NUM: 3,
78 | R_STR: 4,
79 | R_ARRAY: 5,
80 | R_OBJECT: 6,
81 | R_JSON: 7
82 | },
83 |
84 | AssocPair: {}
85 | },
86 |
87 | Term: {
88 | TermType: {
89 | DATUM: 1,
90 | MAKE_ARRAY: 2,
91 | MAKE_OBJ: 3,
92 | VAR: 10,
93 | JAVASCRIPT: 11,
94 | UUID: 169,
95 | HTTP: 153,
96 | ERROR: 12,
97 | IMPLICIT_VAR: 13,
98 | DB: 14,
99 | TABLE: 15,
100 | GET: 16,
101 | GET_ALL: 78,
102 | EQ: 17,
103 | NE: 18,
104 | LT: 19,
105 | LE: 20,
106 | GT: 21,
107 | GE: 22,
108 | NOT: 23,
109 | ADD: 24,
110 | SUB: 25,
111 | MUL: 26,
112 | DIV: 27,
113 | MOD: 28,
114 | FLOOR: 183,
115 | CEIL: 184,
116 | ROUND: 185,
117 | APPEND: 29,
118 | PREPEND: 80,
119 | DIFFERENCE: 95,
120 | SET_INSERT: 88,
121 | SET_INTERSECTION: 89,
122 | SET_UNION: 90,
123 | SET_DIFFERENCE: 91,
124 | SLICE: 30,
125 | SKIP: 70,
126 | LIMIT: 71,
127 | OFFSETS_OF: 87,
128 | CONTAINS: 93,
129 | GET_FIELD: 31,
130 | KEYS: 94,
131 | VALUES: 186,
132 | OBJECT: 143,
133 | HAS_FIELDS: 32,
134 | WITH_FIELDS: 96,
135 | PLUCK: 33,
136 | WITHOUT: 34,
137 | MERGE: 35,
138 | BETWEEN_DEPRECATED: 36,
139 | BETWEEN: 182,
140 | REDUCE: 37,
141 | MAP: 38,
142 | FOLD: 187,
143 | FILTER: 39,
144 | CONCAT_MAP: 40,
145 | ORDER_BY: 41,
146 | DISTINCT: 42,
147 | COUNT: 43,
148 | IS_EMPTY: 86,
149 | UNION: 44,
150 | NTH: 45,
151 | BRACKET: 170,
152 | INNER_JOIN: 48,
153 | OUTER_JOIN: 49,
154 | EQ_JOIN: 50,
155 | ZIP: 72,
156 | RANGE: 173,
157 | INSERT_AT: 82,
158 | DELETE_AT: 83,
159 | CHANGE_AT: 84,
160 | SPLICE_AT: 85,
161 | COERCE_TO: 51,
162 | TYPE_OF: 52,
163 | UPDATE: 53,
164 | DELETE: 54,
165 | REPLACE: 55,
166 | INSERT: 56,
167 | DB_CREATE: 57,
168 | DB_DROP: 58,
169 | DB_LIST: 59,
170 | TABLE_CREATE: 60,
171 | TABLE_DROP: 61,
172 | TABLE_LIST: 62,
173 | CONFIG: 174,
174 | STATUS: 175,
175 | WAIT: 177,
176 | RECONFIGURE: 176,
177 | REBALANCE: 179,
178 | SYNC: 138,
179 | GRANT: 188,
180 | INDEX_CREATE: 75,
181 | INDEX_DROP: 76,
182 | INDEX_LIST: 77,
183 | INDEX_STATUS: 139,
184 | INDEX_WAIT: 140,
185 | INDEX_RENAME: 156,
186 | FUNCALL: 64,
187 | BRANCH: 65,
188 | OR: 66,
189 | AND: 67,
190 | FOR_EACH: 68,
191 | FUNC: 69,
192 | ASC: 73,
193 | DESC: 74,
194 | INFO: 79,
195 | MATCH: 97,
196 | UPCASE: 141,
197 | DOWNCASE: 142,
198 | SAMPLE: 81,
199 | DEFAULT: 92,
200 | JSON: 98,
201 | TO_JSON_STRING: 172,
202 | ISO8601: 99,
203 | TO_ISO8601: 100,
204 | EPOCH_TIME: 101,
205 | TO_EPOCH_TIME: 102,
206 | NOW: 103,
207 | IN_TIMEZONE: 104,
208 | DURING: 105,
209 | DATE: 106,
210 | TIME_OF_DAY: 126,
211 | TIMEZONE: 127,
212 | YEAR: 128,
213 | MONTH: 129,
214 | DAY: 130,
215 | DAY_OF_WEEK: 131,
216 | DAY_OF_YEAR: 132,
217 | HOURS: 133,
218 | MINUTES: 134,
219 | SECONDS: 135,
220 | TIME: 136,
221 | MONDAY: 107,
222 | TUESDAY: 108,
223 | WEDNESDAY: 109,
224 | THURSDAY: 110,
225 | FRIDAY: 111,
226 | SATURDAY: 112,
227 | SUNDAY: 113,
228 | JANUARY: 114,
229 | FEBRUARY: 115,
230 | MARCH: 116,
231 | APRIL: 117,
232 | MAY: 118,
233 | JUNE: 119,
234 | JULY: 120,
235 | AUGUST: 121,
236 | SEPTEMBER: 122,
237 | OCTOBER: 123,
238 | NOVEMBER: 124,
239 | DECEMBER: 125,
240 | LITERAL: 137,
241 | GROUP: 144,
242 | SUM: 145,
243 | AVG: 146,
244 | MIN: 147,
245 | MAX: 148,
246 | SPLIT: 149,
247 | UNGROUP: 150,
248 | RANDOM: 151,
249 | CHANGES: 152,
250 | ARGS: 154,
251 | BINARY: 155,
252 | GEOJSON: 157,
253 | TO_GEOJSON: 158,
254 | POINT: 159,
255 | LINE: 160,
256 | POLYGON: 161,
257 | DISTANCE: 162,
258 | INTERSECTS: 163,
259 | INCLUDES: 164,
260 | CIRCLE: 165,
261 | GET_INTERSECTING: 166,
262 | FILL: 167,
263 | GET_NEAREST: 168,
264 | POLYGON_SUB: 171,
265 | MINVAL: 180,
266 | MAXVAL: 181
267 | },
268 |
269 | AssocPair: {}
270 | }
271 | }
272 |
--------------------------------------------------------------------------------
/test/string-manipulation.js:
--------------------------------------------------------------------------------
1 | var config = require('./../config.js');
2 |
3 | var r = require('rethinkdb');
4 | var assert = require('assert');
5 |
6 | var connections = {};
7 | var TEST_DB = 'reqlitetest';
8 | var TEST_TABLE = 'reqliteteststringmanipulation';
9 | var MISSING_ID = 'nonExistingId';
10 | var MISSING_FIELD = 'nonExistingField';
11 | var MISSING_INDEX = 'nonExistingIndex';
12 | var COMPLEX_OBJECT = {
13 | id: 1,
14 | foo: 2,
15 | buzz: {
16 | hello: [10, 11, 12],
17 | world: {
18 | nested: 100,
19 | field: 200
20 | },
21 | bonjour: [
22 | {monde: 1000, ciao: 4000},
23 | {monde: 2000, ciao: 5000},
24 | {monde: 3000, ciao: 6000},
25 | ]
26 | },
27 | };
28 |
29 | var compare = require('./util.js').generateCompare(connections);
30 |
31 | describe('string-manipulation.js', function(){
32 | before(function(done) {
33 | setTimeout(function() { // Delay for nodemon to restart the server
34 | r.connect(config.rethinkdb).bind({}).then(function(conn) {
35 | connections.rethinkdb = conn;
36 | return r.connect(config.reqlite);
37 | }).then(function(conn) {
38 | connections.reqlite = conn;
39 | done();
40 | });
41 | }, 500);
42 | });
43 |
44 | it('match - 1', function(done) {
45 | var query = r.expr('foo').match('foo');
46 | compare(query, done);
47 | });
48 |
49 | it('match - 2', function(done) {
50 | var query = r.expr('foo').match('bar');
51 | compare(query, done);
52 | });
53 |
54 | it('match - 3', function(done) {
55 | var query = r.expr('afoo').match('foo');
56 | compare(query, done);
57 | });
58 |
59 | it('match - 4', function(done) {
60 | var query = r.expr('afoobbb').match('foo');
61 | compare(query, done);
62 | });
63 |
64 | it('match - 5', function(done) {
65 | var query = r.expr('afoobbb').match('^afoo');
66 | compare(query, done);
67 | });
68 |
69 | it('match - 6', function(done) {
70 | var query = r.expr('afoobbb').match('^b');
71 | compare(query, done);
72 | });
73 |
74 | it('match - 7', function(done) {
75 | var query = r.expr('John').match('(?i)john');
76 | compare(query, done);
77 | });
78 |
79 | it('match - 8', function(done) {
80 | var query = r.expr('JOHN').match('(?i)john');
81 | compare(query, done);
82 | });
83 |
84 | it('match - 9', function(done) {
85 | var query = r.expr('JOHN').match('john');
86 | compare(query, done);
87 | });
88 |
89 | it('match - 10', function(done) {
90 | var query = r.expr(1).match('john');
91 | compare(query, done);
92 | });
93 |
94 | it('match - 11', function(done) {
95 | var query = r.expr('foo').match(1);
96 | compare(query, done);
97 | });
98 |
99 | it('match - 12', function(done) {
100 | var query = r.expr('aaafoo').match('a+foo');
101 | compare(query, done);
102 | });
103 |
104 | it('match - 13', function(done) {
105 | var query = r.expr('aaa\nfoo').match('a+\n?foo');
106 | compare(query, done);
107 | });
108 |
109 | it('match - 14', function(done) {
110 | var query = r.expr('aaafoo').match('a+\n?foo');
111 | compare(query, done);
112 | });
113 |
114 | it('match - 15', function(done) {
115 | var query = r.expr('aaafoo').match('a+\n?foo', 'bar');
116 | compare(query, done);
117 | });
118 |
119 | it('split - 1', function(done) {
120 | var query = r.expr("foo bar bax").split();
121 | compare(query, done);
122 | });
123 |
124 | it('split - 2', function(done) {
125 | var query = r.expr("12,37,,22,").split(',');
126 | compare(query, done);
127 | });
128 |
129 | it('split - 3', function(done) {
130 | var query = r.expr("mlucy").split('');
131 | compare(query, done);
132 | });
133 |
134 | it('split - 4', function(done) {
135 | var query = r.expr("12,37,,22,").split(",", 3);
136 | compare(query, done);
137 | });
138 |
139 | it('split - 5', function(done) {
140 | var query = r.expr("foo bar bax").split(null, 1);
141 | compare(query, done);
142 | });
143 |
144 | it('split - 6', function(done) {
145 | var query = r.expr("foo bar bax").split(null, 1);
146 | compare(query, done);
147 | });
148 |
149 | it('split - 7', function(done) {
150 | var query = r.expr("foo bar bax").split('foo', 'bar', 'buzz');
151 | compare(query, done);
152 | });
153 |
154 | it('split - 8', function(done) {
155 | var query = r.expr(2).split(null, 1);
156 | compare(query, done);
157 | });
158 |
159 | it('split - 9', function(done) {
160 | var query = r.expr('foo bar').split(2, 1);
161 | compare(query, done);
162 | });
163 |
164 | it('split - 10', function(done) {
165 | var query = r.expr('foo bar').split('foo', 'bar');
166 | compare(query, done);
167 | });
168 |
169 | it('split - 11', function(done) {
170 | var query = r.expr('foo bar buzz').split().count();
171 | compare(query, done);
172 | });
173 |
174 | it('upcase - 1', function(done) {
175 | var query = r.expr("Sentence about LaTeX.").upcase();
176 | compare(query, done);
177 | });
178 |
179 | it('upcase - 2', function(done) {
180 | var query = r.expr("Sentence about LaTeX.").upcase('bar');
181 | compare(query, done);
182 | });
183 |
184 | it('upcase - 3', function(done) {
185 | var query = r.expr(2).upcase('bar');
186 | compare(query, done);
187 | });
188 |
189 | it('upcase - 3', function(done) {
190 | var query = r.expr("C'est l'été.").upcase();
191 | compare(query, done);
192 | });
193 |
194 | it('downcase - 1', function(done) {
195 | var query = r.expr("Sentence about LaTeX.").downcase();
196 | compare(query, done);
197 | });
198 |
199 | it('downcase - 2', function(done) {
200 | var query = r.expr("Sentence about LaTeX.").downcase('bar');
201 | compare(query, done);
202 | });
203 |
204 | it('downcase - 3', function(done) {
205 | var query = r.expr(3).downcase('bar');
206 | compare(query, done);
207 | });
208 |
209 | it('downcase - 4', function(done) {
210 | var query = r.expr("C'ÉTÉ ouhYou").downcase();
211 | compare(query, done);
212 | });
213 |
214 | /*
215 | */
216 |
217 | });
218 |
--------------------------------------------------------------------------------
/lib/group.js:
--------------------------------------------------------------------------------
1 | var Promise = require("bluebird");
2 | var Sequence = require(__dirname+"/sequence.js");
3 |
4 | function Group(groups) {
5 | this.groups = groups || new Sequence(); // {group : , reduction: }
6 | // this.type?
7 | }
8 | module.exports = Group;
9 |
10 | var util = require(__dirname+"/utils.js");
11 |
12 | // Import methods from Sequence
13 | var keys = Object.keys(Sequence.prototype);
14 | for(var i=0; i 0)
150 | && (this.groups.get(0).reduction instanceof Sequence)) {
151 | return "GROUPED_STREAM";
152 | }
153 | else {
154 | return "GROUPED_DATA";
155 | }
156 | };
157 |
158 | Group.prototype.toDatum = function() {
159 | var result = {
160 | "$reql_type$": "GROUPED_DATA",
161 | "data": []
162 | };
163 | for(var i=0; i 234
35 | var millisecondsStr = ''+milliseconds;
36 | while (millisecondsStr.length < 5) {
37 | millisecondsStr += '0';
38 | }
39 | millisecondsStr = millisecondsStr.slice(0, 3);
40 | var dateStr = ''+year+'-'+month+'-'+day+' '+hours+':'+minutes+':'+seconds+'.'+millisecondsStr+' '+timezone;
41 |
42 | var date = moment(dateStr, 'YYYY-M-D H:m:s.SSS ZZ');
43 | return new ReqlDate(date.unix()+date.milliseconds()/1000, ReqlDate.convertTimezone(timezone, query));
44 | };
45 |
46 | ReqlDate.fromMoment = function(date, timezone, query) {
47 | return new ReqlDate(date.unix()+date.milliseconds()/1000, ReqlDate.convertTimezone(timezone, query));
48 | };
49 |
50 | ReqlDate.iso8601 = function(date, timezone, query) {
51 | var dateObject = new Date(date);
52 | if (isNaN(dateObject.getTime())) {
53 | query.frames.push(0);
54 | var match = [
55 | 'digit', // year
56 | 'digit',
57 | 'digit',
58 | 'digit',
59 | 'dash',
60 | 'digit', // month
61 | 'digit',
62 | 'dash',
63 | 'digit', // day
64 | 'digit',
65 | 'T',
66 | 'digit', // hours
67 | 'digit',
68 | 'colon',
69 | 'digit', // minutes
70 | 'digit',
71 | 'colon',
72 | 'digit', // seconds
73 | 'digit',
74 | '+-',
75 | 'digit', // hours timezone
76 | 'digit',
77 | 'colon',
78 | 'digit', // minutes timezone
79 | 'digit',
80 | ];
81 | //TODO Fix error handling. RethinkDB looks for T etc.
82 | for(var i=0; i 0)) {
128 | return regexResult[0];
129 | }
130 | else if (options.default_timezone !== undefined) {
131 | return options.default_timezone;
132 | }
133 | else {
134 | throw new Error.ReqlRuntimeError("ISO 8601 string has no time zone, and no default time zone was provided", frames);
135 | }
136 | }
137 | };
138 |
139 | ReqlDate.prototype.inTimezone = function(timezone) {
140 | return new ReqlDate(this.epoch_time, timezone);
141 | };
142 |
143 | ReqlDate.prototype.toMoment = function() {
144 | return moment(this.epoch_time, 'X').utcOffset(this.timezone);
145 | };
146 |
147 |
148 | ReqlDate.convertTimezone = function(timezone, query) {
149 | //TODO Mimic better errors
150 | if (timezone === "Z") {
151 | return "+00:00";
152 | }
153 | //TODO Safeguard against null
154 | else if (timezone.indexOf(":") === -1) { // TODO Refactor these errors...
155 | throw new Error.ReqlRuntimeError('Garbage characters `'+timezone[timezone.length-1]+'` at end of timezone string `'+timezone+'`', query.frames);
156 | }
157 | else if ((timezone[0] !== '-') && (timezone[0] !== '+')) {
158 | throw new Error.ReqlRuntimeError('Timezone `'+timezone+'` does not start with `-` or `+`', query.frames);
159 | }
160 | else if (!/^\d$/.test(timezone[1])) {
161 | throw new Error.ReqlRuntimeError('Invalid date string `'+timezone+'` (got `'+timezone[1]+'` but expected a digit)', query.frames);
162 | }
163 | else if (!/^\d$/.test(timezone[2])) {
164 | throw new Error.ReqlRuntimeError('Invalid date string `'+timezone+'` (got `'+timezone[2]+'` but expected a digit)', query.frames);
165 | }
166 | else if (timezone.length < 6) {
167 | //throw new Error.ReqlRuntimeError('Truncated date string `'+timezone+'`', query.frames);
168 | throw new Error.ReqlRuntimeError('Invalid time zone string `'+timezone+'`. Valid time zones are `+hh:mm`, `-hh:mm`, `+hhmm`, `-hhmm`, `+hh`, `-hh` and `Z`', query.frames);
169 | }
170 | else if (!/^\d$/.test(timezone[4])) {
171 | throw new Error.ReqlRuntimeError('Invalid date string `'+timezone+'` (got `'+timezone[4]+'` but expected a digit)', query.frames);
172 | }
173 | else if (!/^\d$/.test(timezone[5])) {
174 | throw new Error.ReqlRuntimeError('Invalid date string `'+timezone+'` (got `'+timezone[5]+'` but expected a digit)', query.frames);
175 | }
176 | else if (timezone.length > 6) {
177 | throw new Error.ReqlRuntimeError('Garbage characters `'+timezone.slice(6)+'` at end of timezone string `'+timezone+'`', query.frames);
178 | }
179 |
180 | return timezone;
181 |
182 | };
183 |
184 |
--------------------------------------------------------------------------------
/lib/connection.js:
--------------------------------------------------------------------------------
1 | var crypto = require('crypto');
2 | var KEY_LENGTH = 32; // Because we are currently using SHA 256
3 | var NULL_BUFFER = new Buffer('\0', 'binary');
4 |
5 | function Connection(connection, server, options) {
6 | var self = this;
7 |
8 | self.id = server.getNextId();
9 | self.connection = connection;
10 | self.options = options;
11 | self.server = server;
12 |
13 | self.user = undefined;
14 | self.password = undefined;
15 | self.salt = new Buffer(crypto.randomBytes(16)).toString('base64')+'=='
16 | self.serverNonce = new Buffer(crypto.randomBytes(17)).toString('base64')
17 | self.clientNonce = undefined;
18 | self.iterations = 4096;
19 |
20 | self.open = false;
21 | self.buffer = new Buffer(0);
22 | self.state = 0;
23 | self.protocol = undefined;
24 | self.ignoreIncoming = false; // Used to handle asynchronous operation in the handshake.
25 | self.queries = {}; // token -> Query
26 |
27 | self.connection.on('connect', function() {
28 | self.open = true;
29 | });
30 | self.connection.on('data', function(data) {
31 | self.buffer = Buffer.concat([self.buffer, data]);
32 | self.read();
33 | });
34 | self.connection.on("end", function() {
35 | self.server.removeConnection(self.id);
36 | });
37 | self.connection.on("error", function(error) {
38 | util.log(error);
39 | });
40 | }
41 |
42 | module.exports = Connection;
43 |
44 | var protodef = require(__dirname+"/protodef.js");
45 | var Query = require(__dirname+"/query.js");
46 | var util = require(__dirname+"/utils.js");
47 | var Query = require(__dirname+"/query.js");
48 | var queryTypes = protodef.Query.QueryType;
49 |
50 | Connection.prototype.read = function() {
51 | var self = this;
52 | if (self.ignoreIncoming === true) {
53 | return;
54 | }
55 | if (self.state === 0) {
56 | if (self.buffer.length >= 4) {
57 | var version = self.buffer.readUInt32LE(0);
58 | self.buffer = self.buffer.slice(4);
59 |
60 | if (version !== self.options.version) {
61 | //TODO Send appropriate error
62 | self.connection.end();
63 | }
64 | else {
65 | self.protocolVersion = version;
66 | self.connection.write(Buffer.concat([new Buffer(JSON.stringify({
67 | success: true,
68 | min_protocol_version: self.options.minProtocolVersion,
69 | max_protocol_version: self.options.maxProtocolVersion
70 | })), NULL_BUFFER]));
71 | }
72 | self.state++;
73 | self.read();
74 | }
75 | // else, we need more data
76 | }
77 | else if (self.state === 1) {
78 | for(var i=0; i, 'r': }
85 | var authentication = util.splitCommaEqual(self.auth.authentication);
86 | self.user = authentication.n;
87 | self.password = ''; // TODO: Pull password from the database.
88 | self.clientNonce = authentication.r;
89 | self.connection.write(Buffer.concat([new Buffer(JSON.stringify({
90 | success: true,
91 | authentication: self.getAuthentication()
92 | })), NULL_BUFFER]));
93 | self.state++;
94 | self.read();
95 | } catch(error) {
96 | // TODO Send appropriate error
97 | self.connection.end();
98 | }
99 | }
100 | }
101 | }
102 | else if (self.state === 2) {
103 | for(var i=0; i= 8+4) { // 8 for the token, 4 for the query's length
141 | var token = self.buffer.readUInt32LE(0);
142 | var queryLength = self.buffer.readUInt32LE(8);
143 |
144 | // TODO Similate async?
145 | if (self.buffer.length >= queryLength+8+4) {
146 | (function(token) {
147 | self.buffer = self.buffer.slice(8+4);
148 | var queryStr = self.buffer.slice(0, queryLength).toString();
149 | self.buffer = self.buffer.slice(queryLength);
150 | self.handleQuery(token, queryStr);
151 |
152 | })(token);
153 | }
154 | else {
155 | break;
156 | }
157 |
158 | }
159 | }
160 | };
161 |
162 | Connection.prototype.handleQuery = function(token, queryStr) {
163 | var self = this;
164 | try {
165 | //TODO Split things for better error handling
166 | var queryAST = JSON.parse(queryStr);
167 | var queryType = queryAST[0];
168 | util.log('=== Query ===========================================', token);
169 | util.log(JSON.stringify(queryAST));
170 |
171 |
172 | var queryResult;
173 | if (queryType === queryTypes.START) {
174 | //TODO Pass options
175 | var query = new Query(self.server, queryAST, {}, token);
176 | queryResult = query.run();
177 | Promise.resolve(queryResult).then(function(response) {
178 | if (query.isComplete() === false) {
179 | self.queries[token] = query;
180 | }
181 | self.send(token, response);
182 | util.log('=== Response ===========================================', token);
183 | util.log(JSON.stringify(response, null, 2));
184 | if (response.debug != null) {
185 | util.log(response.debug);
186 | }
187 | }).catch(function(error) {
188 | util.log('queryResult - response', error);
189 | });
190 |
191 | }
192 | else if (queryType === queryTypes.CONTINUE) {
193 | if (self.queries[token] === undefined) {
194 | throw new Error("TOKEN NOT FOUND");
195 | //TODO throw
196 | }
197 |
198 | response = self.queries[token].continue();
199 | response.then(function(finalResult) {
200 | util.log('=== Delayed response ===========================================', token);
201 | util.log(finalResult);
202 | util.log(self.queries[token].token);
203 | self.send(token, finalResult);
204 | if ((self.queries[token] != null)
205 | && (self.queries[token].isComplete() === true)) {
206 | delete self.queries[token];
207 | }
208 | }).catch(function(error) {
209 | throw error;
210 | });
211 | }
212 | else if (queryType === queryTypes.STOP) {
213 | //TODO Clean listeners
214 | if (self.queries[token] === undefined) {
215 | throw new Error("TOKEN NOT FOUND");
216 | }
217 |
218 | self.queries[token].stop();
219 | for(var i=0; i0) && (intersects === true)) {
146 | return false;
147 | }
148 | }
149 | return true;
150 | };
151 |
152 | ReqlGeometry.prototype.toGeolib = function(query) {
153 | if (this.type === 'Point') {
154 | return {
155 | longitude: this.coordinates[0],
156 | latitude: this.coordinates[1]
157 | };
158 | }
159 | else if (this.type === 'Polygon') {
160 | var result = [];
161 | for(var i=0; i 2) {
200 | throw new Error.ReqlRuntimeError('A third altitude coordinate in GeoJSON positions was found, but is not supported', query.frames);
201 | }
202 | util.assertType(coordinates.get(i).get(0), "NUMBER", this);
203 | util.assertType(coordinates.get(i).get(1), "NUMBER", this);
204 | }
205 | return new ReqlGeometry(geojson.type, geojson.coordinates.toDatum(), query);
206 | }
207 | else if (geojson.type === 'Polygon') {
208 | util.assertType(geojson.coordinates, "ARRAY", query);
209 | coordinates = geojson.coordinates;
210 | // RethinkDB accepts an empty array here...
211 | if (util.isSequence(coordinates) && (coordinates.length > 0) && (coordinates.get(0).length < 4)) {
212 | throw new Error.ReqlRuntimeError('GeoJSON LinearRing must have at least four positions', query.frames);
213 | }
214 |
215 | for(var i=0; i 2) {
223 | throw new Error.ReqlRuntimeError('A third altitude coordinate in GeoJSON positions was found, but is not supported', query.frames);
224 | }
225 |
226 | util.assertType(coordinates.get(i).get(j).get(0), "NUMBER", query);
227 | util.assertType(coordinates.get(i).get(j).get(1), "NUMBER", query);
228 | }
229 | }
230 |
231 | if (util.isSequence(coordinates) && (coordinates.length > 0)
232 | && !util.eq(coordinates.get(0).get(0), coordinates.get(0).get(coordinates.get(0).length-1))) {
233 | throw new Error.ReqlRuntimeError('First and last vertex of GeoJSON LinearRing must be identical', query.frames);
234 | }
235 | return new ReqlGeometry(geojson.type, coordinates, query);
236 | }
237 | else if ((geojson.type === 'MultiPoint')
238 | || (geojson.type === 'MultiLineString')
239 | || (geojson.type === 'MultiPolygon')) {
240 | throw new Error.ReqlRuntimeError('GeoJSON type `'+geojson.type+'` is not supported', query.frames);
241 | }
242 | else {
243 | throw new Error.ReqlRuntimeError('Unrecognized GeoJSON type `'+geojson.type+'`', query.frames);
244 | }
245 | };
246 |
247 | ReqlGeometry.factors = {
248 | m: 1,
249 | km: 1000,
250 | mi: 1609.344,
251 | nm: 1852,
252 | ft: 0.3048
253 | };
254 |
255 | ReqlGeometry.convert = function(distance, from, to) {
256 | //TODO assert valid units
257 | var factor = ReqlGeometry.factors[from]/ReqlGeometry.factors[to];
258 | return distance*factor;
259 | };
260 |
261 | ReqlGeometry.validateCoordinates = function(coordinates, query) {
262 | if ((coordinates[0] < -180) || (coordinates[0] > 180)) {
263 | throw new Error.ReqlRuntimeError('Longitude must be between -180 and 180. Got '+coordinates[0], query.frames);
264 | }
265 | if ((coordinates[1] < -90) || (coordinates[1] > 90)) {
266 | throw new Error.ReqlRuntimeError('Latitude must be between -90 and 90. Got '+coordinates[1], query.frames);
267 | }
268 | };
269 |
--------------------------------------------------------------------------------
/lib/document.js:
--------------------------------------------------------------------------------
1 | var _util = require('util');
2 | var EventEmitter = require('events').EventEmitter;
3 |
4 | function Document(doc, table) {
5 | this.doc = doc;
6 | this.table = table;
7 | }
8 | _util.inherits(Document, EventEmitter);
9 | module.exports = Document;
10 |
11 | var util = require(__dirname+"/utils.js");
12 | var Error = require(__dirname+"/error.js");
13 | var Sequence = require(__dirname+"/sequence.js");
14 |
15 | Document.prototype.typeOf = function() {
16 | return "SELECTION