# SPDX-FileCopyrightText: 2025 ash_neo4j contributors # # SPDX-License-Identifier: MIT defmodule AshNeo4j.Spatial do @moduledoc """ Convenience helpers for creating the Neo4j POINT indexes that back AshNeo4j's spatial pushdown (#275). After #274, the indexable form of a geometry is a scalar Neo4j POINT companion that the data layer writes automatically alongside the canonical RFC 7946 GeoJSON: * a `%Geo.Point{}` gets one companion at `.point`; * any other geometry gets two, `.bbSW` / `.bbNE` (the bounding-box corners `point.withinBBox` reads). Hand-writing the index Cypher means knowing the resource's Neo4j **label**, the attribute → **property** translation, the **companion suffix** convention, and — for bbox geometries — that there are *two* companions. This module applies that internal knowledge for you, resolving everything from the resource module and attribute name. # top-level Point attribute → one `.point` index AshNeo4j.Spatial.create_index(Place, :location) # non-Point geometry → both bbSW and bbNE indexes AshNeo4j.Spatial.create_index(Place, :bounds) # nested geometry — [attribute, field...] path into a TypedStruct AshNeo4j.Spatial.create_index(Place, [:pet, :home]) # rebuild (DROP IF EXISTS + CREATE) after a storage-shape change AshNeo4j.Spatial.create_index(Place, :location, recreate: true) Consistent with AshNeo4j's "no migrations, index lifecycle is the operator's concern" stance (#45) — this is an ergonomic tool you *choose* to call (e.g. from a start-up task), not automatic behaviour. `create_index/3` uses `IF NOT EXISTS`, so it's safe to run repeatedly. Indexes are schema objects independent of data: clearing nodes does not drop them, and they re-populate as nodes are written. ## Naming Each companion gets its own index named `_`, where `suffix` is `point` / `bbSW` / `bbNE` and `base` defaults to `