1
0
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:
Alexander Korotkov
2018-09-19 01:54:10 +03:00
parent d0cfc3d6a4
commit 2a6368343f
29 changed files with 1681 additions and 428 deletions

View File

@@ -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);
}