@nymphjs/driver-postgresql 1.0.0-beta.97 → 1.0.0-beta.99

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -164,32 +164,27 @@ export default class PostgreSQLDriver extends NymphDriver {
164
164
  isConnected() {
165
165
  return this.connected;
166
166
  }
167
- /**
168
- * Create entity tables in the database.
169
- *
170
- * @param etype The entity type to create a table for. If this is blank, the default tables are created.
171
- * @returns True on success, false on failure.
172
- */
173
- async createTables(etype = null) {
174
- const connection = await this.getConnection(true);
175
- if (etype != null) {
176
- // Create the entity table.
177
- await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} (
167
+ async createEntitiesTable(etype, connection) {
168
+ // Create the entity table.
169
+ await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} (
178
170
  "guid" BYTEA NOT NULL,
179
171
  "tags" TEXT[],
180
172
  "cdate" DOUBLE PRECISION NOT NULL,
181
173
  "mdate" DOUBLE PRECISION NOT NULL,
182
174
  PRIMARY KEY ("guid")
183
175
  ) WITH ( OIDS=FALSE );`, { connection });
184
- await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`, { connection });
185
- await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_cdate`)};`, { connection });
186
- await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_cdate`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING btree ("cdate");`, { connection });
187
- await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_mdate`)};`, { connection });
188
- await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_mdate`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING btree ("mdate");`, { connection });
189
- await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_tags`)};`, { connection });
190
- await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_tags`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING gin ("tags");`, { connection });
191
- // Create the data table.
192
- await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} (
176
+ await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`, { connection });
177
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_cdate`)};`, { connection });
178
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_cdate`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING btree ("cdate");`, { connection });
179
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_mdate`)};`, { connection });
180
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_mdate`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING btree ("mdate");`, { connection });
181
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_tags`)};`, { connection });
182
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_tags`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING gin ("tags");`, { connection });
183
+ await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} SET ( autovacuum_vacuum_scale_factor = 0.05, autovacuum_analyze_scale_factor = 0.05 );`, { connection });
184
+ }
185
+ async createDataTable(etype, connection) {
186
+ // Create the data table.
187
+ await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} (
193
188
  "guid" BYTEA NOT NULL,
194
189
  "name" TEXT NOT NULL,
195
190
  "value" CHARACTER(1) NOT NULL,
@@ -201,31 +196,46 @@ export default class PostgreSQLDriver extends NymphDriver {
201
196
  FOREIGN KEY ("guid")
202
197
  REFERENCES ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
203
198
  ) WITH ( OIDS=FALSE );`, { connection });
204
- await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`, { connection });
205
- await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid`)};`, { connection });
206
- await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid");`, { connection });
207
- await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name`)};`, { connection });
208
- await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid", "name");`, { connection });
209
- await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name__user`)};`, { connection });
210
- await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name__user`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid") WHERE "name" = 'user'::text;`, { connection });
211
- await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name__group`)};`, { connection });
212
- await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name__group`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid") WHERE "name" = 'group'::text;`, { connection });
213
- await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name`)};`, { connection });
214
- await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("name");`, { connection });
215
- await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_string`)};`, { connection });
216
- await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_string`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("name", LEFT("string", 512));`, { connection });
217
- await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_number`)};`, { connection });
218
- await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_number`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("name", "number");`, { connection });
219
- await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_truthy`)};`, { connection });
220
- await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_truthy`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("name") WHERE "truthy" = TRUE;`, { connection });
221
- await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_falsy`)};`, { connection });
222
- await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_falsy`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("name") WHERE "truthy" <> TRUE;`, { connection });
223
- await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_string`)};`, { connection });
224
- await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_string`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING gin ("string" gin_trgm_ops);`, { connection });
225
- await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_json`)};`, { connection });
226
- await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_json`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING gin ("json");`, { connection });
227
- // Create the references table.
228
- await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} (
199
+ await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`, { connection });
200
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid`)};`, { connection });
201
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid");`, { connection });
202
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name`)};`, { connection });
203
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid", "name");`, { connection });
204
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name__user`)};`, { connection });
205
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name__user`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid") WHERE "name" = 'user'::text;`, { connection });
206
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name__group`)};`, { connection });
207
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name__group`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid") WHERE "name" = 'group'::text;`, { connection });
208
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name`)};`, { connection });
209
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("name");`, { connection });
210
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_string`)};`, { connection });
211
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_string`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("name", LEFT("string", 512));`, { connection });
212
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_number`)};`, { connection });
213
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_number`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("name", "number");`, { connection });
214
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name_number`)};`, { connection });
215
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name_number`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid", "name", "number");`, { connection });
216
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_truthy`)};`, { connection });
217
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_truthy`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("name", "truthy");`, { connection });
218
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name_truthy`)};`, { connection });
219
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name_truthy`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid", "name", "truthy");`, { connection });
220
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_string`)};`, { connection });
221
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_string`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING gin ("string" gin_trgm_ops);`, { connection });
222
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_json`)};`, { connection });
223
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_json`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING gin ("json");`, { connection });
224
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_acuserread`)};`, { connection });
225
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_acuserread`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid") WHERE "name"='acUser' AND "number" >= 1;`, { connection });
226
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_acgroupread`)};`, { connection });
227
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_acgroupread`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid") WHERE "name"='acGroup' AND "number" >= 1;`, { connection });
228
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_acotherread`)};`, { connection });
229
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_acotherread`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid") WHERE "name"='acOther' AND "number" >= 1;`, { connection });
230
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_acuser`)};`, { connection });
231
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_acuser`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid") WHERE "name"='user';`, { connection });
232
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_acgroup`)};`, { connection });
233
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_acgroup`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid") WHERE "name"='group';`, { connection });
234
+ await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} SET ( autovacuum_vacuum_scale_factor = 0.05, autovacuum_analyze_scale_factor = 0.05 );`, { connection });
235
+ }
236
+ async createReferencesTable(etype, connection) {
237
+ // Create the references table.
238
+ await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} (
229
239
  "guid" BYTEA NOT NULL,
230
240
  "name" TEXT NOT NULL,
231
241
  "reference" BYTEA NOT NULL,
@@ -233,21 +243,69 @@ export default class PostgreSQLDriver extends NymphDriver {
233
243
  FOREIGN KEY ("guid")
234
244
  REFERENCES ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
235
245
  ) WITH ( OIDS=FALSE );`, { connection });
236
- await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`, { connection });
237
- await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid`)};`, { connection });
238
- await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("guid");`, { connection });
239
- await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_name`)};`, { connection });
240
- await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_name`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("name");`, { connection });
241
- await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_name_reference`)};`, { connection });
242
- await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_name_reference`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("name", "reference");`, { connection });
243
- // Create the unique strings table.
244
- await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} (
246
+ await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`, { connection });
247
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid`)};`, { connection });
248
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("guid");`, { connection });
249
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_name`)};`, { connection });
250
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_name`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("name");`, { connection });
251
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_name_reference`)};`, { connection });
252
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_name_reference`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("name", "reference");`, { connection });
253
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_reference`)};`, { connection });
254
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_reference`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("reference");`, { connection });
255
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid_name_reference`)};`, { connection });
256
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid_name_reference`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("guid", "name", "reference");`, { connection });
257
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_reference_name_guid`)};`, { connection });
258
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_reference_name_guid`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("reference", "name", "guid");`, { connection });
259
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_reference_guid_name`)};`, { connection });
260
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_reference_guid_name`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("reference", "guid", "name");`, { connection });
261
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid_reference_nameuser`)};`, { connection });
262
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid_reference_nameuser`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("guid", "reference") WHERE "name"='user';`, { connection });
263
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid_reference_namegroup`)};`, { connection });
264
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid_reference_namegroup`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("guid", "reference") WHERE "name"='group';`, { connection });
265
+ await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} SET ( autovacuum_vacuum_scale_factor = 0.05, autovacuum_analyze_scale_factor = 0.05 );`, { connection });
266
+ }
267
+ async createTokensTable(etype, connection) {
268
+ // Create the tokens table.
269
+ await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} (
270
+ "guid" BYTEA NOT NULL,
271
+ "name" TEXT NOT NULL,
272
+ "token" INTEGER NOT NULL,
273
+ "position" INTEGER NOT NULL,
274
+ "stem" BOOLEAN NOT NULL,
275
+ PRIMARY KEY ("guid", "name", "token", "position"),
276
+ FOREIGN KEY ("guid")
277
+ REFERENCES ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
278
+ ) WITH ( OIDS=FALSE );`, { connection });
279
+ await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`, { connection });
280
+ await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}_id_name_token`)};`, { connection });
281
+ await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}_id_name_token`)} ON ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} USING btree ("name", "token");`, { connection });
282
+ await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} SET ( autovacuum_vacuum_scale_factor = 0.05, autovacuum_analyze_scale_factor = 0.05 );`, { connection });
283
+ }
284
+ async createUniquesTable(etype, connection) {
285
+ // Create the unique strings table.
286
+ await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} (
245
287
  "guid" BYTEA NOT NULL,
246
288
  "unique" TEXT NOT NULL UNIQUE,
247
289
  PRIMARY KEY ("guid", "unique"),
248
290
  FOREIGN KEY ("guid")
249
291
  REFERENCES ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
250
292
  ) WITH ( OIDS=FALSE );`, { connection });
293
+ await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`, { connection });
294
+ }
295
+ /**
296
+ * Create entity tables in the database.
297
+ *
298
+ * @param etype The entity type to create a table for. If this is blank, the default tables are created.
299
+ * @returns True on success, false on failure.
300
+ */
301
+ async createTables(etype = null) {
302
+ const connection = await this.getConnection(true);
303
+ if (etype != null) {
304
+ await this.createEntitiesTable(etype, connection);
305
+ await this.createDataTable(etype, connection);
306
+ await this.createReferencesTable(etype, connection);
307
+ await this.createTokensTable(etype, connection);
308
+ await this.createUniquesTable(etype, connection);
251
309
  }
252
310
  else {
253
311
  // Add trigram extensions.
@@ -432,6 +490,12 @@ export default class PostgreSQLDriver extends NymphDriver {
432
490
  guid,
433
491
  },
434
492
  });
493
+ await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
494
+ etypes: [etype],
495
+ params: {
496
+ guid,
497
+ },
498
+ });
435
499
  await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
436
500
  etypes: [etype],
437
501
  params: {
@@ -462,6 +526,19 @@ export default class PostgreSQLDriver extends NymphDriver {
462
526
  });
463
527
  return true;
464
528
  }
529
+ async getEtypes() {
530
+ const tables = await this.queryArray('SELECT "table_name" AS "table_name" FROM "information_schema"."tables" WHERE "table_catalog"=@db AND "table_schema"=\'public\' AND "table_name" LIKE @prefix;', {
531
+ params: {
532
+ db: this.config.database,
533
+ prefix: this.prefix + 'entities_' + '%',
534
+ },
535
+ });
536
+ const etypes = [];
537
+ for (const table of tables) {
538
+ etypes.push(table.table_name.substr((this.prefix + 'entities_').length));
539
+ }
540
+ return etypes;
541
+ }
465
542
  async *exportDataIterator() {
466
543
  if (yield {
467
544
  type: 'comment',
@@ -506,16 +583,7 @@ export default class PostgreSQLDriver extends NymphDriver {
506
583
  return;
507
584
  }
508
585
  // Get the etypes.
509
- const tables = await this.queryArray('SELECT "table_name" AS "table_name" FROM "information_schema"."tables" WHERE "table_catalog"=@db AND "table_schema"=\'public\' AND "table_name" LIKE @prefix;', {
510
- params: {
511
- db: this.config.database,
512
- prefix: this.prefix + 'entities_' + '%',
513
- },
514
- });
515
- const etypes = [];
516
- for (const table of tables) {
517
- etypes.push(table.table_name.substr((this.prefix + 'entities_').length));
518
- }
586
+ const etypes = await this.getEtypes();
519
587
  for (const etype of etypes) {
520
588
  // Export entities.
521
589
  const dataIterator = await this.queryIter(`SELECT encode(e."guid", 'hex') AS "guid", e."tags", e."cdate", e."mdate", d."name", d."value", d."json", d."string", d."number"
@@ -839,6 +907,125 @@ export default class PostgreSQLDriver extends NymphDriver {
839
907
  params[value] = PostgreSQLDriver.escapeNullSequences(svalue);
840
908
  }
841
909
  break;
910
+ case 'search':
911
+ case '!search':
912
+ if (curValue[0] === 'cdate' || curValue[0] === 'mdate') {
913
+ if (curQuery) {
914
+ curQuery += typeIsOr ? ' OR ' : ' AND ';
915
+ }
916
+ curQuery +=
917
+ (xor(typeIsNot, clauseNot) ? 'NOT ' : '') + '(FALSE)';
918
+ break;
919
+ }
920
+ else {
921
+ if (curQuery) {
922
+ curQuery += typeIsOr ? ' OR ' : ' AND ';
923
+ }
924
+ const name = `param${++count.i}`;
925
+ const queryPartToken = (term) => {
926
+ const value = `param${++count.i}`;
927
+ params[value] = term.token;
928
+ return ('EXISTS (SELECT "guid" FROM ' +
929
+ PostgreSQLDriver.escape(this.prefix + 'tokens_' + etype) +
930
+ ' WHERE "guid"=' +
931
+ ieTable +
932
+ '."guid" AND "name"=@' +
933
+ name +
934
+ ' AND "token"=@' +
935
+ value +
936
+ (term.nostemmed ? ' AND "stem"=FALSE' : '') +
937
+ ')');
938
+ };
939
+ const queryPartSeries = (series) => {
940
+ const tokenTableSuffix = makeTableSuffix();
941
+ const tokenParts = series.tokens.map((token, i) => {
942
+ const value = `param${++count.i}`;
943
+ params[value] = token.token;
944
+ return {
945
+ fromClause: i === 0
946
+ ? 'FROM ' +
947
+ PostgreSQLDriver.escape(this.prefix + 'tokens_' + etype) +
948
+ ' t' +
949
+ tokenTableSuffix +
950
+ '0'
951
+ : 'JOIN ' +
952
+ PostgreSQLDriver.escape(this.prefix + 'tokens_' + etype) +
953
+ ' t' +
954
+ tokenTableSuffix +
955
+ i +
956
+ ' ON t' +
957
+ tokenTableSuffix +
958
+ i +
959
+ '."guid" = t' +
960
+ tokenTableSuffix +
961
+ '0."guid" AND t' +
962
+ tokenTableSuffix +
963
+ i +
964
+ '."name" = t' +
965
+ tokenTableSuffix +
966
+ '0."name" AND t' +
967
+ tokenTableSuffix +
968
+ i +
969
+ '."position" = t' +
970
+ tokenTableSuffix +
971
+ '0."position" + ' +
972
+ i,
973
+ whereClause: 't' +
974
+ tokenTableSuffix +
975
+ i +
976
+ '."token"=@' +
977
+ value +
978
+ (token.nostemmed
979
+ ? ' AND t' + tokenTableSuffix + i + '."stem"=FALSE'
980
+ : ''),
981
+ };
982
+ });
983
+ return ('EXISTS (SELECT t' +
984
+ tokenTableSuffix +
985
+ '0."guid" ' +
986
+ tokenParts.map((part) => part.fromClause).join(' ') +
987
+ ' WHERE t' +
988
+ tokenTableSuffix +
989
+ '0."guid"=' +
990
+ ieTable +
991
+ '."guid" AND t' +
992
+ tokenTableSuffix +
993
+ '0."name"=@' +
994
+ name +
995
+ ' AND ' +
996
+ tokenParts.map((part) => part.whereClause).join(' AND ') +
997
+ ')');
998
+ };
999
+ const queryPartTerm = (term) => {
1000
+ if (term.type === 'series') {
1001
+ return queryPartSeries(term);
1002
+ }
1003
+ else if (term.type === 'not') {
1004
+ return 'NOT ' + queryPartTerm(term.operand);
1005
+ }
1006
+ else if (term.type === 'or') {
1007
+ let queryParts = [];
1008
+ for (let operand of term.operands) {
1009
+ queryParts.push(queryPartTerm(operand));
1010
+ }
1011
+ return '(' + queryParts.join(' OR ') + ')';
1012
+ }
1013
+ return queryPartToken(term);
1014
+ };
1015
+ const parsedFTSQuery = this.tokenizer.parseSearchQuery(curValue[1]);
1016
+ // Run through the query and add terms.
1017
+ let termStrings = [];
1018
+ for (let term of parsedFTSQuery) {
1019
+ termStrings.push(queryPartTerm(term));
1020
+ }
1021
+ curQuery +=
1022
+ (xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
1023
+ '(' +
1024
+ termStrings.join(' AND ') +
1025
+ ')';
1026
+ params[name] = curValue[0];
1027
+ }
1028
+ break;
842
1029
  case 'match':
843
1030
  case '!match':
844
1031
  if (curValue[0] === 'cdate') {
@@ -1584,100 +1771,163 @@ export default class PostgreSQLDriver extends NymphDriver {
1584
1771
  });
1585
1772
  return result?.cur_uid == null ? null : Number(result.cur_uid);
1586
1773
  }
1587
- async importEntity({ guid, cdate, mdate, tags, sdata, etype, }) {
1774
+ async importEntity(entity) {
1775
+ return await this.importEntityInternal(entity, false);
1776
+ }
1777
+ async importEntityTokens(entity) {
1778
+ return await this.importEntityInternal(entity, true);
1779
+ }
1780
+ async importEntityInternal({ guid, cdate, mdate, tags, sdata, etype, }, onlyTokens) {
1588
1781
  try {
1589
1782
  let promises = [];
1590
- promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
1591
- etypes: [etype],
1592
- params: {
1593
- guid,
1594
- },
1595
- }));
1596
- promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
1597
- etypes: [etype],
1598
- params: {
1599
- guid,
1600
- },
1601
- }));
1602
- promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
1603
- etypes: [etype],
1604
- params: {
1605
- guid,
1606
- },
1607
- }));
1608
- promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
1783
+ if (!onlyTokens) {
1784
+ promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
1785
+ etypes: [etype],
1786
+ params: {
1787
+ guid,
1788
+ },
1789
+ }));
1790
+ promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
1791
+ etypes: [etype],
1792
+ params: {
1793
+ guid,
1794
+ },
1795
+ }));
1796
+ promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
1797
+ etypes: [etype],
1798
+ params: {
1799
+ guid,
1800
+ },
1801
+ }));
1802
+ }
1803
+ promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
1609
1804
  etypes: [etype],
1610
1805
  params: {
1611
1806
  guid,
1612
1807
  },
1613
1808
  }));
1809
+ if (!onlyTokens) {
1810
+ promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
1811
+ etypes: [etype],
1812
+ params: {
1813
+ guid,
1814
+ },
1815
+ }));
1816
+ }
1614
1817
  await Promise.all(promises);
1615
1818
  promises = [];
1616
- await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid", "tags", "cdate", "mdate") VALUES (decode(@guid, 'hex'), @tags, @cdate, @mdate);`, {
1617
- etypes: [etype],
1618
- params: {
1619
- guid,
1620
- tags,
1621
- cdate: isNaN(cdate) ? null : cdate,
1622
- mdate: isNaN(mdate) ? null : mdate,
1623
- },
1624
- });
1625
- for (const name in sdata) {
1626
- const value = sdata[name];
1627
- const uvalue = JSON.parse(value);
1628
- if (value === undefined) {
1629
- continue;
1630
- }
1631
- const storageValue = typeof uvalue === 'number'
1632
- ? 'N'
1633
- : typeof uvalue === 'string'
1634
- ? 'S'
1635
- : 'J';
1636
- const jsonValue = storageValue === 'J'
1637
- ? PostgreSQLDriver.escapeNullSequences(value)
1638
- : null;
1639
- promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ("guid", "name", "value", "json", "string", "number", "truthy") VALUES (decode(@guid, 'hex'), @name, @storageValue, @jsonValue, @string, @number, @truthy);`, {
1819
+ if (!onlyTokens) {
1820
+ await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid", "tags", "cdate", "mdate") VALUES (decode(@guid, 'hex'), @tags, @cdate, @mdate);`, {
1640
1821
  etypes: [etype],
1641
1822
  params: {
1642
1823
  guid,
1643
- name,
1644
- storageValue,
1645
- jsonValue,
1646
- string: storageValue === 'J'
1647
- ? null
1648
- : PostgreSQLDriver.escapeNulls(`${uvalue}`),
1649
- number: isNaN(Number(uvalue)) ? null : Number(uvalue),
1650
- truthy: !!uvalue,
1824
+ tags,
1825
+ cdate: isNaN(cdate) ? null : cdate,
1826
+ mdate: isNaN(mdate) ? null : mdate,
1651
1827
  },
1652
- }));
1653
- const references = this.findReferences(value);
1654
- for (const reference of references) {
1655
- promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} ("guid", "name", "reference") VALUES (decode(@guid, 'hex'), @name, decode(@reference, 'hex'));`, {
1828
+ });
1829
+ for (const name in sdata) {
1830
+ const value = sdata[name];
1831
+ const uvalue = JSON.parse(value);
1832
+ if (value === undefined) {
1833
+ continue;
1834
+ }
1835
+ const storageValue = typeof uvalue === 'number'
1836
+ ? 'N'
1837
+ : typeof uvalue === 'string'
1838
+ ? 'S'
1839
+ : 'J';
1840
+ const jsonValue = storageValue === 'J'
1841
+ ? PostgreSQLDriver.escapeNullSequences(value)
1842
+ : null;
1843
+ promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ("guid", "name", "value", "json", "string", "number", "truthy") VALUES (decode(@guid, 'hex'), @name, @storageValue, @jsonValue, @string, @number, @truthy);`, {
1656
1844
  etypes: [etype],
1657
1845
  params: {
1658
1846
  guid,
1659
1847
  name,
1660
- reference,
1848
+ storageValue,
1849
+ jsonValue,
1850
+ string: storageValue === 'J'
1851
+ ? null
1852
+ : PostgreSQLDriver.escapeNulls(`${uvalue}`),
1853
+ number: isNaN(Number(uvalue)) ? null : Number(uvalue),
1854
+ truthy: !!uvalue,
1661
1855
  },
1662
1856
  }));
1857
+ const references = this.findReferences(value);
1858
+ for (const reference of references) {
1859
+ promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} ("guid", "name", "reference") VALUES (decode(@guid, 'hex'), @name, decode(@reference, 'hex'));`, {
1860
+ etypes: [etype],
1861
+ params: {
1862
+ guid,
1863
+ name,
1864
+ reference,
1865
+ },
1866
+ }));
1867
+ }
1663
1868
  }
1664
1869
  }
1665
- const uniques = await this.nymph
1666
- .getEntityClassByEtype(etype)
1667
- .getUniques({ guid, cdate, mdate, tags, data: {}, sdata });
1668
- for (const unique of uniques) {
1669
- promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} ("guid", "unique") VALUES (decode(@guid, 'hex'), @unique);`, {
1670
- etypes: [etype],
1671
- params: {
1672
- guid,
1673
- unique,
1674
- },
1675
- }).catch((e) => {
1676
- if (e instanceof EntityUniqueConstraintError) {
1677
- this.nymph.config.debugError('postgresql', `Import entity unique constraint violation for GUID "${guid}" on etype "${etype}": "${unique}"`);
1870
+ const EntityClass = this.nymph.getEntityClassByEtype(etype);
1871
+ for (let name in sdata) {
1872
+ let tokenString = null;
1873
+ try {
1874
+ tokenString = EntityClass.getFTSText(name, JSON.parse(sdata[name]));
1875
+ }
1876
+ catch (e) {
1877
+ // Ignore error.
1878
+ }
1879
+ if (tokenString != null) {
1880
+ const tokens = this.tokenizer.tokenize(tokenString);
1881
+ while (tokens.length) {
1882
+ const currentTokens = tokens.splice(0, 100);
1883
+ const params = {
1884
+ guid,
1885
+ name,
1886
+ };
1887
+ const values = [];
1888
+ for (let i = 0; i < currentTokens.length; i++) {
1889
+ const token = currentTokens[i];
1890
+ params['token' + i] = token.token;
1891
+ params['position' + i] = token.position;
1892
+ params['stem' + i] = token.stem;
1893
+ values.push("(decode(@guid, 'hex'), @name, @token" +
1894
+ i +
1895
+ ', @position' +
1896
+ i +
1897
+ ', @stem' +
1898
+ i +
1899
+ ')');
1900
+ }
1901
+ promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} ("guid", "name", "token", "position", "stem") VALUES ${values.join(', ')};`, {
1902
+ etypes: [etype],
1903
+ params,
1904
+ }));
1678
1905
  }
1679
- return e;
1680
- }));
1906
+ }
1907
+ }
1908
+ if (!onlyTokens) {
1909
+ const uniques = await EntityClass.getUniques({
1910
+ guid,
1911
+ cdate,
1912
+ mdate,
1913
+ tags,
1914
+ data: {},
1915
+ sdata,
1916
+ });
1917
+ for (const unique of uniques) {
1918
+ promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} ("guid", "unique") VALUES (decode(@guid, 'hex'), @unique);`, {
1919
+ etypes: [etype],
1920
+ params: {
1921
+ guid,
1922
+ unique,
1923
+ },
1924
+ }).catch((e) => {
1925
+ if (e instanceof EntityUniqueConstraintError) {
1926
+ this.nymph.config.debugError('postgresql', `Import entity unique constraint violation for GUID "${guid}" on etype "${etype}": "${unique}"`);
1927
+ }
1928
+ return e;
1929
+ }));
1930
+ }
1681
1931
  }
1682
1932
  await Promise.all(promises);
1683
1933
  }
@@ -1780,6 +2030,7 @@ export default class PostgreSQLDriver extends NymphDriver {
1780
2030
  }
1781
2031
  async saveEntity(entity) {
1782
2032
  const insertData = async (guid, data, sdata, uniques, etype) => {
2033
+ const EntityClass = this.nymph.getEntityClassByEtype(etype);
1783
2034
  const runInsertQuery = async (name, value, svalue) => {
1784
2035
  if (value === undefined) {
1785
2036
  return;
@@ -1818,6 +2069,41 @@ export default class PostgreSQLDriver extends NymphDriver {
1818
2069
  },
1819
2070
  }));
1820
2071
  }
2072
+ let tokenString = null;
2073
+ try {
2074
+ tokenString = EntityClass.getFTSText(name, value);
2075
+ }
2076
+ catch (e) {
2077
+ // Ignore error.
2078
+ }
2079
+ if (tokenString != null) {
2080
+ const tokens = this.tokenizer.tokenize(tokenString);
2081
+ while (tokens.length) {
2082
+ const currentTokens = tokens.splice(0, 100);
2083
+ const params = {
2084
+ guid,
2085
+ name,
2086
+ };
2087
+ const values = [];
2088
+ for (let i = 0; i < currentTokens.length; i++) {
2089
+ const token = currentTokens[i];
2090
+ params['token' + i] = token.token;
2091
+ params['position' + i] = token.position;
2092
+ params['stem' + i] = token.stem;
2093
+ values.push("(decode(@guid, 'hex'), @name, @token" +
2094
+ i +
2095
+ ', @position' +
2096
+ i +
2097
+ ', @stem' +
2098
+ i +
2099
+ ')');
2100
+ }
2101
+ promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} ("guid", "name", "token", "position", "stem") VALUES ${values.join(', ')};`, {
2102
+ etypes: [etype],
2103
+ params,
2104
+ }));
2105
+ }
2106
+ }
1821
2107
  await Promise.all(promises);
1822
2108
  };
1823
2109
  for (const unique of uniques) {
@@ -1885,6 +2171,12 @@ export default class PostgreSQLDriver extends NymphDriver {
1885
2171
  guid,
1886
2172
  },
1887
2173
  }));
2174
+ promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} WHERE "guid"=decode(@guid, 'hex') FOR UPDATE;`, {
2175
+ etypes: [etype],
2176
+ params: {
2177
+ guid,
2178
+ },
2179
+ }));
1888
2180
  promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex') FOR UPDATE;`, {
1889
2181
  etypes: [etype],
1890
2182
  params: {
@@ -1916,6 +2208,12 @@ export default class PostgreSQLDriver extends NymphDriver {
1916
2208
  guid,
1917
2209
  },
1918
2210
  }));
2211
+ promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
2212
+ etypes: [etype],
2213
+ params: {
2214
+ guid,
2215
+ },
2216
+ }));
1919
2217
  promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
1920
2218
  etypes: [etype],
1921
2219
  params: {
@@ -2019,9 +2317,28 @@ export default class PostgreSQLDriver extends NymphDriver {
2019
2317
  table: table.table_name,
2020
2318
  },
2021
2319
  });
2022
- return !result?.exists;
2320
+ if (!result?.exists) {
2321
+ return 'json';
2322
+ }
2323
+ }
2324
+ const table2 = await this.queryGet('SELECT "table_name" AS "table_name" FROM "information_schema"."tables" WHERE "table_catalog"=@db AND "table_schema"=\'public\' AND "table_name" LIKE @tokenTable LIMIT 1;', {
2325
+ params: {
2326
+ db: this.config.database,
2327
+ tokenTable: this.prefix + 'tokens_' + '%',
2328
+ },
2329
+ });
2330
+ if (!table2 || !table2.table_name) {
2331
+ return 'tokens';
2023
2332
  }
2024
2333
  return false;
2025
2334
  }
2335
+ async liveMigration(_migrationType) {
2336
+ const etypes = await this.getEtypes();
2337
+ const connection = await this.getConnection(true);
2338
+ for (let etype of etypes) {
2339
+ await this.createTokensTable(etype, connection);
2340
+ }
2341
+ connection.done();
2342
+ }
2026
2343
  }
2027
2344
  //# sourceMappingURL=PostgreSQLDriver.js.map