mirror of
https://github.com/postgres/postgres.git
synced 2025-11-19 13:42:17 +03:00
Add support for nearest-neighbor (KNN) searches to SP-GiST
Currently, KNN searches were supported only by GiST. SP-GiST also capable to support them. This commit implements that support. SP-GiST scan stack is replaced with queue, which serves as stack if no ordering is specified. KNN support is provided for three SP-GIST opclasses: quad_point_ops, kd_point_ops and poly_ops (catversion is bumped). Some common parts between GiST and SP-GiST KNNs are extracted into separate functions. Discussion: https://postgr.es/m/570825e8-47d0-4732-2bf6-88d67d2d51c8%40postgrespro.ru Author: Nikita Glukhov, Alexander Korotkov based on GSoC work by Vlad Sterzhanov Review: Andrey Borodin, Alexander Korotkov
This commit is contained in:
@@ -74,9 +74,11 @@
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/spgist.h"
|
||||
#include "access/spgist_private.h"
|
||||
#include "access/stratnum.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "utils/float.h"
|
||||
#include "utils/fmgroids.h"
|
||||
#include "utils/fmgrprotos.h"
|
||||
#include "utils/geo_decls.h"
|
||||
|
||||
@@ -367,6 +369,31 @@ overAbove4D(RectBox *rect_box, RangeBox *query)
|
||||
return overHigher2D(&rect_box->range_box_y, &query->right);
|
||||
}
|
||||
|
||||
/* Lower bound for the distance between point and rect_box */
|
||||
static double
|
||||
pointToRectBoxDistance(Point *point, RectBox *rect_box)
|
||||
{
|
||||
double dx;
|
||||
double dy;
|
||||
|
||||
if (point->x < rect_box->range_box_x.left.low)
|
||||
dx = rect_box->range_box_x.left.low - point->x;
|
||||
else if (point->x > rect_box->range_box_x.right.high)
|
||||
dx = point->x - rect_box->range_box_x.right.high;
|
||||
else
|
||||
dx = 0;
|
||||
|
||||
if (point->y < rect_box->range_box_y.left.low)
|
||||
dy = rect_box->range_box_y.left.low - point->y;
|
||||
else if (point->y > rect_box->range_box_y.right.high)
|
||||
dy = point->y - rect_box->range_box_y.right.high;
|
||||
else
|
||||
dy = 0;
|
||||
|
||||
return HYPOT(dx, dy);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* SP-GiST config function
|
||||
*/
|
||||
@@ -534,17 +561,6 @@ spg_box_quad_inner_consistent(PG_FUNCTION_ARGS)
|
||||
RangeBox *centroid,
|
||||
**queries;
|
||||
|
||||
if (in->allTheSame)
|
||||
{
|
||||
/* Report that all nodes should be visited */
|
||||
out->nNodes = in->nNodes;
|
||||
out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes);
|
||||
for (i = 0; i < in->nNodes; i++)
|
||||
out->nodeNumbers[i] = i;
|
||||
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/*
|
||||
* We are saving the traversal value or initialize it an unbounded one, if
|
||||
* we have just begun to walk the tree.
|
||||
@@ -554,6 +570,40 @@ spg_box_quad_inner_consistent(PG_FUNCTION_ARGS)
|
||||
else
|
||||
rect_box = initRectBox();
|
||||
|
||||
if (in->allTheSame)
|
||||
{
|
||||
/* Report that all nodes should be visited */
|
||||
out->nNodes = in->nNodes;
|
||||
out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes);
|
||||
for (i = 0; i < in->nNodes; i++)
|
||||
out->nodeNumbers[i] = i;
|
||||
|
||||
if (in->norderbys > 0 && in->nNodes > 0)
|
||||
{
|
||||
double *distances = palloc(sizeof(double) * in->norderbys);
|
||||
int j;
|
||||
|
||||
for (j = 0; j < in->norderbys; j++)
|
||||
{
|
||||
Point *pt = DatumGetPointP(in->orderbys[j].sk_argument);
|
||||
|
||||
distances[j] = pointToRectBoxDistance(pt, rect_box);
|
||||
}
|
||||
|
||||
out->distances = (double **) palloc(sizeof(double *) * in->nNodes);
|
||||
out->distances[0] = distances;
|
||||
|
||||
for (i = 1; i < in->nNodes; i++)
|
||||
{
|
||||
out->distances[i] = palloc(sizeof(double) * in->norderbys);
|
||||
memcpy(out->distances[i], distances,
|
||||
sizeof(double) * in->norderbys);
|
||||
}
|
||||
}
|
||||
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/*
|
||||
* We are casting the prefix and queries to RangeBoxes for ease of the
|
||||
* following operations.
|
||||
@@ -571,6 +621,8 @@ spg_box_quad_inner_consistent(PG_FUNCTION_ARGS)
|
||||
out->nNodes = 0;
|
||||
out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes);
|
||||
out->traversalValues = (void **) palloc(sizeof(void *) * in->nNodes);
|
||||
if (in->norderbys > 0)
|
||||
out->distances = (double **) palloc(sizeof(double *) * in->nNodes);
|
||||
|
||||
/*
|
||||
* We switch memory context, because we want to allocate memory for new
|
||||
@@ -648,6 +700,22 @@ spg_box_quad_inner_consistent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
out->traversalValues[out->nNodes] = next_rect_box;
|
||||
out->nodeNumbers[out->nNodes] = quadrant;
|
||||
|
||||
if (in->norderbys > 0)
|
||||
{
|
||||
double *distances = palloc(sizeof(double) * in->norderbys);
|
||||
int j;
|
||||
|
||||
out->distances[out->nNodes] = distances;
|
||||
|
||||
for (j = 0; j < in->norderbys; j++)
|
||||
{
|
||||
Point *pt = DatumGetPointP(in->orderbys[j].sk_argument);
|
||||
|
||||
distances[j] = pointToRectBoxDistance(pt, next_rect_box);
|
||||
}
|
||||
}
|
||||
|
||||
out->nNodes++;
|
||||
}
|
||||
else
|
||||
@@ -763,6 +831,17 @@ spg_box_quad_leaf_consistent(PG_FUNCTION_ARGS)
|
||||
break;
|
||||
}
|
||||
|
||||
if (flag && in->norderbys > 0)
|
||||
{
|
||||
Oid distfnoid = in->orderbys[0].sk_func.fn_oid;
|
||||
|
||||
out->distances = spg_key_orderbys_distances(leaf, false,
|
||||
in->orderbys, in->norderbys);
|
||||
|
||||
/* Recheck is necessary when computing distance to polygon */
|
||||
out->recheckDistances = distfnoid == F_DIST_POLYP;
|
||||
}
|
||||
|
||||
PG_RETURN_BOOL(flag);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user