fix(places): read an imported row for the dedup set in one helper

The candidate a GPX or KML row is checked as is built in lineCandidate, so
places.service.ts stays within its size baseline, and the atomic import test
imports the same Ridge line it later expects to be skipped.
This commit is contained in:
Maurice
2026-10-28 15:17:37 +01:00
parent ef86bf2660
commit aee275e2b0
3 changed files with 16 additions and 7 deletions
+8
View File
@@ -134,6 +134,14 @@ function lineCourse(routeGeometry: string | null | undefined): string {
return [first[0], first[1], last[0], last[1], south, west, north, east].map((v) => v.toFixed(3)).join(',');
}
/** An imported row as the dedup set reads it: a line when it carries one. */
export function lineCandidate(
name: string | null | undefined,
row: { lat: number | null; lng: number | null; routeGeometry?: string | null },
): DedupCandidate {
return { name, lat: row.lat, lng: row.lng, track: !!row.routeGeometry, routeGeometry: row.routeGeometry };
}
/** A track's entry in `DedupSet.tracks`: its normalized name and its course. */
function trackKey(name: string, routeGeometry: string | null | undefined): string {
return `${name}\u0000${lineCourse(routeGeometry)}`;
+6 -6
View File
@@ -58,7 +58,7 @@ import {
buildCategoryNameLookup,
escapeLikePattern,
dedupSetOf,
isPlaceDuplicate,
isPlaceDuplicate, lineCandidate,
mapWithConcurrency,
pickEnrichmentMatch,
reclaimPhotoCache,
@@ -758,7 +758,7 @@ export class PlacesService {
async gpxDuplicates(tripId: string, rows: PreparedGpxPlace[]): Promise<boolean[]> {
const dedup = await this.buildDedupSet(tripId);
return rows.map((wp) => {
const candidate = { name: wp.name, lat: wp.lat, lng: wp.lng, track: !!wp.routeGeometry, routeGeometry: wp.routeGeometry };
const candidate = lineCandidate(wp.name, wp);
if (isPlaceDuplicate(candidate, dedup)) return true;
trackInsertedInDedupSet(candidate, dedup);
return false;
@@ -925,7 +925,7 @@ export class PlacesService {
// `findWithTagsAndRatings` INSIDE the caller's transaction, so the read
// sees the still-uncommitted row.
for (const wp of waypoints) {
if (isPlaceDuplicate({ name: wp.name, lat: wp.lat, lng: wp.lng, track: !!wp.routeGeometry, routeGeometry: wp.routeGeometry }, dedup)) {
if (isPlaceDuplicate(lineCandidate(wp.name, wp), dedup)) {
skipped++;
continue;
}
@@ -960,7 +960,7 @@ export class PlacesService {
});
const place = (await this.placesRepo.findWithTagsAndRatings(placeId))!;
created.push(place);
trackInsertedInDedupSet({ name: wp.name, lat: wp.lat, lng: wp.lng, track: !!wp.routeGeometry, routeGeometry: wp.routeGeometry }, dedup);
trackInsertedInDedupSet(lineCandidate(wp.name, wp), dedup);
}
return { places: created, count: created.length, skipped };
@@ -1033,7 +1033,7 @@ export class PlacesService {
const fallbackName = `Placemark ${fallbackIndex}`;
const name = parsedPlacemark.name || fallbackName;
if (isPlaceDuplicate({ name, lat: parsedPlacemark.lat, lng: parsedPlacemark.lng, track: isPath, routeGeometry: parsedPlacemark.routeGeometry }, dedup)) {
if (isPlaceDuplicate(lineCandidate(name, parsedPlacemark), dedup)) {
summary.skippedCount += 1;
dupCount++;
fallbackIndex += 1;
@@ -1079,7 +1079,7 @@ export class PlacesService {
const place = (await this.placesRepo.findWithTagsAndRatings(placeId))!;
created.push(place);
trackInsertedInDedupSet({ name, lat: parsedPlacemark.lat, lng: parsedPlacemark.lng, track: isPath, routeGeometry: parsedPlacemark.routeGeometry }, dedup);
trackInsertedInDedupSet(lineCandidate(name, parsedPlacemark), dedup);
summary.createdCount += 1;
fallbackIndex += 1;
}