Skip to content

KML import re-searches the whole document once per geometry #66

Description

@njakobsen

SpatialFeatures::Importers::KML#each_record resolves each geometry's Placemark with feature.ancestors('Placemark').first. Nokogiri::XML::Node#ancestors answers a selector by walking to the document root and then calling root.search(selector) over the whole document, scanning the results for each ancestor (nokogiri/xml/node.rb:1344-1364). The search is not cached, so it runs once per geometry and the import costs geometries x document size.

Files where each Placemark holds one geometry never show it. Files exported from design software do: one Placemark is a <MultiGeometry> of hundreds of small faces, so a few thousand Placemarks become six figures of geometries in a document tens of megabytes wide, and the product dominates the import.

Measured with Nokogiri 1.19.4, on documents built by repeating the test.kml fixture's Placemark:

Placemarks Parts each Geometries ancestors('Placemark') Parent walk
100 20 2,000 0.21s 0.0031s
250 40 10,000 3.38s 0.0045s
500 40 20,000 14.44s 0.0155s

Geometries rise 10x from the first row to the last and the cost rises 69x, which is the quadratic.

On a 33.8 MB CAD-derived KML from a production deployment holding 75,407 polygons, the same call measures 10.1 ms each, or 758 seconds for that one file. It is roughly half the wall-clock of an import of that record that took 2h05m and held the deployment's only queue worker for the duration.

The nearest enclosing Placemark can be found by walking the parent chain, which is the same answer for a fraction of the cost and no change in behaviour.

🤖 Generated with Claude Code

https://claude.ai/code/session_012b7n8NbLAd6s7c3atii4Mv

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions