Je suis confronté à une difficulté avec une requête postgis. Actuellement, avec seulement 1000 points en bdd de données, je suis à environ à 12 secondes pour générer ma page web, ce qui est énorme
Donc j’imagine qu’avec 50 ou 100 ou 200 fois plus de points et plusieurs personnes qui font des recherches en même temps, j’aurai le temps d’aller boire un café … ou de me pendre
J’ai deux options, soit optimiser la requête et arriver à un temps de chargement optimal ou bien tricher un peu en faisant des pré-calculs, que je stocke en bdd. Franchement, la deuxième option, n’a rien d’idéale, ça supposerait de parcourir toutes les données de façon régulière et générer/updater la data qui va bien.
Donc c’est pour ça que je crie au secours et appelle à votre expérience pour voir ce qu’il est possible de faire.
La requête me permet de récupérer l’ensemble des POIs situé de part et d’autre d’une trace (en fonction d’un point de départ).
Et la sous requête qui calcule la distance entre le point de départ et le point affiché, pour pouvoir afficher les POIs dans l’ordre. Cette sous-requête est donc appelée pour chaque point d’arrivée potentiel.
Pour afficher le kilométrage, je rappelle une deuxième fois l’équivalent de la sous requête. Ce qui n’est pas franchement idéal…
La requ^te pour récupérer la liste des POIs :
def poi_around_trace_from__(poi, trace, dist)
distance_sql = <<-SQL
SELECT
ST_Distance(tr.path::geography, pta.lonlat::geography) +
ST_Distance(tr.path::geography, poi.lonlat::geography) +
ST_Length(ST_LineSubstring(
tr.path,
least(ST_LineLocatePoint(tr.path, pta.lonlat::geometry), ST_LineLocatePoint(tr.path, poi.lonlat::geometry)),
greatest(ST_LineLocatePoint(tr.path, pta.lonlat::geometry), ST_LineLocatePoint(tr.path, poi.lonlat::geometry))),false) AS dst_line, poi.*
FROM traces tr, pois pta, (
SELECT poi.* AS pois
FROM traces tr, pois pta, pois poi
WHERE tr.id = #{trace.id}
AND pta.id = #{poi.id}
AND ST_DWithin(ST_LineSubstring(
tr.path,
ST_LineLocatePoint(tr.path, pta.lonlat::geometry) + (#{dist} * 1000) / (tr.length * 1000) ,
ST_LineLocatePoint(way.path, pta.lonlat::geometry) + #{end_point} / ST_Length(way.path, false))::geography,
poi.lonlat::geography,
200)
) as poi
WHERE tr.id = #{trace.id}
AND pta.id = #{poi.id}
AND poi.id = poi.id
ORDER BY dst_line ASC
SQL
Rails.cache.fetch(['sql-distance', trace, poi, dist]) {
Poi.find_by_sql(distance_sql)
}
end
la requête pour le calcul des distances :
def distance_along_way_to(poi1, poi2, way)
distance_sql = <<-SQL
SELECT
ST_Distance(way.path::geography, pta.lonlat::geography) +
ST_Distance(way.path::geography, ptb.lonlat::geography) +
ST_Length(ST_LineSubstring(
way.path,
least(ST_LineLocatePoint(way.path, pta.lonlat::geometry), ST_LineLocatePoint(way.path, ptb.lonlat::geometry)),
greatest(ST_LineLocatePoint(way.path, pta.lonlat::geometry), ST_LineLocatePoint(way.path, ptb.lonlat::geometry))),false) AS dst_line
FROM ways way, pois pta, pois ptb
WHERE way.id = #{way.id}
AND pta.id = #{poi1.id}
AND ptb.id = #{poi2.id}
SQL
Rails.cache.fetch(['sql-distance', way, poi1, poi2]) {
Poi.find_by_sql(distance_sql).first.dst_line
}
end
Je met également un explain de la première requête, si ça peut aider, moi, je ne sais pas l’interpréter
"Sort (cost=46.67..46.71 rows=15 width=209)"
" Sort Key: (((_st_distance((tr.path)::geography, pta.lonlat, '0'::double precision, true) + _st_distance((tr.path)::geography, poi.lonlat, '0'::double precision, true)) + st_length((st_linesubstring(tr.path, LEAST(st_linelocatepoint(tr.path, (pta.lonlat)::geometry), st_linelocatepoint(tr.path, (poi.lonlat)::geometry)), GREATEST(st_linelocatepoint(tr.path, (pta.lonlat)::geometry), st_linelocatepoint(tr.path, (poi.lonlat)::geometry))))::geography, false)))"
" -> Nested Loop (cost=0.00..46.38 rows=15 width=209)"
" Join Filter: st_dwithin((st_linesubstring(tr_1.path, (st_linelocatepoint(tr_1.path, (pta_1.lonlat)::geometry) + ('20000'::double precision / (tr_1.length * '1000'::double precision))), (st_linelocatepoint(tr_1.path, (pta_1.lonlat)::geometry) + ('200000'::double precision / st_length((tr_1.path)::geography, false)))))::geography, poi.lonlat, '2000'::double precision)"
" -> Nested Loop (cost=0.00..7.23 rows=1 width=136)"
" -> Nested Loop (cost=0.00..4.64 rows=1 width=104)"
" -> Nested Loop (cost=0.00..3.61 rows=1 width=64)"
" -> Seq Scan on traces tr (cost=0.00..1.02 rows=1 width=32)"
" Filter: (id = 2)"
" -> Seq Scan on pois pta (cost=0.00..2.58 rows=1 width=32)"
" Filter: (id = 2)"
" -> Seq Scan on traces tr_1 (cost=0.00..1.02 rows=1 width=40)"
" Filter: (id = 2)"
" -> Seq Scan on pois pta_1 (cost=0.00..2.58 rows=1 width=32)"
" Filter: (id = 2)"
" -> Seq Scan on pois poi (cost=0.00..2.46 rows=46 width=201)"
" Filter: (id IS NOT NULL)"
Je trouverais dommage, après avoir passé autant de temps sur Postgis, à ne pas pouvoir utiliser l’outil et devoir me rabattre sur le système bricolé des pré-calculs
Mais je suis sûr qu’il existe une solution, sinon faut 'expliquer comment google fait pour afficher aussi vite ses itinéraire sur une map