sql-builder.js 2.96 KB
const _ = require('lodash');

class SqlBuilder {
    constructor(table) {
        this._select = [];
        this._from = table;
        this._where = [];
        this._orWhere = [];
        this._limit = null;
        this._offset = null;
        this._orderBy = null;
        this._groupBy = null;
    }

    static of(table) {
        return new SqlBuilder(table)
    }

    // placeholer is {}
    // select * from where time={}
    static raw(sql, ...args) {
        return args.reduce((t, a) => {
            return t.replace('{}', a)
        }, sql.slice(0))
    }

    select(...args) {
        this._select.push(...args);
        return this;
    }

    where(field, op, value) {
        if (_.isPlainObject(field)) {
            this._where.push(field);
            return this;
        }

        if (arguments.length === 1 && _.isString(field)) {
            this._where.push({field: '', op: '', value: field});
            return this
        } else {
            this._where.push({field, op, value});
            return this;
        }
    }

    timeWhere(value) {

    }

    orWhere(field, op, value) {
        this._orWhere.push({field, op, value});
        return this;
    }

    from(table) {
        this._from = table;
        return this;
    }

    limit(limit) {
        this._limit = limit;
        return this;
    }

    offset(offset) {
        this._offset = offset;
        return this;
    }

    // order = desc or asc
    orderBy(order = 'DESC') {
        this._orderBy = order;
        return this;
    }

    toSql() {
        return this.toString();
    }

    groupBy(field) {
        this._groupBy = field;
        return this;
    }

    static raw(str) {
        return {
            toString() {
                return str;
            }
        }
    }

    toString() {
        let sql = `SELECT ${this._select.join(',')} FROM "${this._from}" `;

        let andWhere = this._where.map(({field, op, value}) => {
            if (field) {
                return `"${field}"${op}${_.isString(value) ? `'${value}'`: value}`
            } else {
                return value
            }
        }).filter(v => v && v.length).join(' AND ');
        let orWhere = this._orWhere.map(({field, op, value}) => `${field}${op}${value}`).filter(v => v.length).join(' OR ');

        if (andWhere) {
            sql += `WHERE ${andWhere} `;
        }

        if (andWhere && orWhere) {
            sql += `AND (${orWhere}) `;
        } else {
            if (orWhere) {
                sql += `WHERE ${orWhere}`;
            }
        }

        if (this._orderBy) {
            sql += `ORDER BY time ${this._orderBy.order} `;
        }

        if (this._groupBy) {
            sql += `GROUP BY ` + this._groupBy.map(f => `"${f}"`).join(',')
        }

        if (!_.isNil(this._limit)) {
            sql += `LIMIT ${this._limit} `;
        }

        if (!_.isNil(this._offset)) {
            sql += `OFFSET ${this._offset} `;
        }

        return sql;
    }
}

module.exports = SqlBuilder;