├── .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 |
  1. Run `node browserify.js` in the reqlite repository
  2. 12 |
  3. Run `npm run browserify` in the rethinkdbdash repository
  4. 13 |
  5. Copy `rethinkdbdash.js` in browser/
  6. 14 |
  7. Run `python -m SimpleHTTPServer`
  8. 15 |
  9. Open http://localhost:8000
  10. 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"; 17 | }; 18 | 19 | Document.prototype.pluck = function(keys) { 20 | return util.pluck(this.doc, keys); 21 | }; 22 | Document.prototype.hasFields = function(keys) { 23 | return util.hasFields(this.doc, keys); 24 | }; 25 | 26 | //TODO Forbid nested writes 27 | // TODO Refactor with replace? 28 | Document.prototype.update = function(newValue, options, query, internalOptions) { 29 | var self = this; 30 | var primaryKey = self.table.options.primaryKey; 31 | 32 | return new Promise(function(resolve, reject) { 33 | // Update context 34 | if (options.non_atomic !== true) { 35 | internalOptions = {deterministic: true}; 36 | } 37 | 38 | if (util.isFunction(newValue)) { 39 | var varId = util.getVarId(newValue); 40 | query.context[varId] = self; 41 | query.evaluate(newValue, internalOptions).bind({}).then(function(updateValue) { 42 | util.assertType(updateValue, 'DATUM', query); 43 | delete query.context[varId]; 44 | resolve(Document.handleUpdateValue(self, updateValue, primaryKey, options)); 45 | }).catch(function(err) { 46 | if (err.message.match(/^Could not prove argument deterministic. Maybe you want to use the non_atomic flag?/)) { 47 | reject(err); 48 | } 49 | var result = util.writeResult(); 50 | result.errors++; 51 | if (err.message.match(/^Expected type DATUM but found/)) { 52 | result.first_error = err.message; 53 | } 54 | else { 55 | result.first_error = err.message+'.'; //WTF? 56 | } 57 | resolve(result); 58 | }); 59 | } 60 | else { 61 | query.evaluate(newValue, internalOptions).bind({}).then(function(updateValue) { 62 | if (typeof updateValue === 'function') { 63 | updateValue = updateValue(util.toDatum(self)); 64 | util.assertJavaScriptResult(updateValue, query); 65 | updateValue = util.revertDatum(updateValue); 66 | } 67 | util.assertType(updateValue, 'DATUM', self); 68 | resolve(Document.handleUpdateValue(self, updateValue, primaryKey, options)); 69 | }).catch(function(err) { 70 | if (err.message.match(/^Could not prove argument deterministic. Maybe you want to use the non_atomic flag?/)) { 71 | reject(err); 72 | } 73 | var result = util.writeResult(); 74 | result.errors++; 75 | result.first_error = err.message+'.'; 76 | resolve(result); 77 | }); 78 | } 79 | }); 80 | }; 81 | 82 | Document.handleUpdateValue = function(self, updateValue, primaryKey, options) { 83 | var result = util.writeResult(); 84 | if (updateValue === null) { 85 | updateValue = {}; 86 | } 87 | if ((updateValue[primaryKey] !== undefined) 88 | && (self.doc[primaryKey] !== updateValue[primaryKey])) { 89 | result.errors++; 90 | result.first_error = "Primary key `id` cannot be changed(`"+ 91 | JSON.stringify(util.toDatum(updateValue), null, 2)+"` -> `"+ 92 | JSON.stringify(self.doc, null, 2)+"`)"; 93 | if (options.return_changes === true) { 94 | result.changes = new Sequence(); 95 | } 96 | if (options.return_changes === 'always') { 97 | var change = { 98 | new_val: util.deepCopy(self.doc), 99 | old_val: util.deepCopy(self.doc), 100 | } 101 | if (result.first_error) { 102 | change.error = result.first_error; 103 | } 104 | result.changes = new Sequence([change]); 105 | } 106 | } 107 | else { 108 | var old_val = util.deepCopy(self.doc); 109 | if (updateValue instanceof Document) { 110 | updateValue = updateValue.doc; 111 | } 112 | var changed = util._merge(self.doc, updateValue); 113 | if (options.return_changes === 'always') { 114 | result.changes = new Sequence([{ 115 | new_val: util.deepCopy(self.doc), 116 | old_val: old_val 117 | }]); 118 | } 119 | if (changed === true) { 120 | result.replaced++; 121 | self.emit('change', {new_val: self.doc, old_val: old_val}); 122 | if (options.return_changes === true) { 123 | result.changes = new Sequence([{ 124 | new_val: util.deepCopy(self.doc), 125 | old_val: old_val 126 | }]); 127 | } 128 | } 129 | else { 130 | result.unchanged++; 131 | if (options.return_changes === true) { 132 | result.changes = new Sequence(); 133 | } 134 | if (options.return_changes === 'always') { 135 | result.changes = new Sequence([{ 136 | new_val: util.deepCopy(self.doc), 137 | old_val: util.deepCopy(self.doc), 138 | }]); 139 | } 140 | 141 | } 142 | } 143 | return result; 144 | }; 145 | 146 | Document.prototype.merge = function(obj, query) { 147 | return util.merge(this.doc, obj, query); 148 | }; 149 | 150 | Document.prototype.without = function(keys) { 151 | return util.without(this.doc, keys); 152 | }; 153 | 154 | Document.prototype.replace = function(newValue, options, query, internalOptions) { 155 | //TODO Throw if we are missing the primary key 156 | var self = this; 157 | var primaryKey = self.table.options.primaryKey; 158 | 159 | return new Promise(function(resolve, reject) { 160 | // Update context 161 | if (options.non_atomic !== true) { 162 | internalOptions = {deterministic: true}; 163 | } 164 | 165 | if (util.isFunction(newValue)) { 166 | var varId = util.getVarId(newValue); 167 | query.context[varId] = self; 168 | query.evaluate(newValue, internalOptions).bind({}).then(function(updateValue) { 169 | delete query.context[varId]; 170 | resolve(Document.handleReplaceValue(self, updateValue, primaryKey, options)); 171 | }).catch(function(err) { 172 | if (err.message.match(/^Could not prove argument deterministic. Maybe you want to use the non_atomic flag?/)) { 173 | reject(err); 174 | } 175 | var result = util.writeResult(); 176 | result.errors++; 177 | if (err.message.match(/^Expected type DATUM but found/)) { 178 | result.first_error = err.message; 179 | } 180 | else { 181 | result.first_error = err.message+'.'; //WTF? 182 | } 183 | resolve(result); 184 | }); 185 | } 186 | else { 187 | query.evaluate(newValue, internalOptions).bind({}).then(function(updateValue) { 188 | if (typeof updateValue === 'function') { 189 | updateValue = updateValue(util.toDatum(self)); 190 | util.assertJavaScriptResult(updateValue, query); 191 | updateValue = util.revertDatum(updateValue); 192 | } 193 | util.assertType(updateValue, 'DATUM', self); 194 | resolve(Document.handleReplaceValue(self, updateValue, primaryKey, options)); 195 | }).catch(function(err) { 196 | if (err.message.match(/^Could not prove argument deterministic. Maybe you want to use the non_atomic flag?/)) { 197 | reject(err); 198 | } 199 | var result = util.writeResult(); 200 | result.errors++; 201 | result.first_error = err.message+'.'; 202 | resolve(result); 203 | }); 204 | } 205 | }); 206 | }; 207 | 208 | Document.handleReplaceValue = function(self, replaceValue, primaryKey, options) { 209 | var result = util.writeResult(); 210 | if (replaceValue === null) { 211 | self.delete({}); 212 | result.deleted++; 213 | } 214 | else if ((replaceValue[primaryKey] !== undefined) 215 | && (self.doc[primaryKey] !== replaceValue[primaryKey])) { 216 | result.errors++; 217 | result.first_error = "Primary key `id` cannot be changed(`"+ 218 | JSON.stringify(util.toDatum(replaceValue), null, 2)+"` -> `"+ 219 | JSON.stringify(self.doc, null, 2)+"`)"; 220 | if (options.return_changes === true) { 221 | result.changes = new Sequence(); 222 | } 223 | if (options.return_changes === 'always') { 224 | var change = { 225 | new_val: util.deepCopy(self.doc), 226 | old_val: util.deepCopy(self.doc), 227 | } 228 | if (result.first_error) { 229 | change.error = result.first_error; 230 | } 231 | result.changes = new Sequence([change]); 232 | } 233 | } 234 | else { 235 | var old_val = util.deepCopy(self.doc); 236 | if (replaceValue instanceof Document) { 237 | replaceValue = replaceValue.doc; 238 | } 239 | var changed = util._replace(self.doc, replaceValue); 240 | if (options.return_changes === 'always') { 241 | result.changes = new Sequence([{ 242 | new_val: util.deepCopy(self.doc), 243 | old_val: old_val 244 | }]); 245 | } 246 | if (changed === true) { 247 | result.replaced++; 248 | self.emit('change', {new_val: self.doc, old_val: old_val}); 249 | if (options.return_changes === true) { 250 | result.changes = new Sequence([{ 251 | new_val: util.deepCopy(self.doc), 252 | old_val: old_val 253 | }]); 254 | } 255 | } 256 | else { 257 | result.unchanged++; 258 | if (options.return_changes === true) { 259 | result.changes = new Sequence(); 260 | } 261 | if (options.return_changes === 'always') { 262 | result.changes = new Sequence([{ 263 | new_val: util.deepCopy(self.doc), 264 | old_val: util.deepCopy(self.doc), 265 | }]); 266 | } 267 | } 268 | } 269 | return result; 270 | }; 271 | 272 | Document.prototype.delete = function(options, query) { 273 | return this.table._delete(this, options); 274 | 275 | }; 276 | 277 | Document.prototype.toDatum = function() { 278 | var result = {}; 279 | var keys = Object.keys(this.doc); 280 | for(var i=0; i b.index) { return 1; } 326 | else if (a.index < b.index) { return -1; } 327 | else { return 0; } 328 | }); 329 | return indexes; 330 | }); 331 | }); 332 | 333 | it('indexStatus - 3', function(done) { 334 | var query = r.expr('foo').indexStatus(TEST_INDEX); 335 | compare(query, done); 336 | }); 337 | 338 | it('indexStatus - 4', function(done) { 339 | var query = r.expr('foo').indexStatus(2); 340 | compare(query, done); 341 | }); 342 | 343 | it('indexStatus - 5', function(done) { 344 | var query = r.db(TEST_DB).table(TEST_TABLE).indexStatus('foo', 2); 345 | compare(query, done); 346 | }); 347 | 348 | it('indexWait - 1', function(done) { 349 | var query = r.db(TEST_DB).table(TEST_TABLE).indexWait(TEST_INDEX); 350 | compare(query, done, function(indexes) { 351 | for(var i=0; i b.index) { return 1; } 370 | else if (a.index < b.index) { return -1; } 371 | else { return 0; } 372 | }); 373 | return indexes; 374 | }); 375 | }); 376 | 377 | it('indexWait - 3', function(done) { 378 | var query = r.expr('foo').indexWait(TEST_INDEX); 379 | compare(query, done); 380 | }); 381 | 382 | it('indexWait - 4', function(done) { 383 | var query = r.expr('foo').indexWait(2); 384 | compare(query, done); 385 | }); 386 | 387 | it('indexWait - 5', function(done) { 388 | var query = r.db(TEST_DB).table(TEST_TABLE).indexWait('foo', 2); 389 | compare(query, done); 390 | }); 391 | 392 | it('tableDrop - 1', function(done) { 393 | var query = r.db(TEST_DB).tableDrop(TEST_TABLE); 394 | compare(query, done, function(doc) { 395 | delete doc.config_changes[0].old_val.id; 396 | delete doc.config_changes[0].old_val.shards; 397 | return doc; 398 | }); 399 | }); 400 | 401 | it('tableDrop - 2', function(done) { 402 | var query = r.db(TEST_DB).tableDrop(r.args(['foo', 'bar', 'buzz'])); 403 | compare(query, done); 404 | }); 405 | 406 | it('tableDrop - 3', function(done) { 407 | var query = r.db(TEST_DB).tableDrop(1); 408 | compare(query, done); 409 | }); 410 | 411 | it('tableDrop - 4', function(done) { 412 | var query = r.db(TEST_DB).tableDrop(r.args()); 413 | compare(query, done, function(error) { 414 | return /^Expected 1 argument but found 0/.test(error); 415 | }); 416 | }); 417 | 418 | it('tableDrop - 5', function(done) { 419 | var query = r.expr('foo').tableDrop('bar'); 420 | compare(query, done); 421 | }); 422 | 423 | it('tableDrop - 6 - pre', function(done) { 424 | var query = r.dbDrop(TEST_DB2); 425 | compare(query, done, function() { 426 | return true; // We just want to drop it 427 | }); 428 | }); 429 | it('tableDrop - 6', function(done) { 430 | var query = r.tableDrop(TEST_TABLE); 431 | compare(query, done); 432 | }); 433 | 434 | it('tableDrop - 7', function(done) { 435 | var query = r.tableDrop(TEST_TABLE, {foo: 'bar'}); 436 | compare(query, done); 437 | }); 438 | 439 | it('tableDrop - 8', function(done) { 440 | var query = r.tableDrop(1, {foo: 'bar'}); 441 | compare(query, done); 442 | }); 443 | 444 | /* 445 | */ 446 | }); 447 | -------------------------------------------------------------------------------- /lib/selection.js: -------------------------------------------------------------------------------- 1 | function Selection(selection, table, args) { 2 | args = args || {}; 3 | 4 | this.selection = selection || []; 5 | this.length = this.selection.length; 6 | this.table = table; 7 | 8 | if (args.type === undefined) { 9 | this.type = "SELECTION"; 10 | } 11 | else { 12 | this.type = args.type; 13 | } 14 | this.operations = []; 15 | 16 | if ((args.operation !== undefined) && (args.index !== undefined)) { 17 | this.operations.push({ 18 | operation: args.operation, 19 | index: args.index, 20 | args: args.args, 21 | options: args.options 22 | }); 23 | } 24 | } 25 | module.exports = Selection; 26 | 27 | var util = require(__dirname+"/utils.js"); 28 | var Sequence = require(__dirname+"/sequence.js"); 29 | var Error = require(__dirname+"/error.js"); 30 | var Promise = require("bluebird"); 31 | 32 | 33 | // operation = {type: 'between', index: 'foo'} 34 | Selection.prototype.addOperation = function(operation) { 35 | this.operations.push(operation); 36 | }; 37 | 38 | // Import methods from Sequence 39 | var keys = Object.keys(Sequence.prototype); 40 | for(var i=0; i 0) 220 | && (this.operations[this.operations.length-1].operation === 'orderBy')) { 221 | result = new Selection([], this.table, { 222 | type: 'TABLE_SLICE' 223 | }); 224 | for(var i=0; i 0 263 | if (this.operations[this.operations.length-1].index !== index) { 264 | query.frames.push(0); 265 | query.frames.push(0); 266 | return Promise.reject(new Error.ReqlRuntimeError('Cannot order by index `'+index+'` after calling '+this.operations[this.operations.length-1].operation.toUpperCase()+' on index `'+this.operations[this.operations.length-1].index+'`', query.frames)); 267 | } 268 | } 269 | query.frames.push('index'); 270 | util.assertType(index, 'STRING', query); 271 | query.frames.pop(); 272 | 273 | if (this.table.indexes[index] === undefined) { 274 | return Promise.reject(new Error.ReqlRuntimeError('Index `'+index+'` was not found on table `'+this.db+'.'+this.name+'`', query.frames)); 275 | } 276 | 277 | if (order === 'DESC') { 278 | fields.unshift([74, [this.table.indexes[index].fn], {}]); 279 | } 280 | else { 281 | fields.unshift(this.table.indexes[index].fn); 282 | } 283 | } 284 | 285 | var selection = new Selection([], this.table, { 286 | type: this.type 287 | }); 288 | //TODO Move in the constructor 289 | selection.operations = this.operations.slice(0); 290 | 291 | var mainPromise; 292 | var restrictedSelection = new Selection([], this.table, {}); 293 | if (hasIndex === true) { 294 | var varId = util.getVarId(self.table.indexes[index].fn); 295 | mainPromise = Promise.map(self.selection, function(doc) { 296 | query.context[varId] = doc; 297 | return query.evaluate(self.table.indexes[index].fn, query, internalOptions).then(function(indexValue) { 298 | delete query.context[varId]; 299 | if ((indexValue !== null) || (util.isPlainObject(indexValue) && (indexValue.$reql_type$ === undefined))) { 300 | restrictedSelection.push(doc); 301 | } 302 | }).catch(function(err) { 303 | if (err.message.match(/^No attribute/)) { 304 | // We also drop documents where a non existent error was thrown 305 | } 306 | else { 307 | throw err; 308 | } 309 | }); 310 | }, {concurrency: 1}).then(function() { 311 | return restrictedSelection; 312 | }); 313 | } 314 | else { 315 | mainPromise = Promise.resolve(this); 316 | } 317 | 318 | var sequenceToSort = new Sequence(); 319 | return mainPromise.then(function(restrictedSelection) { 320 | return Promise.map(restrictedSelection.selection, function(ref) { 321 | var element = { 322 | original: ref, 323 | fields: new Sequence() 324 | }; 325 | return util.computeFields(element, 0, fields, query, internalOptions); 326 | }, {concurrency: 1}).then(function(resolved) { 327 | for(var i=0; i 0 429 | if (this.operations[this.operations.length-1].index !== options.index) { 430 | throw new Error.ReqlRuntimeError('Cannot use between index `'+this.operations[this.operations.length-1].index+'` after calling '+this.operations[this.operations.length-1].operation.toUpperCase()+' on index `'+this.index+'`', query.frames); 431 | } 432 | 433 | 434 | 435 | var selection = new Selection([], this.table, { 436 | type: this.type 437 | }); 438 | selection.operations = this.operations.slice(0); 439 | 440 | var varId = util.getVarId(self.table.indexes[options.index].fn); 441 | return Promise.reduce(self.selection, function(result, doc) { 442 | query.context[varId] = doc; 443 | return query.evaluate(self.table.indexes[options.index].fn, internalOptions).then(function(valueIndex) { 444 | delete query.context[varId]; 445 | if (self.table.indexes[options.index].multi === true) { 446 | for(var k=0; k 0); 189 | return message; 190 | }); 191 | }); 192 | 193 | it('get - 4', function(done) { 194 | var query = r.db(TEST_DB).table(TEST_TABLE).get(new Buffer('hello')); 195 | compare(query, done); 196 | }); 197 | 198 | it('get - 5', function(done) { 199 | var query = r.db(TEST_DB).table(TEST_TABLE).get(); 200 | compare(query, done); 201 | }); 202 | 203 | it('get - 6', function(done) { 204 | var query = r.db(TEST_DB).table(TEST_TABLE).get(1,2,3); 205 | compare(query, done); 206 | }); 207 | 208 | it('get - 7', function(done) { 209 | var query = r.db(TEST_DB).table(TEST_TABLE).get(r.db(TEST_DB).table(TEST_TABLE)); 210 | compare(query, done); 211 | }); 212 | 213 | it('get - 8', function(done) { 214 | var query = r.expr([]).get(1); 215 | compare(query, done); 216 | }); 217 | 218 | it('getAll - 1', function(done) { 219 | var query = r.db(TEST_DB).table(TEST_TABLE).getAll(1); 220 | compare(query, done); 221 | }); 222 | 223 | it('getAll - 2', function(done) { 224 | var query = r.db(TEST_DB).table(TEST_TABLE).getAll(1,2,3).orderBy('id'); 225 | compare(query, done); 226 | }); 227 | 228 | it('getAll - 3', function(done) { 229 | var query = r.db(TEST_DB).table(TEST_TABLE).getAll(MISSING_ID); 230 | compare(query, done); 231 | }); 232 | 233 | it('getAll - 4', function(done) { 234 | var query = r.db(TEST_DB).table(TEST_TABLE).getAll(101, {index: 'foo'}).orderBy('id'); 235 | compare(query, done); 236 | }); 237 | 238 | it('getAll - 5', function(done) { 239 | var query = r.db(TEST_DB).table(TEST_TABLE).getAll([200, 201, 202], {index: 'bar'}).orderBy('id'); 240 | compare(query, done); 241 | }); 242 | 243 | it('getAll - 6', function(done) { 244 | var query = r.db(TEST_DB).table(TEST_TABLE).getAll(201, {index: 'barmulti'}).orderBy('id'); 245 | compare(query, done); 246 | }); 247 | 248 | it('getAll - 7', function(done) { 249 | var query = r.db(TEST_DB).table(TEST_TABLE).getAll(); 250 | compare(query, done); 251 | }); 252 | 253 | it('getAll - 8', function(done) { 254 | var query = r.expr('bar').getAll(1); 255 | compare(query, done); 256 | }); 257 | 258 | it('between - 1', function(done) { 259 | var query = r.db(TEST_DB).table(TEST_TABLE).between(r.minval, r.maxval).orderBy('id'); 260 | compare(query, done); 261 | }); 262 | 263 | it('between - 2', function(done) { 264 | var query = r.db(TEST_DB).table(TEST_TABLE).between(r.minval, 3).orderBy('id'); 265 | compare(query, done); 266 | }); 267 | 268 | it('between - 3', function(done) { 269 | var query = r.db(TEST_DB).table(TEST_TABLE).between(2, r.maxval).orderBy('id'); 270 | compare(query, done); 271 | }); 272 | 273 | it('between - 4', function(done) { 274 | var query = r.db(TEST_DB).table(TEST_TABLE).between(2, 3).orderBy('id'); 275 | compare(query, done); 276 | }); 277 | 278 | it('between - 5', function(done) { 279 | var query = r.db(TEST_DB).table(TEST_TABLE).between(2, 3, {left_bound: 'closed', right_bound: 'closed'}).orderBy('id'); 280 | compare(query, done); 281 | }); 282 | 283 | it('between - 6', function(done) { 284 | var query = r.db(TEST_DB).table(TEST_TABLE).between(2, 3, {left_bound: 'open', right_bound: 'closed'}).orderBy('id'); 285 | compare(query, done); 286 | }); 287 | 288 | it('between - 7', function(done) { 289 | var query = r.db(TEST_DB).table(TEST_TABLE).between(2, 3, {left_bound: 'closed', right_bound: 'open'}).orderBy('id'); 290 | compare(query, done); 291 | }); 292 | 293 | it('between - 8', function(done) { 294 | var query = r.db(TEST_DB).table(TEST_TABLE).between(2, 3, {left_bound: 'open', right_bound: 'open'}).orderBy('id'); 295 | compare(query, done); 296 | }); 297 | 298 | it('between - 9', function(done) { 299 | var query = r.db(TEST_DB).table(TEST_TABLE).between(20, 30, {index: 'foo'}).orderBy('id'); 300 | compare(query, done); 301 | }); 302 | 303 | it('between - 10', function(done) { 304 | var query = r.db(TEST_DB).table(TEST_TABLE).between(20, 30, {left_bound: 'hello'}); 305 | compare(query, done); 306 | }); 307 | 308 | it('between - 11', function(done) { 309 | var query = r.db(TEST_DB).table(TEST_TABLE).between(20, 30, {rightBound: 'hello'}); 310 | compare(query, done); 311 | }); 312 | 313 | it('between - 12', function(done) { 314 | var query = r.db(TEST_DB).table(TEST_TABLE).between(r.db(TEST_DB).table(TEST_TABLE), 30, {left_bound: 'hello'}); 315 | compare(query, done); 316 | }); 317 | 318 | it('between - 13', function(done) { 319 | var query = r.db(TEST_DB).table(TEST_TABLE).between(r.minval, r.db(TEST_DB).table(TEST_TABLE), {left_bound: 'hello'}); 320 | compare(query, done); 321 | }); 322 | 323 | it('between - 14', function(done) { 324 | var query = r.expr([1,2,3]).between(r.minval, r.maxval); 325 | compare(query, done, function(error) { 326 | var message = error.split(':')[0]; 327 | assert(message.length > 0); 328 | return message; 329 | }); 330 | }); 331 | 332 | it('between - 15', function(done) { 333 | var query = r.db(TEST_DB).table(TEST_TABLE).between(r.minval, r.maxval).between(r.minval, r.maxval); 334 | compare(query, done); 335 | }); 336 | 337 | it('between - 16', function(done) { 338 | var query = r.db(TEST_DB).table(TEST_TABLE).between(r.minval, r.maxval).between(r.minval, r.maxval, {index: 'foo'}); 339 | compare(query, done); 340 | }); 341 | 342 | it('between - 17', function(done) { 343 | var query = r.db(TEST_DB).table(TEST_TABLE).between(r.minval, r.maxval).orderBy({index: 'id'}); 344 | compare(query, done); 345 | }); 346 | 347 | it('between - 18', function(done) { 348 | var query = r.db(TEST_DB).table(TEST_TABLE).between(r.minval, r.maxval).orderBy({index: 'foo'}); 349 | compare(query, done); 350 | }); 351 | 352 | it('between - 19', function(done) { 353 | var query = r.db(TEST_DB).table(TEST_TABLE).orderBy({index: 'id'}).between(r.minval, r.maxval); 354 | compare(query, done); 355 | }); 356 | 357 | it('between - 20', function(done) { 358 | var query = r.db(TEST_DB).table(TEST_TABLE).orderBy({index: 'id'}).between(r.minval, r.maxval); 359 | compare(query, done); 360 | }); 361 | 362 | it('between - 21', function(done) { 363 | var query = r.db(TEST_DB).table(TEST_TABLE).orderBy({index: 'foo'}).between(r.minval, r.maxval); 364 | compare(query, done); 365 | }); 366 | 367 | it('between - 22', function(done) { 368 | var query = r.db(TEST_DB).table(TEST_TABLE).between(200, 300, {index: 'barmulti'}); 369 | compare(query, done); 370 | }); 371 | 372 | it('between - 23', function(done) { 373 | var query = r.db(TEST_DB).table(TEST_TABLE).orderBy({index: 'barmulti'}).between(200, 300, {index: 'barmulti'}); 374 | compare(query, done); 375 | }); 376 | 377 | it('between - 24', function(done) { 378 | var query = r.db(TEST_DB).table(TEST_TABLE).between(200, 300, {index: 'barmulti'}); 379 | compare(query, done); 380 | }); 381 | 382 | it('between - 25', function(done) { 383 | var query = r.db(TEST_DB).table(TEST_TABLE).between([20, r.minval], [20, r.maxval], {index: 'foobar'}).orderBy('id'); 384 | compare(query, done); 385 | }); 386 | 387 | it('between - 26', function(done) { 388 | var query = r.db(TEST_DB).table(TEST_TABLE).between(10, 30, {index: 'foo'}).orderBy({index: 'foo'}); 389 | compare(query, done); 390 | }); 391 | 392 | it('between - 27', function(done) { 393 | var query = r.db(TEST_DB).table(TEST_TABLE).between(10, 30, {index: 'foo'}).orderBy({index: r.desc('foo')}); 394 | compare(query, done); 395 | }); 396 | 397 | it('filter - 1', function(done) { 398 | var query = r.db(TEST_DB).table(TEST_TABLE).filter({id: 1}).orderBy('id'); 399 | compare(query, done); 400 | }); 401 | 402 | it('filter - 2', function(done) { 403 | var query = r.db(TEST_DB).table(TEST_TABLE).filter(true).orderBy('id'); 404 | compare(query, done); 405 | }); 406 | 407 | it('filter - 3', function(done) { 408 | var query = r.db(TEST_DB).table(TEST_TABLE).filter(r.row('id').eq(1)).orderBy('id'); 409 | compare(query, done); 410 | }); 411 | 412 | it('filter - 4', function(done) { 413 | var query = r.expr([{foo: {nested: "bar", hello: "world"}, buzz: "lol"}]).filter({foo: {nested: "bar"}}); 414 | compare(query, done); 415 | }); 416 | 417 | it('filter - 5', function(done) { 418 | var query = r.expr([{foo: {nested: ["bar", "baar"], hello: "world"}, buzz: "lol"}]).filter({foo: {nested: ["bar", "baar"]}}); 419 | compare(query, done); 420 | }); 421 | 422 | it('filter - 6', function(done) { 423 | var query = r.db(TEST_DB).table(TEST_TABLE).filter(function(doc) { 424 | return doc('id').eq(1); 425 | }).orderBy('id'); 426 | compare(query, done); 427 | }); 428 | 429 | it('filter - 7', function(done) { 430 | var query = r.expr([1,2,3,4,5]).filter(function(value) { 431 | return value.gt(2); 432 | }).orderBy(r.row); 433 | compare(query, done); 434 | }); 435 | 436 | it('filter - 8', function(done) { 437 | var query = r.expr([1,2,3,4,5]).filter(r.args([1,2,3])); 438 | compare(query, done); 439 | }); 440 | 441 | it('filter - 9', function(done) { 442 | var query = r.expr('foo').filter(r.row('id').eq(2)); 443 | compare(query, done); 444 | }); 445 | 446 | it('filter - 10', function(done) { 447 | var query = r.expr([1,2,3,4,5]).filter(r.js('(function(row) { return row > 2; })')); 448 | compare(query, done); 449 | }); 450 | 451 | it('filter - 11', function(done) { 452 | var query = r.expr([1,2,3,4,5]).filter(r.js('(function(row) { return Infinity; })')); 453 | compare(query, done); 454 | }); 455 | 456 | it('filter - 12', function(done) { 457 | var query = r.expr([1,2,3,4,5]).filter(r.js('(function(row) { return row.a.b; })')); 458 | compare(query, done); 459 | }); 460 | 461 | it('filter - 13', function(done) { 462 | var query = r.expr([1,2,3,4,5]).filter(r.js('(function(row) { return undefined; })')); 463 | compare(query, done); 464 | }); 465 | 466 | it('filter - 14', function(done) { 467 | var query = r.expr([1,2,3,4,5]).filter(r.js('(function(row) { return new RegExp("bar"); })')); 468 | compare(query, done); 469 | }); 470 | 471 | it('filter - 15', function(done) { 472 | var query = r.expr([1,2,3,4,5]).filter(function(doc) { 473 | return 0; 474 | }); 475 | compare(query, done); 476 | }); 477 | 478 | 479 | /* 480 | // See rethinkdb/rethinkdb/issues/4189 481 | it('filter - 15', function(done) { 482 | var query = r.expr([1,2,3,4,5]).filter(function(x, y) { 483 | return x; 484 | }); 485 | compare(query, done); 486 | }); 487 | */ 488 | 489 | /* 490 | // Crash the server, see rethinkdb/rethinkdb#4190 491 | it('filter - 15', function(done) { 492 | var query = r.expr([1,2,3,4,5]).filter(r.js('(function(row) { return function() {}; })')) 493 | compare(query, done); 494 | }); 495 | */ 496 | 497 | /* 498 | */ 499 | }); 500 | -------------------------------------------------------------------------------- /test/joins.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 = 'reqlitetestjoins'; 9 | var TEST_TABLE2 = 'reqlitetestjoins2'; 10 | var MISSING_ID = 'nonExistingId'; 11 | var MISSING_FIELD = 'nonExistingField'; 12 | var MISSING_INDEX = 'nonExistingIndex'; 13 | 14 | var compare = require('./util.js').generateCompare(connections); 15 | 16 | describe('joins.js', function(){ 17 | before(function(done) { 18 | setTimeout(function() { // Delay for nodemon to restart the server 19 | r.connect(config.rethinkdb).bind({}).then(function(conn) { 20 | connections.rethinkdb = conn; 21 | return r.connect(config.reqlite); 22 | }).then(function(conn) { 23 | connections.reqlite = conn; 24 | this.query = r.dbCreate(TEST_DB); 25 | return this.query.run(connections.rethinkdb); 26 | }).catch(function() { // ignore errors 27 | }).finally(function() { 28 | return this.query.run(connections.reqlite); 29 | }).catch(function() { // ignore errors 30 | }).finally(function() { 31 | this.query = r.db(TEST_DB).tableDrop(TEST_TABLE); 32 | return this.query.run(connections.rethinkdb); 33 | }).catch(function() { // ignore errors 34 | }).finally(function() { 35 | return this.query.run(connections.reqlite); 36 | }).catch(function() { // ignore errors 37 | }).finally(function() { 38 | this.query = r.db(TEST_DB).tableCreate(TEST_TABLE); 39 | return this.query.run(connections.rethinkdb); 40 | }).catch(function() { // ignore errors 41 | }).finally(function() { 42 | return this.query.run(connections.reqlite); 43 | }).catch(function() { // ignore errors 44 | }).finally(function() { 45 | this.query = r.db(TEST_DB).tableDrop(TEST_TABLE2); 46 | return this.query.run(connections.rethinkdb); 47 | }).catch(function() { // ignore errors 48 | }).finally(function() { 49 | return this.query.run(connections.reqlite); 50 | }).catch(function() { // ignore errors 51 | }).finally(function() { 52 | this.query = r.db(TEST_DB).tableCreate(TEST_TABLE2); 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).table(TEST_TABLE).insert([ 60 | {id: 1, table: 1, foo: 10, bar: [100, 101, 102], optional: 1}, 61 | {id: 2, table: 1, foo: 10, bar: [200, 201, 202], optional: 1}, 62 | {id: 3, table: 1, foo: 20, bar: [300, 301, 302], optional: 2}, 63 | {id: 4, table: 1, foo: 20, bar: [400, 401, 402]}, 64 | ]); 65 | return this.query.run(connections.rethinkdb); 66 | }).catch(function() { // ignore errors 67 | }).finally(function() { 68 | return this.query.run(connections.reqlite); 69 | }).catch(function() { // ignore errors 70 | }).finally(function() { 71 | this.query = r.db(TEST_DB).table(TEST_TABLE2).insert([ 72 | {id: 1, table: 2, foo: 10, bar: [100, 101, 102]}, 73 | {id: 2, table: 2, foo: 20, bar: [200, 201, 202], optional: 2}, 74 | {id: 3, table: 2, foo: 30, bar: [300, 301, 302], optional: 1}, 75 | {id: 4, table: 2, foo: 40, bar: [400, 401, 402]}, 76 | ]); 77 | return this.query.run(connections.rethinkdb); 78 | }).catch(function() { // ignore errors 79 | }).finally(function() { 80 | return this.query.run(connections.reqlite); 81 | }).catch(function() { // ignore errors 82 | }).finally(function() { 83 | this.query = r.db(TEST_DB).table(TEST_TABLE2).indexCreate('foo'); 84 | return this.query.run(connections.rethinkdb); 85 | }).catch(function() { // ignore errors 86 | }).finally(function() { 87 | return this.query.run(connections.reqlite); 88 | }).catch(function() { // ignore errors 89 | }).finally(function() { 90 | this.query = r.db(TEST_DB).table(TEST_TABLE2).indexCreate('optional'); 91 | return this.query.run(connections.rethinkdb); 92 | }).catch(function() { // ignore errors 93 | }).finally(function() { 94 | return this.query.run(connections.reqlite); 95 | }).catch(function() { // ignore errors 96 | }).finally(function() { 97 | this.query = r.db(TEST_DB).table(TEST_TABLE2).indexCreate('bar'); 98 | return this.query.run(connections.rethinkdb); 99 | }).catch(function() { // ignore errors 100 | }).finally(function() { 101 | return this.query.run(connections.reqlite); 102 | }).catch(function() { // ignore errors 103 | }).finally(function() { 104 | this.query = r.db(TEST_DB).table(TEST_TABLE2).indexCreate('barmulti', r.row('bar'), {multi: true}); 105 | return this.query.run(connections.rethinkdb); 106 | }).catch(function() { // ignore errors 107 | }).finally(function() { 108 | return this.query.run(connections.reqlite); 109 | }).catch(function() { // ignore errors 110 | }).finally(function() { 111 | this.query = r.db(TEST_DB).table(TEST_TABLE).indexWait(); 112 | return this.query.run(connections.rethinkdb); 113 | }).catch(function(e) { // ignore errors 114 | }).finally(function() { 115 | return this.query.run(connections.reqlite); 116 | }).catch(function() { // ignore errors 117 | }).finally(function() { 118 | this.query = r.db(TEST_DB).table(TEST_TABLE2).indexWait(); 119 | return this.query.run(connections.rethinkdb); 120 | }).catch(function(e) { // ignore errors 121 | }).finally(function() { 122 | return this.query.run(connections.reqlite); 123 | }).catch(function() { // ignore errors 124 | }).finally(function() { 125 | //TODO Add and test a geo index 126 | done(); 127 | }); 128 | }, 400); 129 | }); 130 | 131 | it('check init join - 1', function(done) { 132 | var query = r.db(TEST_DB).table(TEST_TABLE).orderBy(r.row); 133 | compare(query, done); 134 | }); 135 | 136 | it('innerJoin - 1', function(done) { 137 | var query = r.db(TEST_DB).table(TEST_TABLE).innerJoin( 138 | r.db(TEST_DB).table(TEST_TABLE2), 139 | true 140 | ).orderBy(r.row); 141 | compare(query, done); 142 | }); 143 | 144 | it('innerJoin - 2', function(done) { 145 | var query = r.db(TEST_DB).table(TEST_TABLE).innerJoin( 146 | r.db(TEST_DB).table(TEST_TABLE2), 147 | function(left, right) { 148 | return left('id').eq(right('id')); 149 | } 150 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 151 | compare(query, done); 152 | }); 153 | 154 | it('innerJoin - 3', function(done) { 155 | var query = r.db(TEST_DB).table(TEST_TABLE).innerJoin( 156 | r.db(TEST_DB).table(TEST_TABLE2), 157 | r.js('(function (left, right) { return left.id == right.id; })') 158 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 159 | compare(query, done); 160 | }); 161 | 162 | it('innerJoin - 4', function(done) { 163 | var query = r.db(TEST_DB).table(TEST_TABLE).innerJoin(); 164 | compare(query, done); 165 | }); 166 | 167 | it('innerJoin - 5', function(done) { 168 | var query = r.db(TEST_DB).table(TEST_TABLE).innerJoin( 169 | r.db(TEST_DB).table(TEST_TABLE2), 170 | function(left) { return left('id').eq(left('id')); } 171 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 172 | compare(query, done, function(error) { 173 | return /^Expected 1 argument but found 2/.test(error); 174 | }); 175 | }); 176 | 177 | it('innerJoin - 6', function(done) { 178 | var query = r.db(TEST_DB).table(TEST_TABLE).innerJoin( 179 | r.db(TEST_DB).table(TEST_TABLE2), 180 | function(left, right, extra) { return left('id').eq(right('id')); } 181 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 182 | compare(query, done, function(error) { 183 | return /^Expected 3 arguments but found 2/.test(error); 184 | }); 185 | }); 186 | 187 | it('innerJoin - 7', function(done) { 188 | var query = r.db(TEST_DB).table(TEST_TABLE).innerJoin( 189 | 'foo', 190 | function(left, right, extra) { return left('id').eq(right('id')); } 191 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 192 | compare(query, done); 193 | }); 194 | 195 | it('innerJoin - 8', function(done) { 196 | var query = r.expr('foo').innerJoin( 197 | r.db(TEST_DB).table(TEST_TABLE2), 198 | function(left, right) { return left('id').eq(right('id')); } 199 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 200 | compare(query, done); 201 | }); 202 | 203 | it('innerJoin - 9', function(done) { 204 | var query = r.db(TEST_DB).table(TEST_TABLE2).innerJoin( 205 | r.db(TEST_DB).table(TEST_TABLE2), 206 | function(left, right) { return left('optional').eq(right('optional')); } 207 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 208 | compare(query, done, function(error) { 209 | var message = error.split(':')[0]; 210 | assert(message.length > 0); 211 | return message; 212 | }); 213 | }); 214 | 215 | it('innerJoin - 19', function(done) { 216 | var query = r.db(TEST_DB).table(TEST_TABLE).innerJoin( 217 | r.db(TEST_DB).table(TEST_TABLE2), 218 | r.js('(function (left, right) { return left.id == right.optional.foo; })') 219 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 220 | compare(query, done); 221 | }); 222 | 223 | it('innerJoin - 11', function(done) { 224 | var query = r.db(TEST_DB).table(TEST_TABLE).innerJoin( 225 | r.db(TEST_DB).table(TEST_TABLE2), 226 | false 227 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 228 | compare(query, done); 229 | }); 230 | 231 | it('innerJoin - 12', function(done) { 232 | var query = r.db(TEST_DB).table(TEST_TABLE).innerJoin( 233 | r.db(TEST_DB).table(TEST_TABLE2), 234 | function(left, right) { 235 | return left('id').gt(right('id')); 236 | } 237 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 238 | compare(query, done); 239 | }); 240 | 241 | it('innerJoin - 13', function(done) { 242 | var query = r.db(TEST_DB).table(TEST_TABLE).innerJoin( 243 | r.db(TEST_DB).table(TEST_TABLE2), 244 | function(left, right) { 245 | return left(MISSING_FIELD).gt(right(MISSING_FIELD)); 246 | } 247 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 248 | compare(query, done, function(error) { 249 | var message = error.split(':')[0]; 250 | assert(message.length > 0); 251 | return message; 252 | }); 253 | }); 254 | 255 | it('outerJoin - 1', function(done) { 256 | var query = r.db(TEST_DB).table(TEST_TABLE).outerJoin( 257 | r.db(TEST_DB).table(TEST_TABLE2), 258 | true 259 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 260 | compare(query, done); 261 | }); 262 | 263 | it('outerJoin - 2', function(done) { 264 | var query = r.db(TEST_DB).table(TEST_TABLE).outerJoin( 265 | r.db(TEST_DB).table(TEST_TABLE2), 266 | function(left, right) { 267 | return left('id').eq(right('id')); 268 | } 269 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 270 | compare(query, done); 271 | }); 272 | 273 | it('outerJoin - 3', function(done) { 274 | var query = r.db(TEST_DB).table(TEST_TABLE).outerJoin( 275 | r.db(TEST_DB).table(TEST_TABLE2), 276 | r.js('(function (left, right) { return left.id == right.id; })') 277 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 278 | compare(query, done); 279 | }); 280 | 281 | it('outerJoin - 4', function(done) { 282 | var query = r.db(TEST_DB).table(TEST_TABLE).outerJoin(); 283 | compare(query, done); 284 | }); 285 | 286 | it('outerJoin - 5', function(done) { 287 | var query = r.db(TEST_DB).table(TEST_TABLE).outerJoin( 288 | r.db(TEST_DB).table(TEST_TABLE2), 289 | function(left) { return left('id').eq(left('id')); } 290 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 291 | compare(query, done, function(error) { 292 | return /^Expected 1 argument but found 2/.test(error); 293 | }); 294 | }); 295 | 296 | it('outerJoin - 6', function(done) { 297 | var query = r.db(TEST_DB).table(TEST_TABLE).outerJoin( 298 | r.db(TEST_DB).table(TEST_TABLE2), 299 | function(left, right, extra) { return left('id').eq(right('id')); } 300 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 301 | compare(query, done, function(error) { 302 | return /^Expected 3 arguments but found 2/.test(error); 303 | }); 304 | }); 305 | 306 | it('outerJoin - 7', function(done) { 307 | var query = r.db(TEST_DB).table(TEST_TABLE).outerJoin( 308 | 'foo', 309 | function(left, right, extra) { return left('id').eq(right('id')); } 310 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 311 | compare(query, done); 312 | }); 313 | 314 | it('outerJoin - 8', function(done) { 315 | var query = r.expr('foo').outerJoin( 316 | r.db(TEST_DB).table(TEST_TABLE2), 317 | function(left, right) { return left('id').eq(right('id')); } 318 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 319 | compare(query, done); 320 | }); 321 | 322 | it('outerJoin - 9', function(done) { 323 | var query = r.db(TEST_DB).table(TEST_TABLE2).outerJoin( 324 | r.db(TEST_DB).table(TEST_TABLE2), 325 | function(left, right) { return left('optional').eq(right('optional')); } 326 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 327 | compare(query, done, function(error) { 328 | var message = error.split(':')[0]; 329 | assert(message.length > 0); 330 | return message; 331 | }); 332 | }); 333 | 334 | it('outerJoin - 19', function(done) { 335 | var query = r.db(TEST_DB).table(TEST_TABLE).outerJoin( 336 | r.db(TEST_DB).table(TEST_TABLE2), 337 | r.js('(function (left, right) { return left.id == right.optional.foo; })') 338 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 339 | compare(query, done); 340 | }); 341 | 342 | it('outerJoin - 11', function(done) { 343 | var query = r.db(TEST_DB).table(TEST_TABLE).outerJoin( 344 | r.db(TEST_DB).table(TEST_TABLE2), 345 | false 346 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 347 | compare(query, done); 348 | }); 349 | 350 | it('outerJoin - 12', function(done) { 351 | var query = r.db(TEST_DB).table(TEST_TABLE).outerJoin( 352 | r.db(TEST_DB).table(TEST_TABLE2), 353 | function(left, right) { 354 | return left('id').gt(right('id')); 355 | } 356 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 357 | compare(query, done); 358 | }); 359 | 360 | it('outerJoin - 13', function(done) { 361 | var query = r.db(TEST_DB).table(TEST_TABLE).outerJoin( 362 | r.db(TEST_DB).table(TEST_TABLE2), 363 | function(left, right) { 364 | return left(MISSING_FIELD).gt(right(MISSING_FIELD)); 365 | } 366 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 367 | compare(query, done, function(error) { 368 | var message = error.split(':')[0]; 369 | assert(message.length > 0); 370 | return message; 371 | }); 372 | }); 373 | 374 | it('eqJoin - 1', function(done) { 375 | var query = r.db(TEST_DB).table(TEST_TABLE).eqJoin( 376 | 'id', 377 | r.db(TEST_DB).table(TEST_TABLE2) 378 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 379 | compare(query, done); 380 | }); 381 | 382 | it('eqJoin - 2', function(done) { 383 | var query = r.db(TEST_DB).table(TEST_TABLE).eqJoin( 384 | 'foo', // match nothing 385 | r.db(TEST_DB).table(TEST_TABLE2) 386 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 387 | compare(query, done); 388 | }); 389 | 390 | it('eqJoin - 3', function(done) { 391 | var query = r.db(TEST_DB).table(TEST_TABLE).eqJoin( 392 | 'foo', 393 | r.db(TEST_DB).table(TEST_TABLE2), 394 | {index: 'foo'} 395 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 396 | compare(query, done); 397 | }); 398 | 399 | it('eqJoin - 4', function(done) { 400 | var query = r.expr([{foo: 101}, {foo: 201}]).eqJoin( 401 | 'foo', 402 | r.db(TEST_DB).table(TEST_TABLE2), 403 | {index: 'bar'} 404 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 405 | compare(query, done); 406 | }); 407 | 408 | it('eqJoin - 5', function(done) { 409 | var query = r.expr([{foo: [200, 201, 202]}, {foo: [100, 101, 102]}]).eqJoin( 410 | 'foo', 411 | r.db(TEST_DB).table(TEST_TABLE2), 412 | {index: 'bar'} 413 | ).orderBy(r.row('left')('foo'), r.row('right')('id')); 414 | compare(query, done); 415 | }); 416 | 417 | it('eqJoin - 6', function(done) { 418 | var query = r.expr('foo').eqJoin( 419 | 'id', 420 | r.db(TEST_DB).table(TEST_TABLE2) 421 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 422 | compare(query, done); 423 | }); 424 | 425 | it('eqJoin - 7', function(done) { 426 | var query = r.db(TEST_DB).table(TEST_TABLE).eqJoin( 427 | 'id', 428 | [1,2,3,4,5] 429 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 430 | compare(query, done, function(error) { 431 | var message = error.split(':')[0]; 432 | assert(message.length > 0); 433 | return message; 434 | }); 435 | }); 436 | 437 | it('eqJoin - 8', function(done) { 438 | var query = r.db(TEST_DB).table(TEST_TABLE).eqJoin( 439 | r.row(MISSING_FIELD), 440 | r.db(TEST_DB).table(TEST_TABLE) 441 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 442 | compare(query, done); 443 | }); 444 | 445 | it('eqJoin - 9', function(done) { 446 | var query = r.db(TEST_DB).table(TEST_TABLE).eqJoin( 447 | r.row('foo'), 448 | r.db(TEST_DB).table(TEST_TABLE2), 449 | {index: 'foo'} 450 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 451 | compare(query, done); 452 | }); 453 | 454 | it('eqJoin - 10', function(done) { 455 | var query = r.db(TEST_DB).table(TEST_TABLE).eqJoin( 456 | r.row('optional'), 457 | r.db(TEST_DB).table(TEST_TABLE2), 458 | {index: 'optional'} 459 | ).orderBy(r.row('left')('id'), r.row('right')('id')); 460 | compare(query, done); 461 | }); 462 | 463 | it('zip - 1', function(done) { 464 | var query = r.db(TEST_DB).table(TEST_TABLE).eqJoin( 465 | 'id', 466 | r.db(TEST_DB).table(TEST_TABLE2) 467 | ).zip().orderBy(r.row); 468 | compare(query, done); 469 | }); 470 | 471 | it('zip - 2', function(done) { 472 | var query = r.expr('foo').zip(); 473 | compare(query, done); 474 | }); 475 | 476 | it('zip - 3', function(done) { 477 | var query = r.expr([{left: {}, right: {}}]).zip('foo'); 478 | compare(query, done); 479 | }); 480 | 481 | 482 | /* 483 | */ 484 | }); 485 | -------------------------------------------------------------------------------- /lib/table.js: -------------------------------------------------------------------------------- 1 | var _util = require('util'); 2 | var EventEmitter = require('events').EventEmitter; 3 | 4 | //TODO Handle table_slice everywhere 5 | function Table(name, db, options) { 6 | this.name = name; 7 | this.db = db; // the name of the database 8 | this.options = options || {}; 9 | this.options.primaryKey = this.options.primary_key || "id"; 10 | this.documents = {}; 11 | this.indexes = {}; // String -> FUNC terms 12 | this.indexes[this.options.primaryKey] = { 13 | //TODO Make me use TERMS 14 | //fn: function(doc) { return doc[name] }, 15 | // 69=FUNC, 2=MAKE_ARRAY, 31=GET_FIELD, 10=VAR, 1=ARGUMENT_INDEX 16 | //TODO Use a uuid to avoid collision 17 | fn: [ 69, [ [ 2, [ 1 ] ], [ 31, [ [ 10, [ 1 ] ], this.options.primaryKey ] ] ] ], 18 | function: { /* TODO */ }, 19 | geo: false, 20 | outdated: false, 21 | ready: true, 22 | multi: false 23 | }; 24 | this.id = util.uuid(); 25 | if (options.meta === true) { 26 | // meta table 27 | this.meta = true; 28 | } 29 | } 30 | _util.inherits(Table, EventEmitter); 31 | 32 | module.exports = Table; 33 | 34 | var Selection = require(__dirname+"/selection.js"); 35 | var Sequence = require(__dirname+"/sequence.js"); 36 | var Document = require(__dirname+"/document.js"); 37 | var MissingDoc = require(__dirname+"/missing_doc.js"); 38 | var util = require(__dirname+"/utils.js"); 39 | var Error = require(__dirname+"/error.js"); 40 | var Promise = require("bluebird"); 41 | 42 | // Import methods from Selection 43 | var _keys = Object.keys(Selection.prototype); 44 | for(var i=0; i<_keys.length; i++) { 45 | (function(key) { 46 | Table.prototype[key] = function() { 47 | var docs = []; 48 | for(var internalPk in this.documents) { 49 | docs.push(this.documents[internalPk]); 50 | } 51 | var selection = new Selection(docs, this, {}); 52 | return selection[key].apply(selection, arguments); 53 | }; 54 | })(_keys[i]); 55 | } 56 | 57 | Table.prototype.typeOf = function() { 58 | return "TABLE"; 59 | }; 60 | 61 | Table.prototype.get = function(primaryKeyValue) { 62 | var internalPk = util.makeInternalPk(primaryKeyValue); 63 | if (this.documents[internalPk] === undefined) { 64 | return Promise.resolve(new MissingDoc(primaryKeyValue, this)); 65 | } 66 | else { 67 | return Promise.resolve(this.documents[internalPk]); 68 | } 69 | }; 70 | 71 | // Single delete 72 | Table.prototype._delete = function(doc, options) { 73 | var pk = this.options.primaryKey; 74 | var internalPk = util.makeInternalPk(doc.doc[pk]); 75 | var result; 76 | 77 | // The document does exist, if it doesn't, the selection is empty, or we are 78 | // deleting a missing doc 79 | var oldValue = this.documents[internalPk]; 80 | if (oldValue === undefined) { 81 | result = util.writeResult(); 82 | result.skipped++; 83 | return result; 84 | } 85 | delete this.documents[internalPk]; 86 | this.emit('change', { 87 | new_val: null, 88 | old_val: oldValue.doc 89 | }); 90 | 91 | result = util.writeResult(); 92 | result.deleted++; 93 | if (options.return_changes === true || options.return_changes === 'always') { 94 | result.changes = new Sequence([{ 95 | new_val: null, 96 | old_val: util.deepCopy(doc.doc) 97 | }]); 98 | } 99 | return Promise.resolve(result); 100 | }; 101 | 102 | Table.prototype.insert = function(docs, options, query) { 103 | options = options || {}; 104 | 105 | try { 106 | util.assertType(docs, ['OBJECT', 'ARRAY'], query); 107 | } 108 | catch(err) { 109 | var typeValue = util.getType(docs); 110 | throw new Error.ReqlRuntimeError("Cannot convert "+typeValue+" to SEQUENCE", this.frames); 111 | } 112 | 113 | var result = util.writeResult(); 114 | var newDoc; 115 | if (util.isSequence(docs)) { 116 | for(var i=0; i