修地图城市圈的聚合口径:先按地点汇总,再按城市汇总
/api/cities 原来把聚合打在 places ⋈ visits 上:一个地点有 N 条到访,它的 visit_count 与 dwell_minutes 就被累加 N 次,count(*) 数出来的到访条数又被当成「地点数」显示——实测最大的 那个城市气泡写着「到访 72661 次 · 568 个地点」,真值是 568 次 / 86 个地点(差 128 倍)。 改成内层按地点 GROUP BY、外层按城市 sum/count,并补上「已并入 / 子段不算数」的判据, 圈的位置、排序与返回列序都不变。 回归 city_agg_test.go:手写同口径 SQL 对账 + 「地点数不可能多于到访数」的不变量; 变异验证(sum(pa.visits) *2)立刻红。
This commit is contained in:
@@ -948,16 +948,26 @@ func (s *Server) apiCityAgg(w http.ResponseWriter, r *http.Request) {
|
||||
if !p.IsAdmin() {
|
||||
regSQL = regionSQL("p", p.Settings.Regions, p.Settings.RegionBlacklist(), &args)
|
||||
}
|
||||
rows, err := s.db.QueryContext(r.Context(), `SELECT COALESCE(p.city,''), COALESCE(p.province,''),
|
||||
AVG(p.lat)::float8, AVG(p.lon)::float8,
|
||||
COALESCE(sum(p.visit_count),0), count(*), COALESCE(sum(p.dwell_minutes),0),
|
||||
min(p.first_visit_at), max(p.last_visit_at)
|
||||
FROM places p
|
||||
JOIN visits v ON v.place_id = p.id AND v.dataset_id = p.dataset_id
|
||||
WHERE p.dataset_id = $1 AND p.city IS NOT NULL AND p.city <> '' AND p.lat IS NOT NULL`+regSQL+`
|
||||
AND NOT EXISTS (SELECT 1 FROM visits vh WHERE vh.place_id = p.id
|
||||
AND (vh.is_home OR vh.is_work))
|
||||
GROUP BY p.city, p.province
|
||||
// 先按**地点**聚合、再按城市汇总。直接在 places ⋈ visits 上 sum(places.visit_count) 是错的:
|
||||
// 一个地点有 N 条到访,它的 visit_count 就会被累加 N 次(实测「到访 72661 次」,真值 568),
|
||||
// dwell 同理;count(*) 数出来的是到访条数,却被当成「地点数」显示。
|
||||
rows, err := s.db.QueryContext(r.Context(), `SELECT COALESCE(pa.city,''), COALESCE(pa.province,''),
|
||||
AVG(pa.lat)::float8, AVG(pa.lon)::float8,
|
||||
COALESCE(sum(pa.visits),0), count(*), COALESCE(sum(pa.dwell),0),
|
||||
min(pa.first_at), max(pa.last_at)
|
||||
FROM (
|
||||
SELECT p.id, p.city, p.province, p.lat, p.lon, p.first_visit_at AS first_at, p.last_visit_at AS last_at,
|
||||
count(*) AS visits,
|
||||
COALESCE(sum(LEAST(COALESCE(v.duration_min,0),10080)),0) AS dwell
|
||||
FROM places p
|
||||
JOIN visits v ON v.place_id = p.id AND v.dataset_id = p.dataset_id
|
||||
WHERE p.dataset_id = $1 AND p.city IS NOT NULL AND p.city <> '' AND p.lat IS NOT NULL`+regSQL+`
|
||||
AND `+stats.SoloVisitSQL("v")+`
|
||||
AND NOT EXISTS (SELECT 1 FROM visits vh WHERE vh.place_id = p.id
|
||||
AND (vh.is_home OR vh.is_work))
|
||||
GROUP BY p.id
|
||||
) pa
|
||||
GROUP BY pa.city, pa.province
|
||||
ORDER BY 5 DESC`, args...)
|
||||
if err != nil {
|
||||
s.json(w, map[string]string{"error": err.Error()})
|
||||
|
||||
Reference in New Issue
Block a user