---
title: "How do I find Entities that overlap another Entity Type in Python?"
section: "Libraries: Python"
route: /faqoverlappingentitiesinpython
account: {accountId}
bruce_api: https://{accountId}.api.nextspace.host
guardian_api: https://guardian.nextspace.host
---
# How do I find Entities that overlap another Entity Type in Python?

"Which pipes run through this building" and "which sensors sit inside this zone" are the same question: find the Entities of one Type that overlap the Entities of another.

Data Lab answers it with two selections. The primary selection describes what you are measuring against, the secondary selection describes what you want back, and a spatial criterion in the secondary compares each candidate against the primary. The secondary selection is the one that runs, so it is the set you get.

The library builds both selections for you. Build a query per Entity Type, pair them with `secondary`, and add the spatial criterion. Below, the matches are walked and then stamped with an attribute through a Change Set.

You will want to install our Python library to facilitate this process.

```bash
pip install bruce-models
```

**Python**

```python
import bruce_models as BM

# What we are measuring against, and what we want back.
PRIMARY_TYPE_ID = "YOUR_CONTAINER_ENTITY_TYPE_ID"
SECONDARY_TYPE_ID = "YOUR_TARGET_ENTITY_TYPE_ID"

# The attribute stamped on each match, as path segments.
ATTR_PATH = ["inside_building"]

# Primary Entities compared per join.
BATCH = 50

# Either a session token from a login or a long-lived access token.
TOKEN = "YOUR_TOKEN"

bruce = BM.BruceApi({
    "account_id": "{accountId}",
    "session_id": TOKEN
})

containers = BM.DataLabBuilder(bruce).entity_type(PRIMARY_TYPE_ID)
targets = BM.DataLabBuilder(bruce).entity_type(SECONDARY_TYPE_ID)

# The secondary Entities that touch or overlap a primary.
# Use within_primary() instead for fully covered only.
overlapping = containers.secondary(targets, batch=BATCH).intersecting_primary()

print(f"{overlapping.count():,} matches")

changes = BM.ChangeSetBuilder(bruce)

def stamp(primary_id, secondaries):
    """
    Handles the matches for one primary Entity.
    """
    for entity in secondaries:
        values = {}
        BM.Entity.set_value(values, ATTR_PATH, primary_id)
        changes.queue(entity, values)

# An Entity inside two overlapping primaries is answered once per
# primary, and the queue merges those into one edit per Entity.
matched = overlapping.each_group(stamp)
print(f"{matched['primaries']:,} primaries matched something, "
      f"{matched['matches']:,} matches, {len(changes):,} to update")

report = changes.apply()
print(f"{report['succeeded']:,} updated, {report['failed_count']:,} failed")
```

## Choosing the spatial comparison

Three attribute paths address an Entity's spatial data rather than one of your own attributes, and each answers a different question:

`"Bruce"/"Boundaries"` is the bounding box. Cheapest to compare, and the right choice when a box is a good enough answer, which it usually is for "roughly inside this building".

`"Bruce"/"VectorGeometry"` is the exact shape. Use it when the difference between the shape and its box matters, and expect it to cost more.

`"Bruce"/"Location"` is the single point. Use it with `DISTANCETO` and a `Distance` in metres for "within 50 metres of".

The spatial operators are `INTERSECTS` (the shapes touch or overlap), `INSIDE` and `CONTAINS` (the primary fully covers the candidate), `BOUNDSOVERLAP` (a fast, loose box overlap) and `DISTANCETO`.

## Things worth knowing

**The secondary selection is what comes back.** Swapping which Type is primary and which is secondary changes the answer, not just the wording. Put the set you want to act on in the secondary.

**Updating the set you are paging through.** The example queues every edit during the walk and applies them afterwards, so the join cannot shift underneath the paging even when the change would stop a record matching.

**Doing it all server side.** If you only need to stamp an attribute on the matches, the pairing can do the whole thing without returning the records: `overlapping.run_action_set_attribute(path, value)`. That returns a Pending Action to follow rather than a list, and avoids moving the Entities over the network at all. It also takes a `Ref` as the value, which is how you copy something off the primary onto each match.

---

Urls on this page are resolved for account `{accountId}`.
Site index: https://docs.nextspace.host/llms.txt · whole site in one file: https://docs.nextspace.host/llms-full.txt
Human-readable version of this page: https://docs.nextspace.host/faqoverlappingentitiesinpython
