mongo-sql
Version:
A mongo-like interface for sql generation, postgres-style
1,374 lines (1,178 loc) • 28.2 kB
JavaScript
var assert = require('assert');
var builder = require('../');
describe('Conditions', function(){
it ('should build a query selecting on users where id = $1', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: { id: 5 }
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."id" = $1'
);
assert.deepEqual(
query.values
, [5]
);
});
it ('should build a query selecting on users where id = $1 and name = $2', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: { id: 5, name: 'Bob' }
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."id" = $1 and "users"."name" = $2'
);
assert.deepEqual(
query.values
, [5, 'Bob']
);
});
it ('should use boolean syntax', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: { isAwesome: true }
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."isAwesome" is true'
);
assert.deepEqual(
query.values
, []
);
query = builder.sql({
type: 'select'
, table: 'users'
, where: { isAwesome: false }
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."isAwesome" is false'
);
assert.deepEqual(
query.values
, []
);
});
it ('should build a query selecting on users where id = $1 or name = $2', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: { $or: { id: 5, name: 'Bob' } }
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."id" = $1 or "users"."name" = $2'
);
assert.deepEqual(
query.values
, [5, 'Bob']
);
});
it ('$gt', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: { id: { $gt: 5 } }
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."id" > $1'
);
assert.deepEqual(
query.values
, [5]
);
});
it ('$gt multi', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: { $gt: { id: 5, name: 'Tobias' } }
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."id" > $1 and "users"."name" > $2'
);
assert.deepEqual(
query.values
, [5, 'Tobias']
);
});
it ('$gte', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: { id: { $gte: 5 } }
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."id" >= $1'
);
assert.deepEqual(
query.values
, [5]
);
});
it ('$gte multi', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: { $gte: { id: 5, name: 'Tobias' } }
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."id" >= $1 and "users"."name" >= $2'
);
assert.deepEqual(
query.values
, [5, 'Tobias']
);
});
it ('$lt', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: { id: { $lt: 5 } }
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."id" < $1'
);
assert.deepEqual(
query.values
, [5]
);
});
it ('$lt multi', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: { $lt: { id: 5, name: 'Tobias' } }
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."id" < $1 and "users"."name" < $2'
);
assert.deepEqual(
query.values
, [5, 'Tobias']
);
});
it ('$lte', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: { id: { $lte: 5 } }
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."id" <= $1'
);
assert.deepEqual(
query.values
, [5]
);
});
it ('$lte multi', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: { $lte: { id: 5, name: 'Tobias' } }
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."id" <= $1 and "users"."name" <= $2'
);
assert.deepEqual(
query.values
, [5, 'Tobias']
);
});
it ('$null', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: { id: { $null: true } }
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."id" is null'
);
});
it ('$null should be not null when false', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: { id: { $null: false } }
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."id" is not null'
);
});
it ('should use $null when value is null', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: { id: null }
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."id" is null'
);
});
it ('$notNull', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: { id: { $notNull: true } }
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."id" is not null'
);
});
it ('$notNull should be null when false', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: { id: { $notNull: false } }
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."id" is null'
);
});
it ('$like', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: { name: { $like: 'Bobo' } }
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."name" like $1'
);
assert.deepEqual(
query.values
, ['Bobo']
);
});
it ('$ilike', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: { name: { $ilike: 'Bobo' } }
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."name" ilike $1'
);
assert.deepEqual(
query.values
, ['Bobo']
);
});
it ('$in', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
id: {
$in: {
type: 'select'
, table: 'groups'
, columns: ['userId']
, where: { "groupId": 5 }
}
}
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where ' +
'"users"."id" in (' +
'select "groups"."userId" from "groups" where ' +
'"groups"."groupId" = $1)'
);
assert.deepEqual(
query.values
, [5]
);
});
it ('$in array', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
id: {
$in: [1, 2, 3]
}
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where ' +
'"users"."id" in ($1, $2, $3)'
);
assert.deepEqual(
query.values
, [1, 2, 3]
);
});
it ('$in empty array', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
id: {
$in: []
}
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where ' +
'false'
);
assert.deepEqual(
query.values
, []
);
});
it ('$in array with undefined and null', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
id: {
$in: [1, 2, undefined, null, null, 3]
}
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where ' +
'"users"."id" in ($1, $2, $3) ' +
'or "users"."id" is null'
);
assert.deepEqual(
query.values
, [1, 2, 3]
);
});
it ('$in array with undefined and null combined with other statement', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
'true': 'true'
, id: {
$in: [1, 2, undefined, null, null, 3]
}
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where ' +
'"users"."true" = $1 and ' +
'("users"."id" in ($2, $3, $4) ' +
'or "users"."id" is null)'
);
assert.deepEqual(
query.values
, ['true', 1, 2, 3]
);
});
it ('$in array with only null', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
id: {
$in: [null]
}
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where ' +
'"users"."id" is null'
);
assert.deepEqual(query.values, []);
});
it ('$nin', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
id: {
$nin: {
type: 'select'
, table: 'groups'
, columns: ['userId']
, where: { "groupId": 5 }
}
}
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where ' +
'"users"."id" not in (' +
'select "groups"."userId" from "groups" where ' +
'"groups"."groupId" = $1)'
);
assert.deepEqual(
query.values
, [5]
);
});
it ('$nin array', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
id: {
$nin: [1, 2, 3]
}
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where ' +
'"users"."id" not in ($1, $2, $3)'
);
assert.deepEqual(
query.values
, [1, 2, 3]
);
});
it ('$nin array with undefined and null', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
id: {
$nin: [1, 2, undefined, null, null, 3]
}
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where ' +
'"users"."id" not in ($1, $2, $3) ' +
'or "users"."id" is not null'
);
assert.deepEqual(
query.values
, [1, 2, 3]
);
});
it ('$nin array with undefined and null combined with other statements', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
id: {
'true': 'true',
$nin: [1, 2, undefined, null, null, 3]
}
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where ' +
'"users"."true" = $1 and ' +
'("users"."id" not in ($2, $3, $4) ' +
'or "users"."id" is not null)'
);
assert.deepEqual(
query.values
, ['true', 1, 2, 3]
);
});
it ('$nin array with only null', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
id: {
$nin: [null]
}
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where ' +
'"users"."id" is not null'
);
assert.deepEqual(query.values , []);
});
it ('$nin for an empty array should evaluate to true', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
id: {
$nin: []
}
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where ' +
'true'
);
assert.deepEqual(query.values , []);
});
it ('$nin with subquery', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
id: {
$nin: {
type: 'select'
, table: 'sub'
, columns: ['user_id']
, where: {
value: {
$null: false
}
}
}
}
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."id" not in (select "sub"."user_id" from "sub" where "sub"."value" is not null)'
);
});
it ('should allow an arbitrary amount of conditions', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
name: 'Bob'
, id: { $lt: 500 }
, groupId: { $equals: 7 }
, another: { $or: [1, 2, 3, 4] }
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."name" = $1 and "users"."id" < $2 and "users"."groupId" = $3 and ("users"."another" = $4 or "users"."another" = $5 or "users"."another" = $6 or "users"."another" = $7)'
);
assert.deepEqual(
query.values
, ['Bob', 500, 7, 1, 2, 3, 4]
);
});
it ('should inline custom conditions', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
name: { $like: 'Bob' }
, $custom: ['"users"."createdAt" > now() - interval $1 month', 5]
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."name" like $1 and "users"."createdAt" > now() - interval $2 month'
);
assert.deepEqual(
query.values
, ['Bob', 5]
);
});
it ('should handle interpolating custom parameters', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
name: { $like: 'Bob' }
, $custom: ['$1 $2 $3 $4', 5, 6, 7, 8]
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."name" like $1 and $2 $3 $4 $5'
);
assert.deepEqual(
query.values
, ['Bob', 5, 6, 7, 8]
);
});
it ('$years_ago', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
createdAt: { $years_ago: 2 }
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."createdAt" >= now() - interval $1 year'
);
assert.deepEqual(
query.values
, [2]
);
});
it ('$years_ago inverse', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
$years_ago: { createdAt: 2, somethingElse: 3 }
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."createdAt" >= now() - interval $1 year and "users"."somethingElse" >= now() - interval $2 year'
);
assert.deepEqual(
query.values
, [2, 3]
);
});
it ('$months_ago', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
createdAt: { $months_ago: 2 }
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."createdAt" >= now() - interval $1 month'
);
assert.deepEqual(
query.values
, [2]
);
});
it ('$months_ago inverse', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
$months_ago: { createdAt: 2, somethingElse: 3 }
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."createdAt" >= now() - interval $1 month and "users"."somethingElse" >= now() - interval $2 month'
);
assert.deepEqual(
query.values
, [2, 3]
);
});
it ('$days_ago', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
createdAt: { $days_ago: 2 }
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."createdAt" >= now() - interval $1 day'
);
assert.deepEqual(
query.values
, [2]
);
});
it ('$days_ago inverse', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
$days_ago: { createdAt: 2, somethingElse: 3 }
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."createdAt" >= now() - interval $1 day and "users"."somethingElse" >= now() - interval $2 day'
);
assert.deepEqual(
query.values
, [2, 3]
);
});
it ('$hours_ago', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
createdAt: { $hours_ago: 2 }
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."createdAt" >= now() - interval $1 hour'
);
assert.deepEqual(
query.values
, [2]
);
});
it ('$hours_ago inverse', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
$hours_ago: { createdAt: 2, somethingElse: 3 }
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."createdAt" >= now() - interval $1 hour and "users"."somethingElse" >= now() - interval $2 hour'
);
assert.deepEqual(
query.values
, [2, 3]
);
});
it ('$minutes_ago', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
createdAt: { $minutes_ago: 2 }
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."createdAt" >= now() - interval $1 minute'
);
assert.deepEqual(
query.values
, [2]
);
});
it ('$exists', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
$exists: {
type: 'select',
table: 'other_table',
where: { column: 'value' }
}
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where exists (select "other_table".* from "other_table" where "other_table"."column" = $1)'
);
});
it ('$notExists', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
$notExists: {
type: 'select',
table: 'other_table',
where: { column: 'value' }
}
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where not exists (select "other_table".* from "other_table" where "other_table"."column" = $1)'
);
});
it ('$minutes_ago inverse', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
$minutes_ago: { createdAt: 2, somethingElse: 3 }
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."createdAt" >= now() - interval $1 minute and "users"."somethingElse" >= now() - interval $2 minute'
);
assert.deepEqual(
query.values
, [2, 3]
);
});
it ('$seconds_ago', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
createdAt: { $seconds_ago: 2 }
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."createdAt" >= now() - interval $1 second'
);
assert.deepEqual(
query.values
, [2]
);
});
it ('$seconds_ago inverse', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
$seconds_ago: { createdAt: 2, somethingElse: 3 }
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."createdAt" >= now() - interval $1 second and "users"."somethingElse" >= now() - interval $2 second'
);
assert.deepEqual(
query.values
, [2, 3]
);
});
it ('Should have no conditions if where is an empty object', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {}
});
assert.equal(
query.toString()
, 'select "users".* from "users"'
);
});
describe('JSON Operators', function(){
it ('Should Get JSON object field', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
'data->name': 'Bob'
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."data"->\'name\' = $1'
);
assert.deepEqual(
query.values
, ['Bob']
);
});
it ('Should Get JSON array element', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
'data->1': 'Bob'
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."data"->1 = $1'
);
assert.deepEqual(
query.values
, ['Bob']
);
});
it ('Should Get JSON array element as text', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
'data->>5': 'Bob'
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."data"->>5 = $1'
);
assert.deepEqual(
query.values
, ['Bob']
);
});
it ('Should Get JSON object field as text', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
'data->>name': 'Bob'
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."data"->>\'name\' = $1'
);
assert.deepEqual(
query.values
, ['Bob']
);
});
it ('Should operate on JSON text and cast the column', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
'data::json->name': 'Bob'
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."data"::json->\'name\' = $1'
);
assert.deepEqual(
query.values
, ['Bob']
);
});
it ('Should operate on JSON text and cast the column and specify table', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
'other_table.data::json->name': 'Bob'
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "other_table"."data"::json->\'name\' = $1'
);
assert.deepEqual(
query.values
, ['Bob']
);
});
it ('Should operate on JSON and play nicely with other helpers', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
'data->name': 'Bob'
, $or: {
id: 7
, 'data->other_id': 'blah'
, 'data::hstore->other_thing': { $gt: 'bill' }
}
}
});
assert.equal(
query.toString()
, [
'select "users".* from "users" where '
, '"users"."data"->\'name\' = $1 and '
, '("users"."id" = $2 or "users"."data"->\'other_id\' = $3 or '
, '"users"."data"::hstore->\'other_thing\' > $4)'
].join('')
);
assert.deepEqual(
query.values
, ['Bob', 7, 'blah', 'bill']
);
});
it ('Should be able to go deep', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
'data->3->records->4->id': 27
}
});
assert.equal(
query.toString()
, [
'select "users".* from "users" where '
, '"users"."data"->3->\'records\'->4->\'id\' = $1'
].join('')
);
assert.deepEqual(
query.values
, [27]
);
});
it ('Should be able to go deep with array syntax', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
'data#>{3,records,4,id}': 27
}
});
assert.equal(
query.toString()
, [
'select "users".* from "users" where '
, '"users"."data"#>\'{3,records,4,id}\' = $1'
].join('')
);
assert.deepEqual(
query.values
, [27]
);
});
it ('Should not double quote things already quoted', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
'data->\'3\'': 27
}
});
assert.equal(
query.toString()
, [
'select "users".* from "users" where '
, '"users"."data"->\'3\' = $1'
].join('')
);
assert.deepEqual(
query.values
, [27]
);
});
});
it ('should cast', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
'my_column::some_type': 27
}
});
assert.equal(
query.toString()
, [
'select "users".* from "users" where '
, '"users"."my_column"::some_type = $1'
].join('')
);
assert.deepEqual(
query.values
, [27]
);
});
it('having', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, groupBy: 'id'
, having: {
name: { $gt: 'Bob' }
}
});
assert.equal(
query.toString()
, [ 'select "users".* from "users" group by "users"."id" '
, 'having "users"."name" > $1'
].join('')
);
assert.deepEqual(
query.values
, ['Bob']
);
});
it('skip undefined', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, where: {
id: undefined,
name: 'Bob',
}
});
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."name" = $1'
);
assert.deepEqual(
query.values
, ['Bob']
);
});
it('should handle nulls', function() {
var query = builder.sql({
type: 'select',
table: 'users',
where: {
email: { $ne: null }
}
})
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."email" is not null'
);
assert.deepEqual(
query.values
, []
);
query = builder.sql({
type: 'select',
table: 'users',
where: {
email: null,
$ne: { name: null },
}
})
assert.equal(
query.toString()
, 'select "users".* from "users" where "users"."email" is null '
+ 'and "users"."name" is not null'
);
assert.deepEqual(
query.values
, []
);
})
it('should handle $not correctly', function() {
var query = builder.sql({
type: 'select',
table: 'users',
where: {
email: { $not : "not-me@internet.com" }
}
})
assert.equal(
query.toString()
, 'select "users".* from "users" where not "users"."email" = $1'
);
})
it('should handle $not correctly for expression with other expression', function() {
var query = builder.sql({
type: 'select',
table: 'users',
where: {
age: {
$not: [{
$lte: 12
}
]
}
}
})
assert.equal(
query.toString()
, 'select "users".* from "users" where not "users"."age" <= $1'
);
})
});