Skip to content

Commit

Permalink
Added l1Distance, hammingDistance, and jaccardDistance functions for …
Browse files Browse the repository at this point in the history
…Objection.js
  • Loading branch information
ankane committed May 7, 2024
1 parent 6a3cf60 commit 8d354f8
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
- Added support for passing literals to distance functions with Sequelize
- Added support for `halfvec` and `sparsevec` types to Knex.js
- Added `l1Distance`, `hammingDistance`, and `jaccardDistance` functions for Knex.js
- Added `l1Distance`, `hammingDistance`, and `jaccardDistance` functions for Objection.js
- Dropped support for Node < 18

## 0.1.8 (2024-02-11)
Expand Down
14 changes: 13 additions & 1 deletion src/objection/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,16 @@ function cosineDistance(column, value) {
return raw('?? <=> ?', [column, toSql(value)]);
}

module.exports = {fromSql, toSql, l2Distance, maxInnerProduct, cosineDistance};
function l1Distance(column, value) {
return raw('?? <+> ?', [column, toSql(value)]);
}

function hammingDistance(column, value) {
return raw('?? <~> ?', [column, value]);
}

function jaccardDistance(column, value) {
return raw('?? <%> ?', [column, value]);
}

module.exports = {fromSql, toSql, l2Distance, maxInnerProduct, cosineDistance, l1Distance, hammingDistance, jaccardDistance};
27 changes: 23 additions & 4 deletions tests/objection/index.test.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Knex from 'knex';
import { Model } from 'objection';
import pgvector from 'pgvector/objection';
import { l2Distance, maxInnerProduct, cosineDistance } from 'pgvector/objection';
import { l2Distance, maxInnerProduct, cosineDistance, l1Distance, hammingDistance, jaccardDistance } from 'pgvector/objection';

test('example', async () => {
const knex = Knex({
Expand All @@ -22,12 +22,13 @@ test('example', async () => {
await knex.schema.createTable('objection_items', (table) => {
table.increments('id');
table.vector('embedding', 3);
table.bit('binary_embedding', {length: 3});
});

const newItems = [
{embedding: pgvector.toSql([1, 1, 1])},
{embedding: pgvector.toSql([2, 2, 2])},
{embedding: pgvector.toSql([1, 1, 2])},
{embedding: pgvector.toSql([1, 1, 1]), binary_embedding: '000'},
{embedding: pgvector.toSql([2, 2, 2]), binary_embedding: '101'},
{embedding: pgvector.toSql([1, 1, 2]), binary_embedding: '111'},
{embedding: null}
];
await Item.query().insert(newItems);
Expand All @@ -53,6 +54,24 @@ test('example', async () => {
.limit(5);
expect(items.map(v => v.id).slice(2)).toStrictEqual([3, 4]);

// L1 distance
items = await Item.query()
.orderBy(l1Distance('embedding', [1, 1, 1]))
.limit(5);
expect(items.map(v => v.id)).toStrictEqual([1, 3, 2, 4]);

// Hamming distance
items = await Item.query()
.orderBy(hammingDistance('binary_embedding', '101'))
.limit(5);
expect(items.map(v => v.id)).toStrictEqual([2, 3, 1, 4]);

// Jaccard distance
items = await Item.query()
.orderBy(jaccardDistance('binary_embedding', '101'))
.limit(5);
expect(items.map(v => v.id)).toStrictEqual([2, 3, 1, 4]);

await knex.schema.alterTable('objection_items', function(table) {
table.index(knex.raw('embedding vector_l2_ops'), 'objection_items_embedding_idx', 'hnsw');
});
Expand Down

0 comments on commit 8d354f8

Please sign in to comment.