(Background: I'm more of a MySQL guy, I've only been using PostGIS for a few months to solve this particular problem set.)
I have a table that contains a point and a radius:
id | location | radius
1469989 | 0101000020E6100000091B9E5E292651C0DCF4673F529E5140 | 100
1948448 | 0101000020E6100000BB0A293F299863C03B8908FF22D35140 | 10
I want to do a search that basically says "get me all points within (20 km + radius) from me". In other words, some points need to "reach" farther than others and need to be returned from farther away, whereas others have a smaller radius.
My understanding is that I can't use spatial indexes for this since the DB doesn't know in advance what that radius will be.
My other option is to use ST_Buffer to create a buffer with the radius. But this seems to be pretty slow since there are hundreds of thousands of points, each of which get turned into a polygon with X points (default is 36). This is powering a real-time feature, so it needs to be really fast.
Any tips on what the best way to do this is? Or is this just not going to work and I need to look into more aggressive caching / in-memory stores?
Update
The current index looks like this:
CREATE INDEX IF NOT EXISTS "places_location" ON "places" USING gist("location");
and here's a sample query (20 is a dynamic value that IRL would be parameterized):
explain analyze SELECT id from places
WHERE ST_DWithin(ST_MakePoint('-79.387', '43.642')::geography, places.location, 20 *radius*1000);
Results:
Gather (cost=1000.00..7076917.51 rows=68 width=8) (actual time=387.909..5444.545 rows=458668 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Parallel Seq Scan on places (cost=0.00..7075910.71 rows=28 width=8) (actual time=1017.367..5100.683 rows=152889 loops=3)
" Filter: st_dwithin('0101000020E610000054E3A59BC4D853C01904560E2DD24540'::geography, location, (('20'::double precision * radius) * '1000'::double precision), true)"
Rows Removed by Filter: 72442
Planning Time: 0.886 ms
JIT:
Functions: 12
" Options: Inlining true, Optimization true, Expressions true, Deforming true"
" Timing: Generation 29.416 ms, Inlining 593.395 ms, Optimization 1217.528 ms, Emission 941.965 ms, Total 2782.304 ms"
Execution Time: 5598.359 ms
This is what it looks like without dynamic radius:
explain analyze SELECT id from places
WHERE ST_DWithin(ST_MakePoint('-79.387', '43.642')::geography, places.location, 20*1000);
Bitmap Heap Scan on places (cost=19.15..10423.16 rows=68 width=8) (actual time=38.297..242.233 rows=8849 loops=1)
" Filter: st_dwithin('0101000020E610000054E3A59BC4D853C01904560E2DD24540'::geography, location, '20000'::double precision, true)"
Rows Removed by Filter: 2958
Heap Blocks: exact=6553
-> Bitmap Index Scan on places_location (cost=0.00..19.13 rows=363 width=0) (actual time=31.454..31.456 rows=11807 loops=1)
" Index Cond: (location && _st_expand('0101000020E610000054E3A59BC4D853C01904560E2DD24540'::geography, '20000'::double precision))"
Planning Time: 8.443 ms
Execution Time: 247.098 ms
ST_Expand
. Also, you might be able to use a functional index to avoid storing the boxes in the table itself.