fix: InsertQueryBuilder performance issue for raw tables

Instead of using Arrays/Maps with lots of Object.keys() and join()
calls, it's much faster to progressively build the query like we
already do when the metadata is available.
This commit is contained in:
Thomas Sarlandie 2019-04-03 17:52:30 +02:00
parent 21134ceeaa
commit 9ed2de409c
2 changed files with 72 additions and 11 deletions

View File

@ -490,37 +490,55 @@ export class InsertQueryBuilder<Entity> extends QueryBuilder<Entity> {
return "";
return expression;
} else { // for tables without metadata
// get values needs to be inserted
return valueSets.map((valueSet, insertionIndex) => {
const columnValues = Object.keys(valueSet).map(columnName => {
let expression = "";
let parametersCount = Object.keys(this.expressionMap.nativeParameters).length;
valueSets.forEach((valueSet, insertionIndex) => {
const columns = Object.keys(valueSet);
columns.forEach((columnName, columnIndex) => {
if (columnIndex === 0) {
expression += "(";
}
const paramName = "i" + insertionIndex + "_" + columnName;
const value = valueSet[columnName];
// support for SQL expressions in queries
if (value instanceof Function) {
return value();
expression += value();
// if value for this column was not provided then insert default value
} else if (value === undefined) {
if (this.connection.driver instanceof AbstractSqliteDriver) {
return "NULL";
expression += "NULL";
} else {
return "DEFAULT";
expression += "DEFAULT";
}
// just any other regular value
} else {
this.expressionMap.nativeParameters[paramName] = value;
return this.connection.driver.createParameter(paramName, Object.keys(this.expressionMap.nativeParameters).length - 1);
expression += this.connection.driver.createParameter(paramName, parametersCount);
parametersCount++;
}
}).join(", ").trim();
return columnValues ? "(" + columnValues + ")" : "";
}).join(", ");
if (columnIndex === Object.keys(valueSet).length - 1) {
if (insertionIndex === valueSets.length - 1) {
expression += ")";
} else {
expression += "), ";
}
}
else {
expression += ", ";
}
});
});
if (expression === "()")
return "";
return expression;
}
}

View File

@ -0,0 +1,43 @@
import "reflect-metadata";
import {Connection} from "../../../src/connection/Connection";
import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../utils/test-utils";
import {Document} from "../bulk-save-case2/entity/Document";
describe("benchmark > bulk-save > case-querybuilder", () => {
let connections: Connection[];
before(async () => connections = await createTestingConnections({ __dirname, enabledDrivers: ["postgres"] }));
beforeEach(() => reloadTestingDatabases(connections));
after(() => closeTestingConnections(connections));
it("testing bulk save of 10000 objects", () => Promise.all(connections.map(async connection => {
const documents: Document[] = [];
for (let i = 0; i < 10000; i++) {
const document = new Document();
document.id = i.toString();
document.docId = "label/" + i;
document.context = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent vel faucibus nunc. Etiam volutpat vel urna in scelerisque. Cras a erat ipsum. ";
document.label = "label/" + i;
document.date = new Date();
documents.push(document);
}
await connection.createQueryRunner().query(`CREATE TABLE "document" ("id" text NOT NULL, "docId" text NOT NULL, "label" text NOT NULL, "context" text NOT NULL, "date" TIMESTAMP WITH TIME ZONE NOT NULL, CONSTRAINT "PK_e57d3357f83f3cdc0acffc3d777" PRIMARY KEY ("id"))`);
await connection.manager.createQueryBuilder()
.insert()
.into("document", [
"id",
"docId",
"label",
"context",
"date",
])
.values(documents)
.execute();
})));
});