@kernl-sdk/pg 0.1.11 → 0.1.12

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.
Files changed (153) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-check-types.log +36 -0
  3. package/CHANGELOG.md +32 -0
  4. package/README.md +124 -0
  5. package/dist/__tests__/integration.test.js +2 -2
  6. package/dist/__tests__/memory-integration.test.d.ts +2 -0
  7. package/dist/__tests__/memory-integration.test.d.ts.map +1 -0
  8. package/dist/__tests__/memory-integration.test.js +287 -0
  9. package/dist/__tests__/memory.test.d.ts +2 -0
  10. package/dist/__tests__/memory.test.d.ts.map +1 -0
  11. package/dist/__tests__/memory.test.js +357 -0
  12. package/dist/index.d.ts +5 -3
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +5 -3
  15. package/dist/memory/sql.d.ts +30 -0
  16. package/dist/memory/sql.d.ts.map +1 -0
  17. package/dist/memory/sql.js +100 -0
  18. package/dist/memory/store.d.ts +41 -0
  19. package/dist/memory/store.d.ts.map +1 -0
  20. package/dist/memory/store.js +114 -0
  21. package/dist/migrations.d.ts +1 -1
  22. package/dist/migrations.d.ts.map +1 -1
  23. package/dist/migrations.js +9 -3
  24. package/dist/pgvector/__tests__/handle.test.d.ts +2 -0
  25. package/dist/pgvector/__tests__/handle.test.d.ts.map +1 -0
  26. package/dist/pgvector/__tests__/handle.test.js +277 -0
  27. package/dist/pgvector/__tests__/hit.test.d.ts +2 -0
  28. package/dist/pgvector/__tests__/hit.test.d.ts.map +1 -0
  29. package/dist/pgvector/__tests__/hit.test.js +134 -0
  30. package/dist/pgvector/__tests__/integration/document.integration.test.d.ts +7 -0
  31. package/dist/pgvector/__tests__/integration/document.integration.test.d.ts.map +1 -0
  32. package/dist/pgvector/__tests__/integration/document.integration.test.js +587 -0
  33. package/dist/pgvector/__tests__/integration/edge.integration.test.d.ts +8 -0
  34. package/dist/pgvector/__tests__/integration/edge.integration.test.d.ts.map +1 -0
  35. package/dist/pgvector/__tests__/integration/edge.integration.test.js +663 -0
  36. package/dist/pgvector/__tests__/integration/filters.integration.test.d.ts +8 -0
  37. package/dist/pgvector/__tests__/integration/filters.integration.test.d.ts.map +1 -0
  38. package/dist/pgvector/__tests__/integration/filters.integration.test.js +609 -0
  39. package/dist/pgvector/__tests__/integration/lifecycle.integration.test.d.ts +8 -0
  40. package/dist/pgvector/__tests__/integration/lifecycle.integration.test.d.ts.map +1 -0
  41. package/dist/pgvector/__tests__/integration/lifecycle.integration.test.js +449 -0
  42. package/dist/pgvector/__tests__/integration/query.integration.test.d.ts +8 -0
  43. package/dist/pgvector/__tests__/integration/query.integration.test.d.ts.map +1 -0
  44. package/dist/pgvector/__tests__/integration/query.integration.test.js +544 -0
  45. package/dist/pgvector/__tests__/search.test.d.ts +2 -0
  46. package/dist/pgvector/__tests__/search.test.d.ts.map +1 -0
  47. package/dist/pgvector/__tests__/search.test.js +279 -0
  48. package/dist/pgvector/handle.d.ts +60 -0
  49. package/dist/pgvector/handle.d.ts.map +1 -0
  50. package/dist/pgvector/handle.js +213 -0
  51. package/dist/pgvector/hit.d.ts +10 -0
  52. package/dist/pgvector/hit.d.ts.map +1 -0
  53. package/dist/pgvector/hit.js +44 -0
  54. package/dist/pgvector/index.d.ts +7 -0
  55. package/dist/pgvector/index.d.ts.map +1 -0
  56. package/dist/pgvector/index.js +5 -0
  57. package/dist/pgvector/search.d.ts +60 -0
  58. package/dist/pgvector/search.d.ts.map +1 -0
  59. package/dist/pgvector/search.js +227 -0
  60. package/dist/pgvector/sql/__tests__/limit.test.d.ts +2 -0
  61. package/dist/pgvector/sql/__tests__/limit.test.d.ts.map +1 -0
  62. package/dist/pgvector/sql/__tests__/limit.test.js +161 -0
  63. package/dist/pgvector/sql/__tests__/order.test.d.ts +2 -0
  64. package/dist/pgvector/sql/__tests__/order.test.d.ts.map +1 -0
  65. package/dist/pgvector/sql/__tests__/order.test.js +218 -0
  66. package/dist/pgvector/sql/__tests__/query.test.d.ts +2 -0
  67. package/dist/pgvector/sql/__tests__/query.test.d.ts.map +1 -0
  68. package/dist/pgvector/sql/__tests__/query.test.js +392 -0
  69. package/dist/pgvector/sql/__tests__/select.test.d.ts +2 -0
  70. package/dist/pgvector/sql/__tests__/select.test.d.ts.map +1 -0
  71. package/dist/pgvector/sql/__tests__/select.test.js +293 -0
  72. package/dist/pgvector/sql/__tests__/where.test.d.ts +2 -0
  73. package/dist/pgvector/sql/__tests__/where.test.d.ts.map +1 -0
  74. package/dist/pgvector/sql/__tests__/where.test.js +488 -0
  75. package/dist/pgvector/sql/index.d.ts +7 -0
  76. package/dist/pgvector/sql/index.d.ts.map +1 -0
  77. package/dist/pgvector/sql/index.js +6 -0
  78. package/dist/pgvector/sql/limit.d.ts +8 -0
  79. package/dist/pgvector/sql/limit.d.ts.map +1 -0
  80. package/dist/pgvector/sql/limit.js +20 -0
  81. package/dist/pgvector/sql/order.d.ts +9 -0
  82. package/dist/pgvector/sql/order.d.ts.map +1 -0
  83. package/dist/pgvector/sql/order.js +47 -0
  84. package/dist/pgvector/sql/query.d.ts +46 -0
  85. package/dist/pgvector/sql/query.d.ts.map +1 -0
  86. package/dist/pgvector/sql/query.js +54 -0
  87. package/dist/pgvector/sql/schema.d.ts +16 -0
  88. package/dist/pgvector/sql/schema.d.ts.map +1 -0
  89. package/dist/pgvector/sql/schema.js +47 -0
  90. package/dist/pgvector/sql/select.d.ts +11 -0
  91. package/dist/pgvector/sql/select.d.ts.map +1 -0
  92. package/dist/pgvector/sql/select.js +87 -0
  93. package/dist/pgvector/sql/where.d.ts +8 -0
  94. package/dist/pgvector/sql/where.d.ts.map +1 -0
  95. package/dist/pgvector/sql/where.js +137 -0
  96. package/dist/pgvector/types.d.ts +20 -0
  97. package/dist/pgvector/types.d.ts.map +1 -0
  98. package/dist/pgvector/types.js +1 -0
  99. package/dist/pgvector/utils.d.ts +18 -0
  100. package/dist/pgvector/utils.d.ts.map +1 -0
  101. package/dist/pgvector/utils.js +22 -0
  102. package/dist/postgres.d.ts +19 -26
  103. package/dist/postgres.d.ts.map +1 -1
  104. package/dist/postgres.js +15 -27
  105. package/dist/storage.d.ts +48 -0
  106. package/dist/storage.d.ts.map +1 -1
  107. package/dist/storage.js +32 -9
  108. package/dist/thread/sql.d.ts +38 -0
  109. package/dist/thread/sql.d.ts.map +1 -0
  110. package/dist/thread/sql.js +112 -0
  111. package/dist/thread/store.d.ts +2 -2
  112. package/dist/thread/store.d.ts.map +1 -1
  113. package/dist/thread/store.js +32 -102
  114. package/package.json +7 -4
  115. package/src/__tests__/integration.test.ts +15 -17
  116. package/src/__tests__/memory-integration.test.ts +355 -0
  117. package/src/__tests__/memory.test.ts +428 -0
  118. package/src/index.ts +19 -3
  119. package/src/memory/sql.ts +141 -0
  120. package/src/memory/store.ts +166 -0
  121. package/src/migrations.ts +13 -3
  122. package/src/pgvector/README.md +50 -0
  123. package/src/pgvector/__tests__/handle.test.ts +335 -0
  124. package/src/pgvector/__tests__/hit.test.ts +165 -0
  125. package/src/pgvector/__tests__/integration/document.integration.test.ts +717 -0
  126. package/src/pgvector/__tests__/integration/edge.integration.test.ts +835 -0
  127. package/src/pgvector/__tests__/integration/filters.integration.test.ts +721 -0
  128. package/src/pgvector/__tests__/integration/lifecycle.integration.test.ts +570 -0
  129. package/src/pgvector/__tests__/integration/query.integration.test.ts +667 -0
  130. package/src/pgvector/__tests__/search.test.ts +366 -0
  131. package/src/pgvector/handle.ts +285 -0
  132. package/src/pgvector/hit.ts +56 -0
  133. package/src/pgvector/index.ts +7 -0
  134. package/src/pgvector/search.ts +330 -0
  135. package/src/pgvector/sql/__tests__/limit.test.ts +180 -0
  136. package/src/pgvector/sql/__tests__/order.test.ts +248 -0
  137. package/src/pgvector/sql/__tests__/query.test.ts +548 -0
  138. package/src/pgvector/sql/__tests__/select.test.ts +367 -0
  139. package/src/pgvector/sql/__tests__/where.test.ts +554 -0
  140. package/src/pgvector/sql/index.ts +14 -0
  141. package/src/pgvector/sql/limit.ts +29 -0
  142. package/src/pgvector/sql/order.ts +55 -0
  143. package/src/pgvector/sql/query.ts +112 -0
  144. package/src/pgvector/sql/schema.ts +61 -0
  145. package/src/pgvector/sql/select.ts +100 -0
  146. package/src/pgvector/sql/where.ts +152 -0
  147. package/src/pgvector/types.ts +21 -0
  148. package/src/pgvector/utils.ts +24 -0
  149. package/src/postgres.ts +31 -33
  150. package/src/storage.ts +77 -9
  151. package/src/thread/sql.ts +159 -0
  152. package/src/thread/store.ts +40 -127
  153. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,449 @@
1
+ /**
2
+ * Index lifecycle integration tests for pgvector.
3
+ *
4
+ * Tests the full index lifecycle: create, bind, upsert, query,
5
+ * patch, delete documents, delete index.
6
+ */
7
+ import { describe, it, expect, beforeAll, afterAll } from "vitest";
8
+ import { Pool } from "pg";
9
+ import { PGSearchIndex } from "../../search.js";
10
+ const TEST_DB_URL = process.env.KERNL_PG_TEST_URL;
11
+ const SCHEMA = "kernl_lifecycle_integration_test";
12
+ describe.sequential("pgvector index lifecycle integration tests", () => {
13
+ if (!TEST_DB_URL) {
14
+ it.skip("requires KERNL_PG_TEST_URL environment variable", () => { });
15
+ return;
16
+ }
17
+ let pool;
18
+ let pgvec;
19
+ beforeAll(async () => {
20
+ pool = new Pool({ connectionString: TEST_DB_URL });
21
+ await pool.query(`CREATE EXTENSION IF NOT EXISTS vector`);
22
+ await pool.query(`DROP SCHEMA IF EXISTS "${SCHEMA}" CASCADE`);
23
+ await pool.query(`CREATE SCHEMA "${SCHEMA}"`);
24
+ pgvec = new PGSearchIndex({ pool });
25
+ }, 30000);
26
+ afterAll(async () => {
27
+ await pool.query(`DROP SCHEMA IF EXISTS "${SCHEMA}" CASCADE`);
28
+ await pool.end();
29
+ });
30
+ // ============================================================
31
+ // FULL LIFECYCLE TEST
32
+ // ============================================================
33
+ describe("complete index lifecycle", () => {
34
+ const indexId = "lifecycle_test";
35
+ it("creates index with schema", async () => {
36
+ await pgvec.createIndex({
37
+ id: indexId,
38
+ schema: {
39
+ id: { type: "string", pk: true },
40
+ title: { type: "string" },
41
+ views: { type: "int" },
42
+ published: { type: "boolean" },
43
+ embedding: { type: "vector", dimensions: 4, similarity: "cosine" },
44
+ },
45
+ providerOptions: { schema: SCHEMA },
46
+ });
47
+ // Verify table was created
48
+ const tableCheck = await pool.query(`SELECT 1 FROM information_schema.tables
49
+ WHERE table_schema = $1 AND table_name = $2`, [SCHEMA, indexId]);
50
+ expect(tableCheck.rows).toHaveLength(1);
51
+ });
52
+ it("index is immediately usable after creation", async () => {
53
+ const handle = pgvec.index(indexId);
54
+ expect(handle.id).toBe(indexId);
55
+ });
56
+ it("upserts documents", async () => {
57
+ const handle = pgvec.index(indexId);
58
+ const result = await handle.upsert([
59
+ {
60
+ id: "doc-1",
61
+ title: "First Document",
62
+ views: 100,
63
+ published: true,
64
+ embedding: [1, 0, 0, 0],
65
+ },
66
+ {
67
+ id: "doc-2",
68
+ title: "Second Document",
69
+ views: 200,
70
+ published: false,
71
+ embedding: [0, 1, 0, 0],
72
+ },
73
+ {
74
+ id: "doc-3",
75
+ title: "Third Document",
76
+ views: 50,
77
+ published: true,
78
+ embedding: [0, 0, 1, 0],
79
+ },
80
+ ]);
81
+ expect(result.count).toBe(3);
82
+ expect(result.inserted).toBe(3);
83
+ });
84
+ it("queries documents with vector search", async () => {
85
+ const handle = pgvec.index(indexId);
86
+ const hits = await handle.query({
87
+ query: [{ embedding: [1, 0, 0, 0] }],
88
+ topK: 10,
89
+ });
90
+ expect(hits).toHaveLength(3);
91
+ expect(hits[0].id).toBe("doc-1");
92
+ });
93
+ it("queries documents with filter", async () => {
94
+ const handle = pgvec.index(indexId);
95
+ const hits = await handle.query({
96
+ query: [{ embedding: [0.5, 0.5, 0.5, 0.5] }],
97
+ filter: { published: true },
98
+ topK: 10,
99
+ });
100
+ expect(hits).toHaveLength(2);
101
+ for (const hit of hits) {
102
+ expect(hit.document?.published).toBe(true);
103
+ }
104
+ });
105
+ it("patches documents", async () => {
106
+ const handle = pgvec.index(indexId);
107
+ await handle.patch({
108
+ id: "doc-1",
109
+ views: 500,
110
+ title: "Updated First Document",
111
+ });
112
+ const hits = await handle.query({
113
+ filter: { id: "doc-1" },
114
+ topK: 1,
115
+ });
116
+ expect(hits[0].document?.views).toBe(500);
117
+ expect(hits[0].document?.title).toBe("Updated First Document");
118
+ });
119
+ it("deletes documents", async () => {
120
+ const handle = pgvec.index(indexId);
121
+ await handle.delete("doc-3");
122
+ const hits = await handle.query({
123
+ query: [{ embedding: [0.5, 0.5, 0.5, 0.5] }],
124
+ topK: 10,
125
+ });
126
+ expect(hits).toHaveLength(2);
127
+ expect(hits.find((h) => h.id === "doc-3")).toBeUndefined();
128
+ });
129
+ it("describes index", async () => {
130
+ const stats = await pgvec.describeIndex(indexId);
131
+ expect(stats.id).toBe(indexId);
132
+ expect(stats.count).toBe(2);
133
+ expect(stats.dimensions).toBe(4);
134
+ expect(stats.similarity).toBe("cosine");
135
+ expect(stats.status).toBe("ready");
136
+ expect(stats.sizeb).toBeGreaterThan(0);
137
+ });
138
+ it("lists index", async () => {
139
+ const page = await pgvec.listIndexes({ prefix: "lifecycle" });
140
+ expect(page.data).toHaveLength(1);
141
+ expect(page.data[0].id).toBe(indexId);
142
+ expect(page.data[0].status).toBe("ready");
143
+ });
144
+ it("deletes index", async () => {
145
+ await pgvec.deleteIndex(indexId);
146
+ // Verify table was dropped
147
+ const tableCheck = await pool.query(`SELECT 1 FROM information_schema.tables
148
+ WHERE table_schema = $1 AND table_name = $2`, [SCHEMA, indexId]);
149
+ expect(tableCheck.rows).toHaveLength(0);
150
+ });
151
+ it("throws on operations after delete", async () => {
152
+ await expect(pgvec.describeIndex(indexId)).rejects.toThrow(`Index "${indexId}" not bound`);
153
+ });
154
+ });
155
+ // ============================================================
156
+ // BIND EXISTING TABLE
157
+ // ============================================================
158
+ describe("bindIndex", () => {
159
+ const tableId = "existing_table";
160
+ beforeAll(async () => {
161
+ // Create table manually
162
+ await pool.query(`
163
+ CREATE TABLE "${SCHEMA}"."${tableId}" (
164
+ doc_id TEXT PRIMARY KEY,
165
+ doc_title TEXT,
166
+ doc_count INTEGER,
167
+ doc_embedding vector(4)
168
+ )
169
+ `);
170
+ // Insert some data
171
+ await pool.query(`
172
+ INSERT INTO "${SCHEMA}"."${tableId}" (doc_id, doc_title, doc_count, doc_embedding)
173
+ VALUES
174
+ ('existing-1', 'Existing Doc 1', 10, '[0.1, 0.2, 0.3, 0.4]'),
175
+ ('existing-2', 'Existing Doc 2', 20, '[0.5, 0.6, 0.7, 0.8]')
176
+ `);
177
+ });
178
+ it("binds to existing table with custom field mapping", async () => {
179
+ await pgvec.bindIndex("custom_binding", {
180
+ schema: SCHEMA,
181
+ table: tableId,
182
+ pkey: "doc_id",
183
+ fields: {
184
+ id: { column: "doc_id", type: "string" },
185
+ title: { column: "doc_title", type: "string" },
186
+ count: { column: "doc_count", type: "int" },
187
+ embedding: {
188
+ column: "doc_embedding",
189
+ type: "vector",
190
+ dimensions: 4,
191
+ similarity: "cosine",
192
+ },
193
+ },
194
+ });
195
+ const handle = pgvec.index("custom_binding");
196
+ expect(handle.id).toBe("custom_binding");
197
+ });
198
+ it("queries bound table", async () => {
199
+ const handle = pgvec.index("custom_binding");
200
+ const hits = await handle.query({
201
+ query: [{ embedding: [0.1, 0.2, 0.3, 0.4] }],
202
+ topK: 10,
203
+ });
204
+ expect(hits).toHaveLength(2);
205
+ expect(hits[0].id).toBe("existing-1");
206
+ });
207
+ it("upserts to bound table", async () => {
208
+ const handle = pgvec.index("custom_binding");
209
+ await handle.upsert({
210
+ id: "new-doc",
211
+ title: "New Document",
212
+ count: 30,
213
+ embedding: [0.9, 0.9, 0.9, 0.9],
214
+ });
215
+ // Verify in raw SQL
216
+ const result = await pool.query(`SELECT * FROM "${SCHEMA}"."${tableId}" WHERE doc_id = 'new-doc'`);
217
+ expect(result.rows).toHaveLength(1);
218
+ expect(result.rows[0].doc_title).toBe("New Document");
219
+ expect(result.rows[0].doc_count).toBe(30);
220
+ });
221
+ it("respects custom primary key", async () => {
222
+ const handle = pgvec.index("custom_binding");
223
+ // Update existing doc
224
+ await handle.upsert({
225
+ id: "existing-1",
226
+ title: "Updated Existing",
227
+ count: 100,
228
+ embedding: [0.1, 0.2, 0.3, 0.4],
229
+ });
230
+ const result = await pool.query(`SELECT * FROM "${SCHEMA}"."${tableId}" WHERE doc_id = 'existing-1'`);
231
+ expect(result.rows[0].doc_title).toBe("Updated Existing");
232
+ expect(result.rows[0].doc_count).toBe(100);
233
+ });
234
+ it("rebinding updates configuration", async () => {
235
+ // Rebind with different field mapping
236
+ await pgvec.bindIndex("custom_binding", {
237
+ schema: SCHEMA,
238
+ table: tableId,
239
+ pkey: "doc_id",
240
+ fields: {
241
+ id: { column: "doc_id", type: "string" },
242
+ title: { column: "doc_title", type: "string" },
243
+ // count field omitted
244
+ embedding: {
245
+ column: "doc_embedding",
246
+ type: "vector",
247
+ dimensions: 4,
248
+ similarity: "cosine",
249
+ },
250
+ },
251
+ });
252
+ // Should still work
253
+ const handle = pgvec.index("custom_binding");
254
+ const hits = await handle.query({
255
+ query: [{ embedding: [0.5, 0.5, 0.5, 0.5] }],
256
+ topK: 1,
257
+ });
258
+ expect(hits).toHaveLength(1);
259
+ });
260
+ });
261
+ // ============================================================
262
+ // MULTIPLE INDEXES
263
+ // ============================================================
264
+ describe("multiple indexes", () => {
265
+ it("manages multiple indexes independently", async () => {
266
+ // Create two indexes
267
+ await pgvec.createIndex({
268
+ id: "multi_index_a",
269
+ schema: {
270
+ id: { type: "string", pk: true },
271
+ name: { type: "string" },
272
+ embedding: { type: "vector", dimensions: 4 },
273
+ },
274
+ providerOptions: { schema: SCHEMA },
275
+ });
276
+ await pgvec.createIndex({
277
+ id: "multi_index_b",
278
+ schema: {
279
+ id: { type: "string", pk: true },
280
+ title: { type: "string" },
281
+ embedding: { type: "vector", dimensions: 4 },
282
+ },
283
+ providerOptions: { schema: SCHEMA },
284
+ });
285
+ // Insert different data
286
+ const handleA = pgvec.index("multi_index_a");
287
+ const handleB = pgvec.index("multi_index_b");
288
+ await handleA.upsert({ id: "a-1", name: "Index A Doc", embedding: [1, 0, 0, 0] });
289
+ await handleB.upsert({ id: "b-1", title: "Index B Doc", embedding: [0, 1, 0, 0] });
290
+ // Query each
291
+ const hitsA = await handleA.query({
292
+ query: [{ embedding: [1, 0, 0, 0] }],
293
+ topK: 10,
294
+ });
295
+ const hitsB = await handleB.query({
296
+ query: [{ embedding: [0, 1, 0, 0] }],
297
+ topK: 10,
298
+ });
299
+ expect(hitsA).toHaveLength(1);
300
+ expect(hitsA[0].document?.name).toBe("Index A Doc");
301
+ expect(hitsB).toHaveLength(1);
302
+ expect(hitsB[0].document?.title).toBe("Index B Doc");
303
+ // Delete one doesn't affect the other
304
+ await pgvec.deleteIndex("multi_index_a");
305
+ const stillExists = await handleB.query({
306
+ query: [{ embedding: [0, 1, 0, 0] }],
307
+ topK: 10,
308
+ });
309
+ expect(stillExists).toHaveLength(1);
310
+ // Cleanup
311
+ await pgvec.deleteIndex("multi_index_b");
312
+ });
313
+ it("lists multiple indexes with pagination", async () => {
314
+ // Create several indexes
315
+ for (let i = 0; i < 5; i++) {
316
+ await pgvec.createIndex({
317
+ id: `paginated_${i}`,
318
+ schema: {
319
+ id: { type: "string", pk: true },
320
+ embedding: { type: "vector", dimensions: 4 },
321
+ },
322
+ providerOptions: { schema: SCHEMA },
323
+ });
324
+ }
325
+ // List with limit
326
+ const page1 = await pgvec.listIndexes({ prefix: "paginated_", limit: 2 });
327
+ expect(page1.data).toHaveLength(2);
328
+ expect(page1.last).toBe(false);
329
+ const page2 = await page1.next();
330
+ expect(page2).not.toBeNull();
331
+ expect(page2.data).toHaveLength(2);
332
+ const page3 = await page2.next();
333
+ expect(page3).not.toBeNull();
334
+ expect(page3.data).toHaveLength(1);
335
+ expect(page3.last).toBe(true);
336
+ // Cleanup
337
+ for (let i = 0; i < 5; i++) {
338
+ await pgvec.deleteIndex(`paginated_${i}`);
339
+ }
340
+ });
341
+ });
342
+ // ============================================================
343
+ // SCHEMA VARIATIONS
344
+ // ============================================================
345
+ describe("schema variations", () => {
346
+ it("creates index with all field types", async () => {
347
+ await pgvec.createIndex({
348
+ id: "all_types",
349
+ schema: {
350
+ id: { type: "string", pk: true },
351
+ str_field: { type: "string" },
352
+ int_field: { type: "int" },
353
+ float_field: { type: "float" },
354
+ bool_field: { type: "boolean" },
355
+ date_field: { type: "date" },
356
+ embedding: { type: "vector", dimensions: 4 },
357
+ },
358
+ providerOptions: { schema: SCHEMA },
359
+ });
360
+ // Verify column types
361
+ const result = await pool.query(`SELECT column_name, data_type
362
+ FROM information_schema.columns
363
+ WHERE table_schema = $1 AND table_name = $2
364
+ ORDER BY ordinal_position`, [SCHEMA, "all_types"]);
365
+ const columns = Object.fromEntries(result.rows.map((r) => [r.column_name, r.data_type]));
366
+ expect(columns.str_field).toBe("text");
367
+ expect(columns.int_field).toBe("integer");
368
+ expect(columns.float_field).toBe("double precision");
369
+ expect(columns.bool_field).toBe("boolean");
370
+ expect(columns.date_field).toBe("timestamp with time zone");
371
+ await pgvec.deleteIndex("all_types");
372
+ });
373
+ it("creates index with high-dimensional vectors", async () => {
374
+ await pgvec.createIndex({
375
+ id: "high_dim",
376
+ schema: {
377
+ id: { type: "string", pk: true },
378
+ embedding: { type: "vector", dimensions: 1536, similarity: "cosine" },
379
+ },
380
+ providerOptions: { schema: SCHEMA },
381
+ });
382
+ const handle = pgvec.index("high_dim");
383
+ const vec = new Array(1536).fill(0.1);
384
+ await handle.upsert({ id: "high-1", embedding: vec });
385
+ const hits = await handle.query({
386
+ query: [{ embedding: vec }],
387
+ topK: 1,
388
+ });
389
+ expect(hits).toHaveLength(1);
390
+ expect(hits[0].document?.embedding).toHaveLength(1536);
391
+ await pgvec.deleteIndex("high_dim");
392
+ });
393
+ it("creates index with multiple vector fields", async () => {
394
+ await pgvec.createIndex({
395
+ id: "multi_vec",
396
+ schema: {
397
+ id: { type: "string", pk: true },
398
+ title_embedding: { type: "vector", dimensions: 4, similarity: "cosine" },
399
+ content_embedding: { type: "vector", dimensions: 4, similarity: "euclidean" },
400
+ },
401
+ providerOptions: { schema: SCHEMA },
402
+ });
403
+ const handle = pgvec.index("multi_vec");
404
+ await handle.upsert({
405
+ id: "mv-1",
406
+ title_embedding: [1, 0, 0, 0],
407
+ content_embedding: [0, 1, 0, 0],
408
+ });
409
+ // Query on first vector field
410
+ const hits = await handle.query({
411
+ query: [{ title_embedding: [1, 0, 0, 0] }],
412
+ topK: 1,
413
+ });
414
+ expect(hits).toHaveLength(1);
415
+ await pgvec.deleteIndex("multi_vec");
416
+ });
417
+ });
418
+ // ============================================================
419
+ // PERSISTENCE ACROSS INSTANCES
420
+ // ============================================================
421
+ describe("persistence", () => {
422
+ it("loads existing indexes on new instance", async () => {
423
+ // Create index with first instance
424
+ await pgvec.createIndex({
425
+ id: "persist_test",
426
+ schema: {
427
+ id: { type: "string", pk: true },
428
+ name: { type: "string" },
429
+ embedding: { type: "vector", dimensions: 4 },
430
+ },
431
+ providerOptions: { schema: SCHEMA },
432
+ });
433
+ const handle1 = pgvec.index("persist_test");
434
+ await handle1.upsert({ id: "p-1", name: "Persisted", embedding: [1, 0, 0, 0] });
435
+ // Create new instance
436
+ const pgvec2 = new PGSearchIndex({ pool });
437
+ // Should be able to access the index
438
+ const stats = await pgvec2.describeIndex("persist_test");
439
+ expect(stats.count).toBe(1);
440
+ const handle2 = pgvec2.index("persist_test");
441
+ const hits = await handle2.query({
442
+ query: [{ embedding: [1, 0, 0, 0] }],
443
+ topK: 1,
444
+ });
445
+ expect(hits[0].document?.name).toBe("Persisted");
446
+ await pgvec.deleteIndex("persist_test");
447
+ });
448
+ });
449
+ });
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Query behavior integration tests for pgvector.
3
+ *
4
+ * Tests vector search, topK behavior, offset pagination, orderBy,
5
+ * and result structure against real PostgreSQL.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=query.integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.integration.test.d.ts","sourceRoot":"","sources":["../../../../src/pgvector/__tests__/integration/query.integration.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}