@backstage/plugin-search-backend-module-pg 0.3.5 → 0.4.0-next.0

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # @backstage/plugin-search-backend-module-pg
2
2
 
3
+ ## 0.4.0-next.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 8872cc735d: Fixed a bug in search-backend-module-pg where it ignores the skip migration database options when using the database.
8
+
9
+ To use this new implementation you need to create the instance of `DatabaseDocumentStore` using the `PluginDatabaseManager` instead of `Knex`;
10
+
11
+ ```
12
+ import { DatabaseManager, getRootLogger, loadBackendConfig } from '@backstage/backend-common';
13
+ import { DatabaseDocumentStore } from '@backstage/plugin-search-backend-module-pg';
14
+
15
+ const config = await loadBackendConfig({ argv: process.argv, logger: getRootLogger() });
16
+ const databaseManager = DatabaseManager.fromConfig(config, { migrations: { skip: true } });
17
+ const databaseDocumentStore = await DatabaseDocumentStore.create(databaseManager);
18
+ ```
19
+
20
+ ### Patch Changes
21
+
22
+ - Updated dependencies
23
+ - @backstage/backend-common@0.15.1-next.0
24
+ - @backstage/plugin-search-backend-node@1.0.2-next.0
25
+ - @backstage/plugin-search-common@1.0.1-next.0
26
+
27
+ ## 0.3.6
28
+
29
+ ### Patch Changes
30
+
31
+ - Updated dependencies
32
+ - @backstage/backend-common@0.15.0
33
+ - @backstage/plugin-search-backend-node@1.0.1
34
+
35
+ ## 0.3.6-next.0
36
+
37
+ ### Patch Changes
38
+
39
+ - Updated dependencies
40
+ - @backstage/backend-common@0.15.0-next.0
41
+ - @backstage/plugin-search-backend-node@1.0.1-next.0
42
+
3
43
  ## 0.3.5
4
44
 
5
45
  ### Patch Changes
package/dist/index.cjs.js CHANGED
@@ -17,23 +17,34 @@ async function queryPostgresMajorVersion(knex) {
17
17
  return majorVersion;
18
18
  }
19
19
 
20
- const migrationsDir = backendCommon.resolvePackagePath("@backstage/plugin-search-backend-module-pg", "migrations");
20
+ const migrationsDir = backendCommon.resolvePackagePath(
21
+ "@backstage/plugin-search-backend-module-pg",
22
+ "migrations"
23
+ );
21
24
  class DatabaseDocumentStore {
22
25
  constructor(db) {
23
26
  this.db = db;
24
27
  }
25
- static async create(knex) {
28
+ static async create(database) {
29
+ var _a;
30
+ const knex = await database.getClient();
26
31
  try {
27
32
  const majorVersion = await queryPostgresMajorVersion(knex);
28
33
  if (majorVersion < 12) {
29
- throw new Error(`The PgSearchEngine requires at least postgres version 12 (but is running on ${majorVersion})`);
34
+ throw new Error(
35
+ `The PgSearchEngine requires at least postgres version 12 (but is running on ${majorVersion})`
36
+ );
30
37
  }
31
38
  } catch {
32
- throw new Error("The PgSearchEngine is only supported when using a postgres database (>=12.x)");
39
+ throw new Error(
40
+ "The PgSearchEngine is only supported when using a postgres database (>=12.x)"
41
+ );
42
+ }
43
+ if (!((_a = database.migrations) == null ? void 0 : _a.skip)) {
44
+ await knex.migrate.latest({
45
+ directory: migrationsDir
46
+ });
33
47
  }
34
- await knex.migrate.latest({
35
- directory: migrationsDir
36
- });
37
48
  return new DatabaseDocumentStore(knex);
38
49
  }
39
50
  static async supported(knex) {
@@ -51,17 +62,30 @@ class DatabaseDocumentStore {
51
62
  return this.db.transaction();
52
63
  }
53
64
  async prepareInsert(tx) {
54
- await tx.raw("CREATE TEMP TABLE documents_to_insert (type text NOT NULL, document jsonb NOT NULL, hash bytea NOT NULL GENERATED ALWAYS AS (sha256(replace(document::text || type, '\\', '\\\\')::bytea)) STORED) ON COMMIT DROP");
65
+ await tx.raw(
66
+ "CREATE TEMP TABLE documents_to_insert (type text NOT NULL, document jsonb NOT NULL, hash bytea NOT NULL GENERATED ALWAYS AS (sha256(replace(document::text || type, '\\', '\\\\')::bytea)) STORED) ON COMMIT DROP"
67
+ );
55
68
  }
56
69
  async completeInsert(tx, type) {
57
- await tx.insert(tx("documents_to_insert").select("type", "document", "hash")).into(tx.raw("documents (type, document, hash)")).onConflict("hash").ignore();
58
- await tx("documents").where({ type }).whereNotIn("hash", tx("documents_to_insert").select("hash")).delete();
70
+ await tx.insert(
71
+ tx("documents_to_insert").select(
72
+ "type",
73
+ "document",
74
+ "hash"
75
+ )
76
+ ).into(tx.raw("documents (type, document, hash)")).onConflict("hash").ignore();
77
+ await tx("documents").where({ type }).whereNotIn(
78
+ "hash",
79
+ tx("documents_to_insert").select("hash")
80
+ ).delete();
59
81
  }
60
82
  async insertDocuments(tx, type, documents) {
61
- await tx("documents_to_insert").insert(documents.map((document) => ({
62
- type,
63
- document
64
- })));
83
+ await tx("documents_to_insert").insert(
84
+ documents.map((document) => ({
85
+ type,
86
+ document
87
+ }))
88
+ );
65
89
  }
66
90
  async query(tx, searchQuery) {
67
91
  const { types, pgTerm, fields, offset, limit, options } = searchQuery;
@@ -79,13 +103,20 @@ class DatabaseDocumentStore {
79
103
  const value = fields[key];
80
104
  const valueArray = Array.isArray(value) ? value : [value];
81
105
  const valueCompare = valueArray.map((v) => ({ [key]: v })).map((v) => JSON.stringify(v));
82
- query.whereRaw(`(${valueCompare.map(() => "document @> ?").join(" OR ")})`, valueCompare);
106
+ query.whereRaw(
107
+ `(${valueCompare.map(() => "document @> ?").join(" OR ")})`,
108
+ valueCompare
109
+ );
83
110
  });
84
111
  }
85
112
  query.select("type", "document");
86
113
  if (pgTerm && options.useHighlight) {
87
114
  const headlineOptions = `MaxWords=${options.maxWords}, MinWords=${options.minWords}, ShortWord=${options.shortWord}, HighlightAll=${options.highlightAll}, MaxFragments=${options.maxFragments}, FragmentDelimiter=${options.fragmentDelimiter}, StartSel=${options.preTag}, StopSel=${options.postTag}`;
88
- query.select(tx.raw('ts_rank_cd(body, query) AS "rank"')).select(tx.raw(`ts_headline('english', document, query, '${headlineOptions}') as "highlight"`)).orderBy("rank", "desc");
115
+ query.select(tx.raw('ts_rank_cd(body, query) AS "rank"')).select(
116
+ tx.raw(
117
+ `ts_headline('english', document, query, '${headlineOptions}') as "highlight"`
118
+ )
119
+ ).orderBy("rank", "desc");
89
120
  } else if (pgTerm && !options.useHighlight) {
90
121
  query.select(tx.raw('ts_rank_cd(body, query) AS "rank"')).orderBy("rank", "desc");
91
122
  } else {
@@ -134,7 +165,9 @@ class PgSearchEngine {
134
165
  this.databaseStore = databaseStore;
135
166
  var _a, _b, _c, _d, _e, _f, _g;
136
167
  const uuidTag = uuid.v4();
137
- const highlightConfig = config.getOptionalConfig("search.pg.highlightOptions");
168
+ const highlightConfig = config.getOptionalConfig(
169
+ "search.pg.highlightOptions"
170
+ );
138
171
  const highlightOptions = {
139
172
  preTag: `<${uuidTag}>`,
140
173
  postTag: `</${uuidTag}>`,
@@ -149,10 +182,16 @@ class PgSearchEngine {
149
182
  this.highlightOptions = highlightOptions;
150
183
  }
151
184
  static async from(options) {
152
- return new PgSearchEngine(await DatabaseDocumentStore.create(await options.database.getClient()), options.config);
185
+ return new PgSearchEngine(
186
+ await DatabaseDocumentStore.create(options.database),
187
+ options.config
188
+ );
153
189
  }
154
190
  static async fromConfig(config, options) {
155
- return new PgSearchEngine(await DatabaseDocumentStore.create(await options.database.getClient()), config);
191
+ return new PgSearchEngine(
192
+ await DatabaseDocumentStore.create(options.database),
193
+ config
194
+ );
156
195
  }
157
196
  static async supported(database) {
158
197
  return await DatabaseDocumentStore.supported(await database.getClient());
@@ -188,28 +227,32 @@ class PgSearchEngine {
188
227
  const { pgQuery, pageSize } = this.translator(query, {
189
228
  highlightOptions: this.highlightOptions
190
229
  });
191
- const rows = await this.databaseStore.transaction(async (tx) => this.databaseStore.query(tx, pgQuery));
230
+ const rows = await this.databaseStore.transaction(
231
+ async (tx) => this.databaseStore.query(tx, pgQuery)
232
+ );
192
233
  const { page } = decodePageCursor(query.pageCursor);
193
234
  const hasNextPage = rows.length > pageSize;
194
235
  const hasPreviousPage = page > 0;
195
236
  const pageRows = rows.slice(0, pageSize);
196
237
  const nextPageCursor = hasNextPage ? encodePageCursor({ page: page + 1 }) : void 0;
197
238
  const previousPageCursor = hasPreviousPage ? encodePageCursor({ page: page - 1 }) : void 0;
198
- const results = pageRows.map(({ type, document, highlight }, index) => ({
199
- type,
200
- document,
201
- rank: page * pageSize + index + 1,
202
- highlight: {
203
- preTag: pgQuery.options.preTag,
204
- postTag: pgQuery.options.postTag,
205
- fields: highlight ? {
206
- text: highlight.text,
207
- title: highlight.title,
208
- location: highlight.location,
209
- path: ""
210
- } : {}
211
- }
212
- }));
239
+ const results = pageRows.map(
240
+ ({ type, document, highlight }, index) => ({
241
+ type,
242
+ document,
243
+ rank: page * pageSize + index + 1,
244
+ highlight: {
245
+ preTag: pgQuery.options.preTag,
246
+ postTag: pgQuery.options.postTag,
247
+ fields: highlight ? {
248
+ text: highlight.text,
249
+ title: highlight.title,
250
+ location: highlight.location,
251
+ path: ""
252
+ } : {}
253
+ }
254
+ })
255
+ );
213
256
  return { results, nextPageCursor, previousPageCursor };
214
257
  }
215
258
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../src/database/util.ts","../src/database/DatabaseDocumentStore.ts","../src/PgSearchEngine/PgSearchEngineIndexer.ts","../src/PgSearchEngine/PgSearchEngine.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Knex } from 'knex';\n\nexport async function queryPostgresMajorVersion(knex: Knex): Promise<number> {\n if (knex.client.config.client !== 'pg') {\n throw new Error(\"Can't resolve version, not a postgres database\");\n }\n\n const { rows } = await knex.raw('SHOW server_version_num');\n const [result] = rows;\n const version = +result.server_version_num;\n const majorVersion = Math.floor(version / 10000);\n return majorVersion;\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { resolvePackagePath } from '@backstage/backend-common';\nimport { IndexableDocument } from '@backstage/plugin-search-common';\nimport { Knex } from 'knex';\nimport {\n DatabaseStore,\n DocumentResultRow,\n PgSearchQuery,\n RawDocumentRow,\n} from './types';\nimport { queryPostgresMajorVersion } from './util';\n\nconst migrationsDir = resolvePackagePath(\n '@backstage/plugin-search-backend-module-pg',\n 'migrations',\n);\n\nexport class DatabaseDocumentStore implements DatabaseStore {\n static async create(knex: Knex): Promise<DatabaseDocumentStore> {\n try {\n const majorVersion = await queryPostgresMajorVersion(knex);\n\n if (majorVersion < 12) {\n // We are using some features (like generated columns) that aren't\n // available in older postgres versions.\n throw new Error(\n `The PgSearchEngine requires at least postgres version 12 (but is running on ${majorVersion})`,\n );\n }\n } catch {\n // Actually both mysql and sqlite have a full text search, too. We could\n // implement them separately or add them here.\n throw new Error(\n 'The PgSearchEngine is only supported when using a postgres database (>=12.x)',\n );\n }\n\n await knex.migrate.latest({\n directory: migrationsDir,\n });\n return new DatabaseDocumentStore(knex);\n }\n\n static async supported(knex: Knex): Promise<boolean> {\n try {\n const majorVersion = await queryPostgresMajorVersion(knex);\n\n return majorVersion >= 12;\n } catch {\n return false;\n }\n }\n\n constructor(private readonly db: Knex) {}\n\n async transaction<T>(fn: (tx: Knex.Transaction) => Promise<T>): Promise<T> {\n return await this.db.transaction(fn);\n }\n\n async getTransaction(): Promise<Knex.Transaction> {\n return this.db.transaction();\n }\n\n async prepareInsert(tx: Knex.Transaction): Promise<void> {\n // We create a temporary table to collect the hashes of the documents that\n // we expect to be in the documents table at the end. The table is deleted\n // at the end of the transaction.\n // The hash makes sure that we generate a new row for every change.\n await tx.raw(\n 'CREATE TEMP TABLE documents_to_insert (' +\n 'type text NOT NULL, ' +\n 'document jsonb NOT NULL, ' +\n // Generating the hash requires a trick, as the text to bytea\n // conversation runs into errors in case the text contains a backslash.\n // Therefore we have to escape them.\n \"hash bytea NOT NULL GENERATED ALWAYS AS (sha256(replace(document::text || type, '\\\\', '\\\\\\\\')::bytea)) STORED\" +\n ') ON COMMIT DROP',\n );\n }\n\n async completeInsert(tx: Knex.Transaction, type: string): Promise<void> {\n // Copy all new rows into the documents table\n await tx\n .insert(\n tx<RawDocumentRow>('documents_to_insert').select(\n 'type',\n 'document',\n 'hash',\n ),\n )\n .into(tx.raw('documents (type, document, hash)'))\n .onConflict('hash')\n .ignore();\n\n // Delete all documents that we don't expect (deleted and changed)\n await tx<RawDocumentRow>('documents')\n .where({ type })\n .whereNotIn(\n 'hash',\n tx<RawDocumentRow>('documents_to_insert').select('hash'),\n )\n .delete();\n }\n\n async insertDocuments(\n tx: Knex.Transaction,\n type: string,\n documents: IndexableDocument[],\n ): Promise<void> {\n // Insert all documents into the temporary table to process them later\n await tx<DocumentResultRow>('documents_to_insert').insert(\n documents.map(document => ({\n type,\n document,\n })),\n );\n }\n\n async query(\n tx: Knex.Transaction,\n searchQuery: PgSearchQuery,\n ): Promise<DocumentResultRow[]> {\n const { types, pgTerm, fields, offset, limit, options } = searchQuery;\n // TODO(awanlin): We should make the language a parameter so that we can support more then just english\n // Builds a query like:\n // SELECT ts_rank_cd(body, query) AS rank, type, document,\n // ts_headline('english', document, query) AS highlight\n // FROM documents, to_tsquery('english', 'consent') query\n // WHERE query @@ body AND (document @> '{\"kind\": \"API\"}')\n // ORDER BY rank DESC\n // LIMIT 10;\n const query = tx<DocumentResultRow>('documents');\n\n if (pgTerm) {\n query\n .from(tx.raw(\"documents, to_tsquery('english', ?) query\", pgTerm))\n .whereRaw('query @@ body');\n } else {\n query.from('documents');\n }\n\n if (types) {\n query.whereIn('type', types);\n }\n\n if (fields) {\n Object.keys(fields).forEach(key => {\n const value = fields[key];\n const valueArray = Array.isArray(value) ? value : [value];\n const valueCompare = valueArray\n .map(v => ({ [key]: v }))\n .map(v => JSON.stringify(v));\n query.whereRaw(\n `(${valueCompare.map(() => 'document @> ?').join(' OR ')})`,\n valueCompare,\n );\n });\n }\n\n query.select('type', 'document');\n\n if (pgTerm && options.useHighlight) {\n const headlineOptions = `MaxWords=${options.maxWords}, MinWords=${options.minWords}, ShortWord=${options.shortWord}, HighlightAll=${options.highlightAll}, MaxFragments=${options.maxFragments}, FragmentDelimiter=${options.fragmentDelimiter}, StartSel=${options.preTag}, StopSel=${options.postTag}`;\n query\n .select(tx.raw('ts_rank_cd(body, query) AS \"rank\"'))\n .select(\n tx.raw(\n `ts_headline(\\'english\\', document, query, '${headlineOptions}') as \"highlight\"`,\n ),\n )\n .orderBy('rank', 'desc');\n } else if (pgTerm && !options.useHighlight) {\n query\n .select(tx.raw('ts_rank_cd(body, query) AS \"rank\"'))\n .orderBy('rank', 'desc');\n } else {\n query.select(tx.raw('1 as rank'));\n }\n\n return await query.offset(offset).limit(limit);\n }\n}\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { BatchSearchEngineIndexer } from '@backstage/plugin-search-backend-node';\nimport { IndexableDocument } from '@backstage/plugin-search-common';\nimport { Knex } from 'knex';\nimport { DatabaseStore } from '../database';\n\nexport type PgSearchEngineIndexerOptions = {\n batchSize: number;\n type: string;\n databaseStore: DatabaseStore;\n};\n\nexport class PgSearchEngineIndexer extends BatchSearchEngineIndexer {\n private store: DatabaseStore;\n private type: string;\n private tx: Knex.Transaction | undefined;\n\n constructor(options: PgSearchEngineIndexerOptions) {\n super({ batchSize: options.batchSize });\n this.store = options.databaseStore;\n this.type = options.type;\n }\n\n async initialize(): Promise<void> {\n this.tx = await this.store.getTransaction();\n try {\n await this.store.prepareInsert(this.tx);\n } catch (e) {\n // In case of error, rollback the transaction and re-throw the error so\n // that the stream can be closed and destroyed properly.\n this.tx.rollback(e);\n throw e;\n }\n }\n\n async index(documents: IndexableDocument[]): Promise<void> {\n try {\n await this.store.insertDocuments(this.tx!, this.type, documents);\n } catch (e) {\n // In case of error, rollback the transaction and re-throw the error so\n // that the stream can be closed and destroyed properly.\n this.tx!.rollback(e);\n throw e;\n }\n }\n\n async finalize(): Promise<void> {\n // Attempt to complete and commit the transaction.\n try {\n await this.store.completeInsert(this.tx!, this.type);\n this.tx!.commit();\n } catch (e) {\n // Otherwise, rollback the transaction and re-throw the error so that the\n // stream can be closed and destroyed properly.\n this.tx!.rollback!(e);\n throw e;\n }\n }\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { PluginDatabaseManager } from '@backstage/backend-common';\nimport { SearchEngine } from '@backstage/plugin-search-common';\nimport {\n SearchQuery,\n IndexableResultSet,\n IndexableResult,\n} from '@backstage/plugin-search-common';\nimport { PgSearchEngineIndexer } from './PgSearchEngineIndexer';\nimport {\n DatabaseDocumentStore,\n DatabaseStore,\n PgSearchQuery,\n} from '../database';\nimport { v4 as uuid } from 'uuid';\nimport { Config } from '@backstage/config';\n\n/**\n * Search query that the Postgres search engine understands.\n * @public\n */\nexport type ConcretePgSearchQuery = {\n pgQuery: PgSearchQuery;\n pageSize: number;\n};\n\n/**\n * Options available for the Postgres specific query translator.\n * @public\n */\nexport type PgSearchQueryTranslatorOptions = {\n highlightOptions: PgSearchHighlightOptions;\n};\n\n/**\n * Postgres specific query translator.\n * @public\n */\nexport type PgSearchQueryTranslator = (\n query: SearchQuery,\n options: PgSearchQueryTranslatorOptions,\n) => ConcretePgSearchQuery;\n\n/**\n * Options to instantiate PgSearchEngine\n * @public\n */\nexport type PgSearchOptions = {\n database: PluginDatabaseManager;\n};\n\n/**\n * Options for highlighting search terms\n * @public\n */\nexport type PgSearchHighlightOptions = {\n useHighlight?: boolean;\n maxWords?: number;\n minWords?: number;\n shortWord?: number;\n highlightAll?: boolean;\n maxFragments?: number;\n fragmentDelimiter?: string;\n preTag: string;\n postTag: string;\n};\n\nexport class PgSearchEngine implements SearchEngine {\n private readonly highlightOptions: PgSearchHighlightOptions;\n\n /**\n * @deprecated This will be marked as private in a future release, please us fromConfig instead\n */\n constructor(private readonly databaseStore: DatabaseStore, config: Config) {\n const uuidTag = uuid();\n const highlightConfig = config.getOptionalConfig(\n 'search.pg.highlightOptions',\n );\n\n const highlightOptions: PgSearchHighlightOptions = {\n preTag: `<${uuidTag}>`,\n postTag: `</${uuidTag}>`,\n useHighlight: highlightConfig?.getOptionalBoolean('useHighlight') ?? true,\n maxWords: highlightConfig?.getOptionalNumber('maxWords') ?? 35,\n minWords: highlightConfig?.getOptionalNumber('minWords') ?? 15,\n shortWord: highlightConfig?.getOptionalNumber('shortWord') ?? 3,\n highlightAll:\n highlightConfig?.getOptionalBoolean('highlightAll') ?? false,\n maxFragments: highlightConfig?.getOptionalNumber('maxFragments') ?? 0,\n fragmentDelimiter:\n highlightConfig?.getOptionalString('fragmentDelimiter') ?? ' ... ',\n };\n this.highlightOptions = highlightOptions;\n }\n\n /**\n * @deprecated This will be removed in a future release, please us fromConfig instead\n */\n static async from(options: {\n database: PluginDatabaseManager;\n config: Config;\n }): Promise<PgSearchEngine> {\n return new PgSearchEngine(\n await DatabaseDocumentStore.create(await options.database.getClient()),\n options.config,\n );\n }\n\n static async fromConfig(config: Config, options: PgSearchOptions) {\n return new PgSearchEngine(\n await DatabaseDocumentStore.create(await options.database.getClient()),\n config,\n );\n }\n\n static async supported(database: PluginDatabaseManager): Promise<boolean> {\n return await DatabaseDocumentStore.supported(await database.getClient());\n }\n\n translator(\n query: SearchQuery,\n options: PgSearchQueryTranslatorOptions,\n ): ConcretePgSearchQuery {\n const pageSize = 25;\n const { page } = decodePageCursor(query.pageCursor);\n const offset = page * pageSize;\n // We request more result to know whether there is another page\n const limit = pageSize + 1;\n\n return {\n pgQuery: {\n pgTerm: query.term\n .split(/\\s/)\n .map(p => p.replace(/[\\0()|&:*!]/g, '').trim())\n .filter(p => p !== '')\n .map(p => `(${JSON.stringify(p)} | ${JSON.stringify(p)}:*)`)\n .join('&'),\n fields: query.filters as Record<string, string | string[]>,\n types: query.types,\n offset,\n limit,\n options: options.highlightOptions,\n },\n pageSize,\n };\n }\n\n setTranslator(translator: PgSearchQueryTranslator) {\n this.translator = translator;\n }\n\n async getIndexer(type: string) {\n return new PgSearchEngineIndexer({\n batchSize: 1000,\n type,\n databaseStore: this.databaseStore,\n });\n }\n\n async query(query: SearchQuery): Promise<IndexableResultSet> {\n const { pgQuery, pageSize } = this.translator(query, {\n highlightOptions: this.highlightOptions,\n });\n\n const rows = await this.databaseStore.transaction(async tx =>\n this.databaseStore.query(tx, pgQuery),\n );\n\n // We requested one result more than the page size to know whether there is\n // another page.\n const { page } = decodePageCursor(query.pageCursor);\n const hasNextPage = rows.length > pageSize;\n const hasPreviousPage = page > 0;\n const pageRows = rows.slice(0, pageSize);\n const nextPageCursor = hasNextPage\n ? encodePageCursor({ page: page + 1 })\n : undefined;\n const previousPageCursor = hasPreviousPage\n ? encodePageCursor({ page: page - 1 })\n : undefined;\n\n const results = pageRows.map(\n ({ type, document, highlight }, index): IndexableResult => ({\n type,\n document,\n rank: page * pageSize + index + 1,\n highlight: {\n preTag: pgQuery.options.preTag,\n postTag: pgQuery.options.postTag,\n fields: highlight\n ? {\n text: highlight.text,\n title: highlight.title,\n location: highlight.location,\n path: '',\n }\n : {},\n },\n }),\n );\n\n return { results, nextPageCursor, previousPageCursor };\n }\n}\n\nexport function decodePageCursor(pageCursor?: string): { page: number } {\n if (!pageCursor) {\n return { page: 0 };\n }\n\n return {\n page: Number(Buffer.from(pageCursor, 'base64').toString('utf-8')),\n };\n}\n\nexport function encodePageCursor({ page }: { page: number }): string {\n return Buffer.from(`${page}`, 'utf-8').toString('base64');\n}\n"],"names":["resolvePackagePath","BatchSearchEngineIndexer","uuid"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/database/util.ts","../src/database/DatabaseDocumentStore.ts","../src/PgSearchEngine/PgSearchEngineIndexer.ts","../src/PgSearchEngine/PgSearchEngine.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Knex } from 'knex';\n\nexport async function queryPostgresMajorVersion(knex: Knex): Promise<number> {\n if (knex.client.config.client !== 'pg') {\n throw new Error(\"Can't resolve version, not a postgres database\");\n }\n\n const { rows } = await knex.raw('SHOW server_version_num');\n const [result] = rows;\n const version = +result.server_version_num;\n const majorVersion = Math.floor(version / 10000);\n return majorVersion;\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n PluginDatabaseManager,\n resolvePackagePath,\n} from '@backstage/backend-common';\nimport { IndexableDocument } from '@backstage/plugin-search-common';\nimport { Knex } from 'knex';\nimport {\n DatabaseStore,\n DocumentResultRow,\n PgSearchQuery,\n RawDocumentRow,\n} from './types';\nimport { queryPostgresMajorVersion } from './util';\n\nconst migrationsDir = resolvePackagePath(\n '@backstage/plugin-search-backend-module-pg',\n 'migrations',\n);\n\nexport class DatabaseDocumentStore implements DatabaseStore {\n static async create(\n database: PluginDatabaseManager,\n ): Promise<DatabaseDocumentStore> {\n const knex = await database.getClient();\n try {\n const majorVersion = await queryPostgresMajorVersion(knex);\n\n if (majorVersion < 12) {\n // We are using some features (like generated columns) that aren't\n // available in older postgres versions.\n throw new Error(\n `The PgSearchEngine requires at least postgres version 12 (but is running on ${majorVersion})`,\n );\n }\n } catch {\n // Actually both mysql and sqlite have a full text search, too. We could\n // implement them separately or add them here.\n throw new Error(\n 'The PgSearchEngine is only supported when using a postgres database (>=12.x)',\n );\n }\n\n if (!database.migrations?.skip) {\n await knex.migrate.latest({\n directory: migrationsDir,\n });\n }\n\n return new DatabaseDocumentStore(knex);\n }\n\n static async supported(knex: Knex): Promise<boolean> {\n try {\n const majorVersion = await queryPostgresMajorVersion(knex);\n\n return majorVersion >= 12;\n } catch {\n return false;\n }\n }\n\n constructor(private readonly db: Knex) {}\n\n async transaction<T>(fn: (tx: Knex.Transaction) => Promise<T>): Promise<T> {\n return await this.db.transaction(fn);\n }\n\n async getTransaction(): Promise<Knex.Transaction> {\n return this.db.transaction();\n }\n\n async prepareInsert(tx: Knex.Transaction): Promise<void> {\n // We create a temporary table to collect the hashes of the documents that\n // we expect to be in the documents table at the end. The table is deleted\n // at the end of the transaction.\n // The hash makes sure that we generate a new row for every change.\n await tx.raw(\n 'CREATE TEMP TABLE documents_to_insert (' +\n 'type text NOT NULL, ' +\n 'document jsonb NOT NULL, ' +\n // Generating the hash requires a trick, as the text to bytea\n // conversation runs into errors in case the text contains a backslash.\n // Therefore we have to escape them.\n \"hash bytea NOT NULL GENERATED ALWAYS AS (sha256(replace(document::text || type, '\\\\', '\\\\\\\\')::bytea)) STORED\" +\n ') ON COMMIT DROP',\n );\n }\n\n async completeInsert(tx: Knex.Transaction, type: string): Promise<void> {\n // Copy all new rows into the documents table\n await tx\n .insert(\n tx<RawDocumentRow>('documents_to_insert').select(\n 'type',\n 'document',\n 'hash',\n ),\n )\n .into(tx.raw('documents (type, document, hash)'))\n .onConflict('hash')\n .ignore();\n\n // Delete all documents that we don't expect (deleted and changed)\n await tx<RawDocumentRow>('documents')\n .where({ type })\n .whereNotIn(\n 'hash',\n tx<RawDocumentRow>('documents_to_insert').select('hash'),\n )\n .delete();\n }\n\n async insertDocuments(\n tx: Knex.Transaction,\n type: string,\n documents: IndexableDocument[],\n ): Promise<void> {\n // Insert all documents into the temporary table to process them later\n await tx<DocumentResultRow>('documents_to_insert').insert(\n documents.map(document => ({\n type,\n document,\n })),\n );\n }\n\n async query(\n tx: Knex.Transaction,\n searchQuery: PgSearchQuery,\n ): Promise<DocumentResultRow[]> {\n const { types, pgTerm, fields, offset, limit, options } = searchQuery;\n // TODO(awanlin): We should make the language a parameter so that we can support more then just english\n // Builds a query like:\n // SELECT ts_rank_cd(body, query) AS rank, type, document,\n // ts_headline('english', document, query) AS highlight\n // FROM documents, to_tsquery('english', 'consent') query\n // WHERE query @@ body AND (document @> '{\"kind\": \"API\"}')\n // ORDER BY rank DESC\n // LIMIT 10;\n const query = tx<DocumentResultRow>('documents');\n\n if (pgTerm) {\n query\n .from(tx.raw(\"documents, to_tsquery('english', ?) query\", pgTerm))\n .whereRaw('query @@ body');\n } else {\n query.from('documents');\n }\n\n if (types) {\n query.whereIn('type', types);\n }\n\n if (fields) {\n Object.keys(fields).forEach(key => {\n const value = fields[key];\n const valueArray = Array.isArray(value) ? value : [value];\n const valueCompare = valueArray\n .map(v => ({ [key]: v }))\n .map(v => JSON.stringify(v));\n query.whereRaw(\n `(${valueCompare.map(() => 'document @> ?').join(' OR ')})`,\n valueCompare,\n );\n });\n }\n\n query.select('type', 'document');\n\n if (pgTerm && options.useHighlight) {\n const headlineOptions = `MaxWords=${options.maxWords}, MinWords=${options.minWords}, ShortWord=${options.shortWord}, HighlightAll=${options.highlightAll}, MaxFragments=${options.maxFragments}, FragmentDelimiter=${options.fragmentDelimiter}, StartSel=${options.preTag}, StopSel=${options.postTag}`;\n query\n .select(tx.raw('ts_rank_cd(body, query) AS \"rank\"'))\n .select(\n tx.raw(\n `ts_headline(\\'english\\', document, query, '${headlineOptions}') as \"highlight\"`,\n ),\n )\n .orderBy('rank', 'desc');\n } else if (pgTerm && !options.useHighlight) {\n query\n .select(tx.raw('ts_rank_cd(body, query) AS \"rank\"'))\n .orderBy('rank', 'desc');\n } else {\n query.select(tx.raw('1 as rank'));\n }\n\n return await query.offset(offset).limit(limit);\n }\n}\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { BatchSearchEngineIndexer } from '@backstage/plugin-search-backend-node';\nimport { IndexableDocument } from '@backstage/plugin-search-common';\nimport { Knex } from 'knex';\nimport { DatabaseStore } from '../database';\n\nexport type PgSearchEngineIndexerOptions = {\n batchSize: number;\n type: string;\n databaseStore: DatabaseStore;\n};\n\nexport class PgSearchEngineIndexer extends BatchSearchEngineIndexer {\n private store: DatabaseStore;\n private type: string;\n private tx: Knex.Transaction | undefined;\n\n constructor(options: PgSearchEngineIndexerOptions) {\n super({ batchSize: options.batchSize });\n this.store = options.databaseStore;\n this.type = options.type;\n }\n\n async initialize(): Promise<void> {\n this.tx = await this.store.getTransaction();\n try {\n await this.store.prepareInsert(this.tx);\n } catch (e) {\n // In case of error, rollback the transaction and re-throw the error so\n // that the stream can be closed and destroyed properly.\n this.tx.rollback(e);\n throw e;\n }\n }\n\n async index(documents: IndexableDocument[]): Promise<void> {\n try {\n await this.store.insertDocuments(this.tx!, this.type, documents);\n } catch (e) {\n // In case of error, rollback the transaction and re-throw the error so\n // that the stream can be closed and destroyed properly.\n this.tx!.rollback(e);\n throw e;\n }\n }\n\n async finalize(): Promise<void> {\n // Attempt to complete and commit the transaction.\n try {\n await this.store.completeInsert(this.tx!, this.type);\n this.tx!.commit();\n } catch (e) {\n // Otherwise, rollback the transaction and re-throw the error so that the\n // stream can be closed and destroyed properly.\n this.tx!.rollback!(e);\n throw e;\n }\n }\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { PluginDatabaseManager } from '@backstage/backend-common';\nimport { SearchEngine } from '@backstage/plugin-search-common';\nimport {\n SearchQuery,\n IndexableResultSet,\n IndexableResult,\n} from '@backstage/plugin-search-common';\nimport { PgSearchEngineIndexer } from './PgSearchEngineIndexer';\nimport {\n DatabaseDocumentStore,\n DatabaseStore,\n PgSearchQuery,\n} from '../database';\nimport { v4 as uuid } from 'uuid';\nimport { Config } from '@backstage/config';\n\n/**\n * Search query that the Postgres search engine understands.\n * @public\n */\nexport type ConcretePgSearchQuery = {\n pgQuery: PgSearchQuery;\n pageSize: number;\n};\n\n/**\n * Options available for the Postgres specific query translator.\n * @public\n */\nexport type PgSearchQueryTranslatorOptions = {\n highlightOptions: PgSearchHighlightOptions;\n};\n\n/**\n * Postgres specific query translator.\n * @public\n */\nexport type PgSearchQueryTranslator = (\n query: SearchQuery,\n options: PgSearchQueryTranslatorOptions,\n) => ConcretePgSearchQuery;\n\n/**\n * Options to instantiate PgSearchEngine\n * @public\n */\nexport type PgSearchOptions = {\n database: PluginDatabaseManager;\n};\n\n/**\n * Options for highlighting search terms\n * @public\n */\nexport type PgSearchHighlightOptions = {\n useHighlight?: boolean;\n maxWords?: number;\n minWords?: number;\n shortWord?: number;\n highlightAll?: boolean;\n maxFragments?: number;\n fragmentDelimiter?: string;\n preTag: string;\n postTag: string;\n};\n\nexport class PgSearchEngine implements SearchEngine {\n private readonly highlightOptions: PgSearchHighlightOptions;\n\n /**\n * @deprecated This will be marked as private in a future release, please us fromConfig instead\n */\n constructor(private readonly databaseStore: DatabaseStore, config: Config) {\n const uuidTag = uuid();\n const highlightConfig = config.getOptionalConfig(\n 'search.pg.highlightOptions',\n );\n\n const highlightOptions: PgSearchHighlightOptions = {\n preTag: `<${uuidTag}>`,\n postTag: `</${uuidTag}>`,\n useHighlight: highlightConfig?.getOptionalBoolean('useHighlight') ?? true,\n maxWords: highlightConfig?.getOptionalNumber('maxWords') ?? 35,\n minWords: highlightConfig?.getOptionalNumber('minWords') ?? 15,\n shortWord: highlightConfig?.getOptionalNumber('shortWord') ?? 3,\n highlightAll:\n highlightConfig?.getOptionalBoolean('highlightAll') ?? false,\n maxFragments: highlightConfig?.getOptionalNumber('maxFragments') ?? 0,\n fragmentDelimiter:\n highlightConfig?.getOptionalString('fragmentDelimiter') ?? ' ... ',\n };\n this.highlightOptions = highlightOptions;\n }\n\n /**\n * @deprecated This will be removed in a future release, please us fromConfig instead\n */\n static async from(options: {\n database: PluginDatabaseManager;\n config: Config;\n }): Promise<PgSearchEngine> {\n return new PgSearchEngine(\n await DatabaseDocumentStore.create(options.database),\n options.config,\n );\n }\n\n static async fromConfig(config: Config, options: PgSearchOptions) {\n return new PgSearchEngine(\n await DatabaseDocumentStore.create(options.database),\n config,\n );\n }\n\n static async supported(database: PluginDatabaseManager): Promise<boolean> {\n return await DatabaseDocumentStore.supported(await database.getClient());\n }\n\n translator(\n query: SearchQuery,\n options: PgSearchQueryTranslatorOptions,\n ): ConcretePgSearchQuery {\n const pageSize = 25;\n const { page } = decodePageCursor(query.pageCursor);\n const offset = page * pageSize;\n // We request more result to know whether there is another page\n const limit = pageSize + 1;\n\n return {\n pgQuery: {\n pgTerm: query.term\n .split(/\\s/)\n .map(p => p.replace(/[\\0()|&:*!]/g, '').trim())\n .filter(p => p !== '')\n .map(p => `(${JSON.stringify(p)} | ${JSON.stringify(p)}:*)`)\n .join('&'),\n fields: query.filters as Record<string, string | string[]>,\n types: query.types,\n offset,\n limit,\n options: options.highlightOptions,\n },\n pageSize,\n };\n }\n\n setTranslator(translator: PgSearchQueryTranslator) {\n this.translator = translator;\n }\n\n async getIndexer(type: string) {\n return new PgSearchEngineIndexer({\n batchSize: 1000,\n type,\n databaseStore: this.databaseStore,\n });\n }\n\n async query(query: SearchQuery): Promise<IndexableResultSet> {\n const { pgQuery, pageSize } = this.translator(query, {\n highlightOptions: this.highlightOptions,\n });\n\n const rows = await this.databaseStore.transaction(async tx =>\n this.databaseStore.query(tx, pgQuery),\n );\n\n // We requested one result more than the page size to know whether there is\n // another page.\n const { page } = decodePageCursor(query.pageCursor);\n const hasNextPage = rows.length > pageSize;\n const hasPreviousPage = page > 0;\n const pageRows = rows.slice(0, pageSize);\n const nextPageCursor = hasNextPage\n ? encodePageCursor({ page: page + 1 })\n : undefined;\n const previousPageCursor = hasPreviousPage\n ? encodePageCursor({ page: page - 1 })\n : undefined;\n\n const results = pageRows.map(\n ({ type, document, highlight }, index): IndexableResult => ({\n type,\n document,\n rank: page * pageSize + index + 1,\n highlight: {\n preTag: pgQuery.options.preTag,\n postTag: pgQuery.options.postTag,\n fields: highlight\n ? {\n text: highlight.text,\n title: highlight.title,\n location: highlight.location,\n path: '',\n }\n : {},\n },\n }),\n );\n\n return { results, nextPageCursor, previousPageCursor };\n }\n}\n\nexport function decodePageCursor(pageCursor?: string): { page: number } {\n if (!pageCursor) {\n return { page: 0 };\n }\n\n return {\n page: Number(Buffer.from(pageCursor, 'base64').toString('utf-8')),\n };\n}\n\nexport function encodePageCursor({ page }: { page: number }): string {\n return Buffer.from(`${page}`, 'utf-8').toString('base64');\n}\n"],"names":["resolvePackagePath","BatchSearchEngineIndexer","uuid"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
+ import { PluginDatabaseManager } from '@backstage/backend-common';
1
2
  import { IndexableDocument, SearchQuery, SearchEngine, IndexableResultSet } from '@backstage/plugin-search-common';
2
3
  import { Knex } from 'knex';
3
- import { PluginDatabaseManager } from '@backstage/backend-common';
4
4
  import { BatchSearchEngineIndexer } from '@backstage/plugin-search-backend-node';
5
5
  import { Config } from '@backstage/config';
6
6
 
@@ -112,7 +112,7 @@ interface DocumentResultRow {
112
112
 
113
113
  declare class DatabaseDocumentStore implements DatabaseStore {
114
114
  private readonly db;
115
- static create(knex: Knex): Promise<DatabaseDocumentStore>;
115
+ static create(database: PluginDatabaseManager): Promise<DatabaseDocumentStore>;
116
116
  static supported(knex: Knex): Promise<boolean>;
117
117
  constructor(db: Knex);
118
118
  transaction<T>(fn: (tx: Knex.Transaction) => Promise<T>): Promise<T>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@backstage/plugin-search-backend-module-pg",
3
3
  "description": "A module for the search backend that implements search using PostgreSQL",
4
- "version": "0.3.5",
4
+ "version": "0.4.0-next.0",
5
5
  "main": "dist/index.cjs.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "license": "Apache-2.0",
@@ -23,17 +23,17 @@
23
23
  "clean": "backstage-cli package clean"
24
24
  },
25
25
  "dependencies": {
26
- "@backstage/backend-common": "^0.14.1",
26
+ "@backstage/backend-common": "^0.15.1-next.0",
27
27
  "@backstage/config": "^1.0.1",
28
- "@backstage/plugin-search-backend-node": "^1.0.0",
29
- "@backstage/plugin-search-common": "^1.0.0",
28
+ "@backstage/plugin-search-backend-node": "^1.0.2-next.0",
29
+ "@backstage/plugin-search-common": "^1.0.1-next.0",
30
30
  "knex": "^2.0.0",
31
31
  "lodash": "^4.17.21",
32
32
  "uuid": "^8.3.2"
33
33
  },
34
34
  "devDependencies": {
35
- "@backstage/backend-test-utils": "^0.1.26",
36
- "@backstage/cli": "^0.18.0"
35
+ "@backstage/backend-test-utils": "^0.1.28-next.0",
36
+ "@backstage/cli": "^0.18.2-next.0"
37
37
  },
38
38
  "files": [
39
39
  "dist",
@@ -41,5 +41,5 @@
41
41
  "config.d.ts"
42
42
  ],
43
43
  "configSchema": "config.d.ts",
44
- "gitHead": "999878d8f1ae30f6a15925816af2016cb9d717a1"
44
+ "gitHead": "c6c0b1978a7ab4d29d813996c56beb7e6b48a268"
45
45
  }