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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 24 additions & 11 deletions formatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ var { Args, AbstractMethodError} = require('@themost/common');
const { QueryExpression, QueryField, QueryEntity } = require('./query');
const { JSONArray, JSONObject } = require('@themost/json');
const {MethodCallExpression} = require('./expressions');
const {isNameReference, trimNameReference} = require('./name-reference');
const {isNameReference, trimNameReference, isMethodOrNameReference} = require('./name-reference');
var instanceOf = require('./instance-of').instanceOf;
var ObjectNameValidator = require('./object-name.validator').ObjectNameValidator;

Expand Down Expand Up @@ -1081,26 +1081,39 @@ SqlFormatter.prototype.formatSelect = function(obj)
{
//enumerate joins
_.forEach(joins, function(x) {
var joinType;
var joinType = (x.$entity.$join || 'inner').toUpperCase();
if (instanceOf(x.$entity, QueryExpression)) {
// get join type
joinType = (x.$entity.$join || 'inner').toUpperCase();
// append join statement
sql = sql.concat(sprintf(' %s JOIN (%s)', joinType, $this.format(x.$entity)));
// add alias
if (x.$entity.$alias) {
sql = sql.concat(getAliasKeyword.bind($this)()).concat($this.escapeName(x.$entity.$alias));
}
}
else {
// get join table name
} else {
sql += ' ' + joinType + ' JOIN ';
if (typeof x.$entity === 'object') {
const [key] = Object.keys(x.$entity);
if (isMethodOrNameReference(key)) {
sql += $this.escape(x.$entity);
const alias = x.$as || x.$entity.$as;
if (alias) {
sql += getAliasKeyword.bind($this)();
sql += $this.escapeName(alias);
}
const strWhere = $this.formatWhere(x.$with);
if (typeof strWhere === 'string' && strWhere.length > 0) {
sql += ' ON ' + strWhere;
}
return;
}
}
//get join table name
var table = Object.key(x.$entity);
// get on statement (the join comparison)
joinType = (x.$entity.$join || 'inner').toUpperCase();
sql = sql.concat(' '+ joinType + ' JOIN ').concat($this.escapeName(table));
sql += $this.escapeEntity(table);
// add alias
if (x.$entity.$as)
if (x.$entity.$as) {
sql = sql.concat(getAliasKeyword.bind($this)()).concat($this.escapeName(x.$entity.$as));
}
}
if (_.isArray(x.$with))
{
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@themost/query",
"version": "2.6.81",
"version": "2.6.82",
"description": "@themost/query is a query builder for SQL. It includes a wide variety of helper functions for building complex SQL queries under node.js.",
"main": "index.js",
"scripts": {
Expand Down
9 changes: 4 additions & 5 deletions query.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,10 @@ export declare class QueryExpression {
from(entity: QueryExpression): this;
from(entity: (string | QueryEntity | QueryExpression), ...additionalEntity: (string | QueryEntity | QueryExpression)[]): this;
join(entity: any, props?: any, alias?: any): this;
join(entity: QueryEntity): this;
leftJoin(entity: any, props?: any, alias?: any): this;
leftJoin(entity: QueryEntity): this;
rightJoin(entity: any, props?: any, alias?: any): this;
rightJoin(entity: QueryEntity): this;
join(entity: QueryEntity | any): this;
leftJoin(entity: QueryEntity | any): this;
rightJoin(entity: QueryEntity | any): this;
crossJoin(entity: any): this;
with(obj: any): this;
with<T,J>(expr: (value: T, otherValue: J, ...params: any[]) => any, ...params: any[]): this;
orderBy(expr: string | any): this;
Expand Down
68 changes: 64 additions & 4 deletions query.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ var SyncSeriesEventEmitter = require('@themost/events').SyncSeriesEventEmitter;
//noinspection JSUnusedLocalSymbols
require('./natives');
var {Expression} = require("./expressions");
var {isMethodOrNameReference} = require('./name-reference');
/**
* @class
* @constructor
Expand Down Expand Up @@ -264,6 +265,12 @@ QueryExpression.prototype.fields = function() {
//todo::add fields if any
}
else {
if (typeof x.$entity === 'object') {
var [key] = Object.keys(x.$entity);
if (isMethodOrNameReference(key)) {
return;
}
}
var table = Object.key(x.$entity), tableFields = x.$entity[table] || [];
_.forEach(tableFields, function(y) {
if (typeof x === 'string') {
Expand Down Expand Up @@ -728,11 +735,54 @@ QueryExpression.prototype.join = function(entity, props, alias) {
else if (entity instanceof QueryExpression) {
//do nothing (clone object)
obj = entity;
}
else {
} else {
// check if the given entity is a table-scalar function
// e.g. { $jsonEach: [ '$Users.group' ] } or
// using an alias { groups: { $jsonEach: [ '$Users.group' ] } }
var [key] = Object.keys(entity);
if (key && isMethodOrNameReference(key)) {
// the entity is a table-scalar function without alias
// set temporary expand object
if (alias == null) {
throw new Error('Alias is required when joining a table-like expression.');
}
this.privates.expand = {
$entity: obj,
$as: alias // use alias if any
};
Object.defineProperty(this, '$joinCollection', {
configurable: true,
enumerable: false,
writable: true,
value: alias
});
return this;
} else if (key) {
var [methodKey] = Object.keys(entity[key]);
if (methodKey && isMethodOrNameReference(methodKey)) {
// the entity is a table-scalar function with alias
obj = entity[key];
if (alias != null && alias !== key) {
throw new Error('Alias mismatch when joining a table-like expression. Given alias does not match the entity alias.');
}
// set temporary expand object
this.privates.expand = {
$entity: obj,
$as: alias || key // use alias if any
};
Object.defineProperty(this, '$joinCollection', {
configurable: true,
enumerable: false,
writable: true,
value: alias || key
});
return this;
}
}
obj[entity] = props || [];
if (typeof alias === 'string')
obj.$as=alias;
if (typeof alias === 'string') {
obj.$as = alias;
}
}
this.privates.expand = { $entity: obj };
// set current join entity
Expand Down Expand Up @@ -783,6 +833,16 @@ QueryExpression.prototype.join = function(entity, props, alias) {
return this;
}

QueryExpression.prototype.crossJoin = function(entity) {
this.join(entity);
if (this.privates.expand && this.privates.expand.$entity) {
this.privates.expand.$entity.$join = 'cross';
}
// a cross join cannot have a with expression
this.with(false);
return this;
}

/**
* Sets the join expression of the last join entity
* @param obj {*}
Expand Down
Loading