Skip to content

Commit f52b5d3

Browse files
authored
Merge pull request #796 from constructive-io/devin/1773291168-vector-filter-migration
feat(pgvector): move vector search from condition to filter argument
2 parents 4fd2c9b + 8192be6 commit f52b5d3

5 files changed

Lines changed: 45 additions & 43 deletions

File tree

graphile/graphile-pgvector-plugin/package.json

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,7 @@
4646
"postgraphile": "5.0.0-rc.7",
4747
"postgraphile-plugin-connection-filter": "3.0.0-rc.1"
4848
},
49-
"peerDependenciesMeta": {
50-
"postgraphile-plugin-connection-filter": {
51-
"optional": true
52-
}
53-
},
49+
"peerDependenciesMeta": {},
5450
"keywords": [
5551
"postgraphile",
5652
"graphile",

graphile/graphile-pgvector-plugin/src/__tests__/vector-search.test.ts

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { join } from 'path';
22
import { getConnections, seed } from 'graphile-test';
33
import type { GraphQLResponse } from 'graphile-test';
44
import type { PgTestClient } from 'pgsql-test';
5+
import { PostGraphileConnectionFilterPreset } from 'postgraphile-plugin-connection-filter';
56
import { VectorCodecPreset } from '../vector-codec';
67
import { createVectorSearchPlugin } from '../vector-search';
78

@@ -30,6 +31,7 @@ describe('VectorSearchPlugin', () => {
3031
beforeAll(async () => {
3132
const testPreset = {
3233
extends: [
34+
PostGraphileConnectionFilterPreset,
3335
VectorCodecPreset,
3436
{
3537
plugins: [createVectorSearchPlugin({ defaultMetric: 'COSINE' })],
@@ -76,11 +78,11 @@ describe('VectorSearchPlugin', () => {
7678
await db.afterEach();
7779
});
7880

79-
describe('condition field (vectorEmbedding)', () => {
81+
describe('filter field (vectorEmbedding)', () => {
8082
it('filters by vector similarity with distance threshold', async () => {
8183
const result = await query<AllDocumentsResult>(`
8284
query {
83-
allDocuments(condition: {
85+
allDocuments(filter: {
8486
vectorEmbedding: {
8587
vector: [1, 0, 0]
8688
metric: COSINE
@@ -107,10 +109,10 @@ describe('VectorSearchPlugin', () => {
107109
expect(titles).toContain('Document A');
108110
});
109111

110-
it('returns embeddingDistance computed field when condition is active', async () => {
112+
it('returns embeddingDistance computed field when filter is active', async () => {
111113
const result = await query<AllDocumentsResult>(`
112114
query {
113-
allDocuments(condition: {
115+
allDocuments(filter: {
114116
vectorEmbedding: {
115117
vector: [1, 0, 0]
116118
metric: COSINE
@@ -140,7 +142,7 @@ describe('VectorSearchPlugin', () => {
140142
expect(docA!.embeddingDistance).toBeCloseTo(0, 2);
141143
});
142144

143-
it('returns null for embeddingDistance when no condition is active', async () => {
145+
it('returns null for embeddingDistance when no filter is active', async () => {
144146
const result = await query<AllDocumentsResult>(`
145147
query {
146148
allDocuments {
@@ -164,7 +166,7 @@ describe('VectorSearchPlugin', () => {
164166
it('supports L2 metric', async () => {
165167
const result = await query<AllDocumentsResult>(`
166168
query {
167-
allDocuments(condition: {
169+
allDocuments(filter: {
168170
vectorEmbedding: {
169171
vector: [1, 0, 0]
170172
metric: L2
@@ -191,7 +193,7 @@ describe('VectorSearchPlugin', () => {
191193
it('supports IP metric', async () => {
192194
const result = await query<AllDocumentsResult>(`
193195
query {
194-
allDocuments(condition: {
196+
allDocuments(filter: {
195197
vectorEmbedding: {
196198
vector: [1, 0, 0]
197199
metric: IP
@@ -217,11 +219,11 @@ describe('VectorSearchPlugin', () => {
217219
});
218220

219221
describe('orderBy (EMBEDDING_DISTANCE_ASC/DESC)', () => {
220-
it('orders by distance ascending when condition is active', async () => {
222+
it('orders by distance ascending when filter is active', async () => {
221223
const result = await query<AllDocumentsResult>(`
222224
query {
223225
allDocuments(
224-
condition: {
226+
filter: {
225227
vectorEmbedding: {
226228
vector: [1, 0, 0]
227229
metric: COSINE
@@ -254,11 +256,11 @@ describe('VectorSearchPlugin', () => {
254256
}
255257
});
256258

257-
it('orders by distance descending when condition is active', async () => {
259+
it('orders by distance descending when filter is active', async () => {
258260
const result = await query<AllDocumentsResult>(`
259261
query {
260262
allDocuments(
261-
condition: {
263+
filter: {
262264
vectorEmbedding: {
263265
vector: [1, 0, 0]
264266
metric: COSINE
@@ -298,7 +300,7 @@ describe('VectorSearchPlugin', () => {
298300
const result = await query<AllDocumentsResult>(`
299301
query {
300302
allDocuments(
301-
condition: {
303+
filter: {
302304
vectorEmbedding: {
303305
vector: [1, 0, 0]
304306
metric: COSINE
@@ -340,7 +342,7 @@ describe('VectorSearchPlugin', () => {
340342
const result = await query<AllDocumentsResult>(`
341343
query {
342344
allDocuments(
343-
condition: {
345+
filter: {
344346
vectorEmbedding: {
345347
vector: [1, 0, 0]
346348
metric: COSINE

graphile/graphile-pgvector-plugin/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
* - A `Vector` GraphQL scalar (serialized as [Float]) handles I/O
1212
*
1313
* 2. **VectorSearchPlugin** — Auto-discovers all vector columns and adds:
14-
* - `<column>Nearby` condition fields on connections (filter by distance)
14+
* - `<column>Nearby` filter fields on connections (filter by distance)
1515
* - `<column>Distance` computed fields on output types
1616
* - `<COLUMN>_DISTANCE_ASC/DESC` orderBy enum values
17-
* - `closeTo` connection filter operator for the Vector scalar
17+
* Requires postgraphile-plugin-connection-filter to be loaded first.
1818
*
1919
* @example
2020
* ```typescript

graphile/graphile-pgvector-plugin/src/types.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ export interface VectorSearchPluginOptions {
2929
maxLimit?: number;
3030

3131
/**
32-
* Prefix for vector condition fields on connection condition inputs.
32+
* Prefix for vector filter fields on connection filter inputs.
3333
* For example, with prefix 'vector' and a column named 'embedding',
34-
* the generated condition field will be 'vectorEmbedding'.
34+
* the generated filter field will be 'vectorEmbedding'.
3535
* @default 'vector'
3636
*/
37-
conditionPrefix?: string;
37+
filterPrefix?: string;
3838
}

graphile/graphile-pgvector-plugin/src/vector-search.ts

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,22 @@
33
*
44
* Auto-discovers all `vector` columns across all tables and adds:
55
*
6-
* 1. **vectorSearch<TableType>** query fields on Query
7-
* - Accepts a query vector, metric, limit, offset
8-
* - Returns rows ordered by distance with a `distance` score
9-
*
10-
* 2. **<column>Nearby** condition fields on connection condition inputs
6+
* 1. **<column>Nearby** filter fields on connection filter inputs
117
* - Accepts { vector, metric?, distance? } to filter by distance threshold
128
* - Computes distance server-side using pgvector operators
9+
* - Lives inside the `filter` argument (via postgraphile-plugin-connection-filter)
1310
*
14-
* 3. **<column>Distance** computed fields on output types
15-
* - Returns the distance value when a nearby condition is active (null otherwise)
11+
* 2. **<column>Distance** computed fields on output types
12+
* - Returns the distance value when a nearby filter is active (null otherwise)
1613
*
17-
* 4. **<COLUMN>_DISTANCE_ASC/DESC** orderBy enum values
18-
* - Orders results by vector distance when a nearby condition is active
14+
* 3. **<COLUMN>_DISTANCE_ASC/DESC** orderBy enum values
15+
* - Orders results by vector distance when a nearby filter is active
1916
*
2017
* Uses the Grafast meta system (setMeta/getMeta) to pass data between
21-
* the condition apply phase and the output field plan, following the
18+
* the filter apply phase and the output field plan, following the
2219
* pattern from Benjie's postgraphile-plugin-fulltext-filter reference.
2320
*
24-
* Follows the same patterns as graphile-search-plugin (for tsvector columns).
21+
* Requires postgraphile-plugin-connection-filter to be loaded first.
2522
*/
2623

2724
import 'graphile-build';
@@ -133,17 +130,19 @@ export function createVectorSearchPlugin(
133130
const {
134131
defaultMetric = 'COSINE',
135132
maxLimit = 100,
136-
conditionPrefix = 'vector',
133+
filterPrefix = 'vector',
137134
} = options;
138135

139136
return {
140137
name: 'VectorSearchPlugin',
141138
version: '1.0.0',
142139
description:
143-
'Auto-discovers vector columns and adds search fields, conditions, and orderBy',
140+
'Auto-discovers vector columns and adds filter fields, distance computed fields, and orderBy',
144141
after: [
145142
'VectorCodecPlugin',
146143
'PgAttributesPlugin',
144+
'PgConnectionArgFilterPlugin',
145+
'PgConnectionArgFilterAttributesPlugin',
147146
],
148147

149148
// ─── Custom Inflection Methods ─────────────────────────────────────
@@ -454,18 +453,23 @@ export function createVectorSearchPlugin(
454453
},
455454

456455
/**
457-
* Add `<column>Nearby` condition fields on connection condition input types
456+
* Add `<column>Nearby` filter fields on connection filter input types
458457
* for tables with vector columns.
458+
*
459+
* Uses the connection filter plugin's `isPgConnectionFilter` scope.
460+
* The apply function receives a PgCondition wrapping PgSelectStep,
461+
* identical to the condition approach — so we can use the same
462+
* getQueryBuilder() traversal for selectAndReturnIndex/setMeta/orderBy.
459463
*/
460464
GraphQLInputObjectType_fields(fields, build, context) {
461465
const { inflection, sql } = build;
462466
const {
463-
scope: { isPgCondition, pgCodec },
467+
scope: { isPgConnectionFilter, pgCodec } = {} as any,
464468
fieldWithHooks,
465469
} = context;
466470

467471
if (
468-
!isPgCondition ||
472+
!isPgConnectionFilter ||
469473
!pgCodec ||
470474
!pgCodec.attributes ||
471475
pgCodec.isAnonymous
@@ -487,7 +491,7 @@ export function createVectorSearchPlugin(
487491

488492
for (const [attributeName] of vectorAttributes) {
489493
const fieldName = inflection.camelCase(
490-
`${conditionPrefix}_${attributeName}`
494+
`${filterPrefix}_${attributeName}`
491495
);
492496
const baseFieldName = inflection.attribute({
493497
codec: pgCodec as any,
@@ -501,8 +505,8 @@ export function createVectorSearchPlugin(
501505
[fieldName]: fieldWithHooks(
502506
{
503507
fieldName,
504-
isPgConnectionConditionInputField: true,
505-
},
508+
isPgConnectionFilterField: true,
509+
} as any,
506510
{
507511
description: build.wrapDescription(
508512
`Vector similarity search on the \`${attributeName}\` column. ` +
@@ -583,7 +587,7 @@ export function createVectorSearchPlugin(
583587
}
584588
),
585589
},
586-
`VectorSearchPlugin adding condition field '${fieldName}' for vector column '${attributeName}' on '${pgCodec.name}'`
590+
`VectorSearchPlugin adding filter field '${fieldName}' for vector column '${attributeName}' on '${pgCodec.name}'`
587591
);
588592
}
589593

0 commit comments

Comments
 (0)