mirror of
https://github.com/pgvector/pgvector.git
synced 2026-07-22 20:15:46 +08:00
Compare commits
1 Commits
hqann
...
hqann-bigi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8fcf77f89a |
@@ -4,7 +4,15 @@
|
||||
CREATE FUNCTION hnsw_attribute_distance(integer, integer) RETURNS float8
|
||||
AS 'MODULE_PATHNAME', 'hnsw_int4_attribute_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE FUNCTION hnsw_attribute_distance(bigint, bigint) RETURNS float8
|
||||
AS 'MODULE_PATHNAME', 'hnsw_int8_attribute_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE OPERATOR CLASS vector_integer_ops
|
||||
DEFAULT FOR TYPE integer USING hnsw AS
|
||||
OPERATOR 2 = (integer, integer),
|
||||
FUNCTION 3 hnsw_attribute_distance(integer, integer);
|
||||
|
||||
CREATE OPERATOR CLASS vector_bigint_ops
|
||||
DEFAULT FOR TYPE bigint USING hnsw AS
|
||||
OPERATOR 2 = (bigint, bigint),
|
||||
FUNCTION 3 hnsw_attribute_distance(bigint, bigint);
|
||||
|
||||
@@ -296,7 +296,15 @@ CREATE OPERATOR CLASS vector_cosine_ops
|
||||
CREATE FUNCTION hnsw_attribute_distance(integer, integer) RETURNS float8
|
||||
AS 'MODULE_PATHNAME', 'hnsw_int4_attribute_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE FUNCTION hnsw_attribute_distance(bigint, bigint) RETURNS float8
|
||||
AS 'MODULE_PATHNAME', 'hnsw_int8_attribute_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE OPERATOR CLASS vector_integer_ops
|
||||
DEFAULT FOR TYPE integer USING hnsw AS
|
||||
OPERATOR 2 = (integer, integer),
|
||||
FUNCTION 3 hnsw_attribute_distance(integer, integer);
|
||||
|
||||
CREATE OPERATOR CLASS vector_bigint_ops
|
||||
DEFAULT FOR TYPE bigint USING hnsw AS
|
||||
OPERATOR 2 = (bigint, bigint),
|
||||
FUNCTION 3 hnsw_attribute_distance(bigint, bigint);
|
||||
|
||||
14
src/hnsw.c
14
src/hnsw.c
@@ -236,3 +236,17 @@ hnsw_int4_attribute_distance(PG_FUNCTION_ARGS)
|
||||
|
||||
PG_RETURN_FLOAT8(distance);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the distance between two int8 attributes
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(hnsw_int8_attribute_distance);
|
||||
Datum
|
||||
hnsw_int8_attribute_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int64 a = PG_GETARG_INT64(0);
|
||||
int64 b = PG_GETARG_INT64(1);
|
||||
double distance = ((double) a) - ((double) b);
|
||||
|
||||
PG_RETURN_FLOAT8(distance);
|
||||
}
|
||||
|
||||
@@ -114,7 +114,6 @@ typedef struct HnswCandidate
|
||||
{
|
||||
HnswElement element;
|
||||
float distance;
|
||||
bool matches;
|
||||
bool closer;
|
||||
} HnswCandidate;
|
||||
|
||||
@@ -289,7 +288,7 @@ void HnswInitNeighbors(HnswElement element, int m);
|
||||
bool HnswInsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid, Relation heapRel);
|
||||
void HnswUpdateNeighborPages(Relation index, FmgrInfo **procinfos, Oid *collations, HnswElement e, int m, bool checkExisting);
|
||||
void HnswLoadElementFromTuple(HnswElement element, HnswElementTuple etup, bool loadHeaptids, bool loadVec, Relation index);
|
||||
void HnswLoadElement(HnswElement element, float *distance, bool *matches, Datum *q, IndexTuple qtup, ScanKeyData *keyData, Relation index, FmgrInfo **procinfos, Oid *collations, bool loadVec);
|
||||
void HnswLoadElement(HnswElement element, float *distance, Datum *q, IndexTuple qtup, ScanKeyData *keyData, Relation index, FmgrInfo **procinfos, Oid *collations, bool loadVec);
|
||||
void HnswSetElementTuple(HnswElementTuple etup, HnswElement element, bool useIndexTuple);
|
||||
void HnswUpdateConnection(HnswElement element, HnswCandidate * hc, int m, int lc, int *updateIdx, Relation index, FmgrInfo **procinfos, Oid *collations, bool inMemory);
|
||||
void HnswLoadNeighbors(HnswElement element, Relation index, int m);
|
||||
|
||||
@@ -545,13 +545,10 @@ AttributeDistance(double e)
|
||||
* Get the distance
|
||||
*/
|
||||
static double
|
||||
GetDistance(IndexTuple itup, Datum vec, Datum q, IndexTuple qtup, ScanKeyData *keyData, Relation index, FmgrInfo **procinfos, Oid *collations, bool *matches)
|
||||
GetDistance(IndexTuple itup, Datum vec, Datum q, IndexTuple qtup, ScanKeyData *keyData, Relation index, FmgrInfo **procinfos, Oid *collations)
|
||||
{
|
||||
double g = DatumGetFloat8(FunctionCall2Coll(procinfos[0], collations[0], q, vec));
|
||||
|
||||
Assert(PointerIsValid(matches));
|
||||
*matches = true;
|
||||
|
||||
if (IndexRelationGetNumberOfKeyAttributes(index) > 1)
|
||||
{
|
||||
double w = 0.25;
|
||||
@@ -573,10 +570,7 @@ GetDistance(IndexTuple itup, Datum vec, Datum q, IndexTuple qtup, ScanKeyData *k
|
||||
if (isnull || attnull)
|
||||
{
|
||||
if (isnull != attnull)
|
||||
{
|
||||
e += 1000;
|
||||
*matches = false;
|
||||
}
|
||||
}
|
||||
else if (!DatumGetBool(FunctionCall2Coll(&key->sk_func, key->sk_collation, value, key->sk_argument)))
|
||||
{
|
||||
@@ -587,8 +581,6 @@ GetDistance(IndexTuple itup, Datum vec, Datum q, IndexTuple qtup, ScanKeyData *k
|
||||
else
|
||||
/* Distance is zero for inequality */
|
||||
e += 1000;
|
||||
|
||||
*matches = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -625,7 +617,7 @@ GetDistance(IndexTuple itup, Datum vec, Datum q, IndexTuple qtup, ScanKeyData *k
|
||||
* Load an element and optionally get its distance from q
|
||||
*/
|
||||
void
|
||||
HnswLoadElement(HnswElement element, float *distance, bool *matches, Datum *q, IndexTuple qtup, ScanKeyData *keyData, Relation index, FmgrInfo **procinfos, Oid *collations, bool loadVec)
|
||||
HnswLoadElement(HnswElement element, float *distance, Datum *q, IndexTuple qtup, ScanKeyData *keyData, Relation index, FmgrInfo **procinfos, Oid *collations, bool loadVec)
|
||||
{
|
||||
Buffer buf;
|
||||
Page page;
|
||||
@@ -660,7 +652,7 @@ HnswLoadElement(HnswElement element, float *distance, bool *matches, Datum *q, I
|
||||
else
|
||||
value = PointerGetDatum(&etup->data);
|
||||
|
||||
*distance = GetDistance(itup, value, *q, qtup, keyData, index, procinfos, collations, matches);
|
||||
*distance = GetDistance(itup, value, *q, qtup, keyData, index, procinfos, collations);
|
||||
}
|
||||
|
||||
UnlockReleaseBuffer(buf);
|
||||
@@ -672,7 +664,7 @@ HnswLoadElement(HnswElement element, float *distance, bool *matches, Datum *q, I
|
||||
static float
|
||||
GetCandidateDistance(HnswCandidate * hc, Datum q, IndexTuple qtup, ScanKeyData *keyData, Relation index, FmgrInfo **procinfos, Oid *collations)
|
||||
{
|
||||
return GetDistance(hc->element->itup, hc->element->value, q, qtup, keyData, index, procinfos, collations, &hc->matches);
|
||||
return GetDistance(hc->element->itup, hc->element->value, q, qtup, keyData, index, procinfos, collations);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -687,7 +679,7 @@ HnswEntryCandidate(HnswElement entryPoint, Datum q, IndexTuple qtup, ScanKeyData
|
||||
if (inMemory)
|
||||
hc->distance = GetCandidateDistance(hc, q, qtup, keyData, index, procinfos, collations);
|
||||
else
|
||||
HnswLoadElement(hc->element, &hc->distance, &hc->matches, &q, qtup, keyData, index, procinfos, collations, loadVec);
|
||||
HnswLoadElement(hc->element, &hc->distance, &q, qtup, keyData, index, procinfos, collations, loadVec);
|
||||
return hc;
|
||||
}
|
||||
|
||||
@@ -762,8 +754,6 @@ HnswSearchLayer(Datum q, IndexTuple qtup, ScanKeyData *keyData, List *ep, int ef
|
||||
pairingheap *C = pairingheap_allocate(CompareNearestCandidates, NULL);
|
||||
pairingheap *W = pairingheap_allocate(CompareFurthestCandidates, NULL);
|
||||
int wlen = 0;
|
||||
uint64 additional = 0;
|
||||
uint64 maxAdditional = keyData ? 4 * ef : 0;
|
||||
HASHCTL hash_ctl;
|
||||
HTAB *v;
|
||||
|
||||
@@ -792,13 +782,6 @@ HnswSearchLayer(Datum q, IndexTuple qtup, ScanKeyData *keyData, List *ep, int ef
|
||||
pairingheap_add(C, &(CreatePairingHeapNode(hc)->ph_node));
|
||||
pairingheap_add(W, &(CreatePairingHeapNode(hc)->ph_node));
|
||||
|
||||
/* Do not count elements that do not match filter towards ef */
|
||||
if (!hc->matches)
|
||||
{
|
||||
if ((++additional) <= maxAdditional)
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do not count elements being deleted towards ef when vacuuming. It
|
||||
* would be ideal to do this for inserts as well, but this could
|
||||
@@ -839,7 +822,7 @@ HnswSearchLayer(Datum q, IndexTuple qtup, ScanKeyData *keyData, List *ep, int ef
|
||||
if (inMemory)
|
||||
eDistance = GetCandidateDistance(e, q, qtup, keyData, index, procinfos, collations);
|
||||
else
|
||||
HnswLoadElement(e->element, &eDistance, &e->matches, &q, qtup, keyData, index, procinfos, collations, loadVec);
|
||||
HnswLoadElement(e->element, &eDistance, &q, qtup, keyData, index, procinfos, collations, loadVec);
|
||||
|
||||
Assert(!e->element->deleted);
|
||||
|
||||
@@ -865,16 +848,6 @@ HnswSearchLayer(Datum q, IndexTuple qtup, ScanKeyData *keyData, List *ep, int ef
|
||||
*/
|
||||
if (skipElement == NULL || list_length(e->element->heaptids) != 0)
|
||||
{
|
||||
/*
|
||||
* Do not count elements that do not match filter
|
||||
* towards ef
|
||||
*/
|
||||
if (!e->matches)
|
||||
{
|
||||
if ((++additional) <= maxAdditional)
|
||||
continue;
|
||||
}
|
||||
|
||||
wlen++;
|
||||
|
||||
/* No need to decrement wlen */
|
||||
@@ -931,8 +904,6 @@ CompareCandidateDistances(const void *a, const void *b)
|
||||
static float
|
||||
HnswGetCachedDistance(HnswElement a, HnswElement b, int lc, Relation index, FmgrInfo **procinfos, Oid *collations)
|
||||
{
|
||||
bool matches;
|
||||
|
||||
/* Look for cached distance */
|
||||
if (a->neighbors != NULL)
|
||||
{
|
||||
@@ -956,7 +927,7 @@ HnswGetCachedDistance(HnswElement a, HnswElement b, int lc, Relation index, Fmgr
|
||||
}
|
||||
}
|
||||
|
||||
return GetDistance(a->itup, a->value, b->value, b->itup, NULL, index, procinfos, collations, &matches);
|
||||
return GetDistance(a->itup, a->value, b->value, b->itup, NULL, index, procinfos, collations);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1149,7 +1120,7 @@ HnswUpdateConnection(HnswElement element, HnswCandidate * hc, int m, int lc, int
|
||||
HnswCandidate *hc3 = ¤tNeighbors->items[i];
|
||||
|
||||
if (DatumGetPointer(hc3->element->value) == NULL)
|
||||
HnswLoadElement(hc3->element, &hc3->distance, &hc3->matches, &q, qtup, keyData, index, procinfos, collations, true);
|
||||
HnswLoadElement(hc3->element, &hc3->distance, &q, qtup, keyData, index, procinfos, collations, true);
|
||||
else
|
||||
hc3->distance = GetCandidateDistance(hc3, q, qtup, keyData, index, procinfos, collations);
|
||||
|
||||
|
||||
@@ -258,7 +258,7 @@ RepairGraphEntryPoint(HnswVacuumState * vacuumstate)
|
||||
LockPage(index, HNSW_UPDATE_LOCK, ShareLock);
|
||||
|
||||
/* Load element */
|
||||
HnswLoadElement(highestPoint, NULL, NULL, NULL, NULL, NULL, index, vacuumstate->procinfos, vacuumstate->collations, true);
|
||||
HnswLoadElement(highestPoint, NULL, NULL, NULL, NULL, index, vacuumstate->procinfos, vacuumstate->collations, true);
|
||||
|
||||
/* Repair if needed */
|
||||
if (NeedsUpdated(vacuumstate, highestPoint))
|
||||
@@ -296,7 +296,7 @@ RepairGraphEntryPoint(HnswVacuumState * vacuumstate)
|
||||
* is outdated, this can remove connections at higher levels in
|
||||
* the graph until they are repaired, but this should be fine.
|
||||
*/
|
||||
HnswLoadElement(entryPoint, NULL, NULL, NULL, NULL, NULL, index, vacuumstate->procinfos, vacuumstate->collations, true);
|
||||
HnswLoadElement(entryPoint, NULL, NULL, NULL, NULL, index, vacuumstate->procinfos, vacuumstate->collations, true);
|
||||
|
||||
if (NeedsUpdated(vacuumstate, entryPoint))
|
||||
{
|
||||
@@ -442,7 +442,7 @@ MarkDeleted(HnswVacuumState * vacuumstate)
|
||||
BlockNumber insertPage = InvalidBlockNumber;
|
||||
Relation index = vacuumstate->index;
|
||||
BufferAccessStrategy bas = vacuumstate->bas;
|
||||
bool useIndexTuple = IndexRelationGetNumberOfAttributes(index) > 1;
|
||||
bool useIndexTuple = IndexRelationGetNumberOfAttributes(index);
|
||||
|
||||
/*
|
||||
* Wait for index scans to complete. Scans before this point may contain
|
||||
|
||||
@@ -34,8 +34,6 @@ sub test_recall
|
||||
my @actual_ids = split("\n", $actual);
|
||||
my %actual_set = map { $_ => 1 } @actual_ids;
|
||||
|
||||
is(scalar(@actual_ids), $limit);
|
||||
|
||||
my @expected_ids = split("\n", $expected[$i]);
|
||||
|
||||
foreach (@expected_ids)
|
||||
@@ -58,7 +56,7 @@ $node->start;
|
||||
|
||||
# Create table
|
||||
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
|
||||
$node->safe_psql("postgres", "CREATE TABLE tst (i int4, v vector($dim), c int4);");
|
||||
$node->safe_psql("postgres", "CREATE TABLE tst (i int4, v vector($dim), c int8);");
|
||||
$node->safe_psql("postgres",
|
||||
"INSERT INTO tst SELECT i, ARRAY[$array_sql], i % $nc FROM generate_series(1, 20000) i;"
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user