mirror of
https://github.com/lingble/clickhouse.git
synced 2025-11-06 14:08:30 +00:00
added tsv csv parsers on select query
# not completed parsers work on stream
This commit is contained in:
95
index.js
95
index.js
@@ -56,19 +56,19 @@ const DATABASE = 'default';
|
|||||||
const USERNAME = 'default';
|
const USERNAME = 'default';
|
||||||
|
|
||||||
function parseCSV(body) {
|
function parseCSV(body) {
|
||||||
return new Promise((resolve, reject) => {
|
const data = new tsv.Parser(SEPARATORS.CSV, { header: true }).parse(body);
|
||||||
csv.parse(body, { delimiter: ','}, (err, output) => {
|
data.splice(data.length - 1, 1);
|
||||||
if (err) {
|
return data;
|
||||||
reject(err);
|
}
|
||||||
return;
|
|
||||||
}
|
function parseJSON(body) {
|
||||||
resolve(output);
|
return JSON.parse(body);
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTSV(body) {
|
function parseTSV(body) {
|
||||||
return Promise.resolve().then(() => tsv.parse(body, {header: false}));
|
const data = new tsv.Parser(SEPARATORS.TSV, { header: true }).parse(body)
|
||||||
|
data.splice(data.length - 1, 1);
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
function encodeValue(quote, v, format, isArray) {
|
function encodeValue(quote, v, format, isArray) {
|
||||||
@@ -274,27 +274,32 @@ class QueryCursor {
|
|||||||
if (me.opts.debug) {
|
if (me.opts.debug) {
|
||||||
console.log('exec res headers', res.headers);
|
console.log('exec res headers', res.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._parseRowByFormat(res.body).then((result) => cb(null, me.useTotals ? result : result.data || result)).catch(cb);
|
try {
|
||||||
|
const result = this._parseRowsByFormat(res.body);
|
||||||
|
cb(null, me.useTotals ? result : result.data || result)
|
||||||
|
} catch (e) {
|
||||||
|
cb(e);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_parseRowByFormat(body) {
|
_parseRowsByFormat(body) {
|
||||||
let format = Promise.resolve();
|
let rows = null;
|
||||||
switch (this.opts.format) {
|
switch (this.opts.sessionFormat || this.opts.format) {
|
||||||
case "json":
|
case "json":
|
||||||
format = format.then(() => JSON.parse(body));
|
rows = parseJSON(body);
|
||||||
break;
|
break;
|
||||||
case "tsv":
|
case "tsv":
|
||||||
format = format.then(() => parseTSV(body));
|
rows = parseTSV(body);
|
||||||
break;
|
break;
|
||||||
case "csv":
|
case "csv":
|
||||||
format = format.then(() => parseCSV(body));
|
rows = parseCSV(body);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
format = format.then(() => body);
|
rows = body;
|
||||||
}
|
}
|
||||||
return format;
|
return rows;
|
||||||
};
|
};
|
||||||
|
|
||||||
withTotals() {
|
withTotals() {
|
||||||
@@ -448,7 +453,7 @@ class ClickHouse {
|
|||||||
output_format_json_quote_64bit_integers : 0,
|
output_format_json_quote_64bit_integers : 0,
|
||||||
enable_http_compression : 0
|
enable_http_compression : 0
|
||||||
},
|
},
|
||||||
format: "json" // "json" || "csv" || "tsv"
|
format: "json", // "json" || "csv" || "tsv"
|
||||||
},
|
},
|
||||||
opts
|
opts
|
||||||
);
|
);
|
||||||
@@ -554,10 +559,10 @@ class ClickHouse {
|
|||||||
format = this._parseFormat(query, " format JSON");
|
format = this._parseFormat(query, " format JSON");
|
||||||
break;
|
break;
|
||||||
case "tsv":
|
case "tsv":
|
||||||
format = this._parseFormat(query, " format TabSeparated");
|
format = this._parseFormat(query, " format TabSeparatedWithNames");
|
||||||
break;
|
break;
|
||||||
case "csv":
|
case "csv":
|
||||||
format = this._parseFormat(query, " format CSV");
|
format = this._parseFormat(query, " format CSVWithNames");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
format = " ";
|
format = " ";
|
||||||
@@ -565,19 +570,20 @@ class ClickHouse {
|
|||||||
return format;
|
return format;
|
||||||
};
|
};
|
||||||
|
|
||||||
_parseFormat(query, def) {
|
_parseFormat(query, def) {
|
||||||
if (query.match(/format/mg) === null) {
|
if (query.match(/format/mg) === null) {
|
||||||
return def;
|
this.opts.sessionFormat = this.opts.format;
|
||||||
}
|
return def;
|
||||||
if (query.match(/format JSON/mg) !== null) {
|
}
|
||||||
this.opts.format = "json";
|
if (query.match(/format JSON/mg) !== null) {
|
||||||
} else if (query.match(/format TabSeparated/mg) !== null) {
|
this.opts.sessionFormat = "json";
|
||||||
this.opts.format = "tsv";
|
} else if (query.match(/format TabSeparated/mg) !== null) {
|
||||||
} else if (query.match(/format CSV/mg) !== null) {
|
this.opts.sessionFormat = "tsv";
|
||||||
this.opts.format = "csv";
|
} else if (query.match(/format CSV/mg) !== null) {
|
||||||
}
|
this.opts.sessionFormat = "csv";
|
||||||
return "";
|
}
|
||||||
}
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
_mapRowAsObject(fieldList, row) {
|
_mapRowAsObject(fieldList, row) {
|
||||||
return fieldList.map(f => encodeValue(false, row[f] != null ? row[f] : '', 'TabSeparated')).join('\t');
|
return fieldList.map(f => encodeValue(false, row[f] != null ? row[f] : '', 'TabSeparated')).join('\t');
|
||||||
@@ -634,13 +640,10 @@ class ClickHouse {
|
|||||||
let sql = query.trim();
|
let sql = query.trim();
|
||||||
|
|
||||||
// Hack for Sequelize ORM
|
// Hack for Sequelize ORM
|
||||||
if (sql.charAt(sql.length - 1) === ';') {
|
sql = sql.trimEnd().replace(/;$/gm, "");
|
||||||
sql = sql.substr(0, sql.length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sql.match(/^(select|show|exists)/i)) {
|
if (sql.match(/^(select|show|exists)/i)) {
|
||||||
reqParams['url'] = me.url + '?query=' + encodeURIComponent(query + this._getFormat(query)) + '&' + querystring.stringify(configQS);
|
reqParams['url'] = me.url + '?query=' + encodeURIComponent(sql + this._getFormat(sql) + ';') + '&' + querystring.stringify(configQS);
|
||||||
|
|
||||||
if (me.opts.username) {
|
if (me.opts.username) {
|
||||||
reqParams['url'] = reqParams['url'] + '&user=' + me.opts.username;
|
reqParams['url'] = reqParams['url'] + '&user=' + me.opts.username;
|
||||||
}
|
}
|
||||||
@@ -667,7 +670,7 @@ class ClickHouse {
|
|||||||
reqParams['formData'] = formData;
|
reqParams['formData'] = formData;
|
||||||
}
|
}
|
||||||
} else if (query.match(/^insert/i)) {
|
} else if (query.match(/^insert/i)) {
|
||||||
reqParams['url'] = me.url + '?query=' + encodeURIComponent(query + ' FORMAT TabSeparated') + '&' + querystring.stringify(configQS);
|
reqParams['url'] = me.url + '?query=' + encodeURIComponent(sql + ' FORMAT TabSeparated') + '&' + querystring.stringify(configQS);
|
||||||
|
|
||||||
if (me.opts.username) {
|
if (me.opts.username) {
|
||||||
reqParams['url'] = reqParams['url'] + '&user=' + me.opts.username;
|
reqParams['url'] = reqParams['url'] + '&user=' + me.opts.username;
|
||||||
@@ -678,10 +681,10 @@ class ClickHouse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
reqParams['body'] = me._getBodyForInsert(query, data);
|
reqParams['body'] = me._getBodyForInsert(sql, data);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
reqParams['url'] = me.url + '?query=' + encodeURIComponent(query) + '&' + querystring.stringify(configQS);
|
reqParams['url'] = me.url + '?query=' + encodeURIComponent(sql + ";") + '&' + querystring.stringify(configQS);
|
||||||
|
|
||||||
if (me.opts.username) {
|
if (me.opts.username) {
|
||||||
reqParams['url'] = reqParams['url'] + '&user=' + me.opts.username;
|
reqParams['url'] = reqParams['url'] + '&user=' + me.opts.username;
|
||||||
@@ -726,6 +729,6 @@ class ClickHouse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
ClickHouse
|
ClickHouse
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
35
test/test.js
35
test/test.js
@@ -79,30 +79,30 @@ describe('Select', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('use callback with csv format', callback => {
|
it('use callback #3 with csv format', callback => {
|
||||||
clickhouse.query(`${sql} format CSV`).exec((err, rows) => {
|
clickhouse.query(`${sql} format CSVWithNames`).exec((err, rows) => {
|
||||||
expect(err).to.not.be.ok();
|
expect(err).to.not.be.ok();
|
||||||
|
|
||||||
expect(rows).to.have.length(rowCount);
|
expect(rows).to.have.length(rowCount);
|
||||||
expect(rows[0]).to.eql([0, '0', '1970-01-02' ]);
|
expect(rows[0]).to.eql({ number: 0, str: 0, date: '1970-01-02' });
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('use callback #2 with csv format', callback => {
|
it('use callback #4 with csv format', callback => {
|
||||||
clickhouse.query(`${sql} format CSV`, (err, rows) => {
|
clickhouse.query(`${sql} format CSVWithNames`, (err, rows) => {
|
||||||
expect(err).to.not.be.ok();
|
expect(err).to.not.be.ok();
|
||||||
|
|
||||||
expect(rows).to.have.length(rowCount);
|
expect(rows).to.have.length(rowCount);
|
||||||
expect(rows[0]).to.eql([0, '0', '1970-01-02' ]);
|
expect(rows[0]).to.eql({ number: 0, str: 0, date: '1970-01-02' });
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('use callback with tsv format', callback => {
|
it('use callback #5 with tsv format', callback => {
|
||||||
clickhouse.query(`${sql} format TabSeparatedWithNames`).exec((err, rows) => {
|
clickhouse.query(`${sql} format TabSeparatedWithNames`).exec((err, rows) => {
|
||||||
expect(err).to.not.be.ok();
|
expect(err).to.not.be.ok();
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ describe('Select', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('use callback #2 with tsv format', callback => {
|
it('use callback #6 with tsv format', callback => {
|
||||||
clickhouse.query(`${sql} format TabSeparatedWithNames`, (err, rows) => {
|
clickhouse.query(`${sql} format TabSeparatedWithNames`, (err, rows) => {
|
||||||
expect(err).to.not.be.ok();
|
expect(err).to.not.be.ok();
|
||||||
|
|
||||||
@@ -134,6 +134,7 @@ describe('Select', () => {
|
|||||||
|
|
||||||
clickhouse.query(sql).stream()
|
clickhouse.query(sql).stream()
|
||||||
.on('data', () => ++i)
|
.on('data', () => ++i)
|
||||||
|
// TODO: on this case you should catch error
|
||||||
.on('error', err => error = err)
|
.on('error', err => error = err)
|
||||||
.on('end', () => {
|
.on('end', () => {
|
||||||
expect(error).to.not.be.ok();
|
expect(error).to.not.be.ok();
|
||||||
@@ -525,6 +526,24 @@ describe('Constructor options', () => {
|
|||||||
new ClickHouse({
|
new ClickHouse({
|
||||||
host: 'http://localhost:8124',
|
host: 'http://localhost:8124',
|
||||||
port: 8123
|
port: 8123
|
||||||
|
}),
|
||||||
|
|
||||||
|
new ClickHouse({
|
||||||
|
host: 'http://localhost:8124',
|
||||||
|
port: 8123,
|
||||||
|
format: "json"
|
||||||
|
}),
|
||||||
|
|
||||||
|
new ClickHouse({
|
||||||
|
host: 'http://localhost:8124',
|
||||||
|
port: 8123,
|
||||||
|
format: "tsv"
|
||||||
|
}),
|
||||||
|
|
||||||
|
new ClickHouse({
|
||||||
|
host: 'http://localhost:8124',
|
||||||
|
port: 8123,
|
||||||
|
format: "csv"
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user