# Repositories
Active contributors: Saksham, Ravi
The repository layer separates data access from business logic. It is intentionally small: three files under `app/repositories/`. Most services query the ORM directly through the injected `AsyncSession`, but property search is complex enough (geospatial filters, full-text search, amenity joins, multi-axis sorting, cursor pagination) to warrant a dedicated repository and a query builder that eliminates duplicated filter/sort logic across property, swipe, and search paths.
## Directory layout
```
app/repositories/
├── base.py # BaseRepository[T] generic CRUD
├── property_repository.py # PropertyRepository: filtered listing, radius, count, sorting
└── property_query_builder.py # PropertyQueryBuilder: where-clauses from UnifiedPropertyFilter
```
## Key abstractions
| Abstraction | Location | Purpose |
|---|---|---|
| `BaseRepository[T]` | `app/repositories/base.py` | Generic CRUD: `get`, `get_with_relations`, `list`, `count`, `create`, `update`, `delete`, `exists` |
| `PropertyRepository` | `app/repositories/property_repository.py` | Property-specific queries with geospatial, filter, sort helpers |
| `PropertyQueryBuilder` | `app/repositories/property_query_builder.py` | Builds `(conditions, distance_expr, search_meta)` from a `UnifiedPropertyFilter` |
| `SortBy` enum | `app/schemas/property.py` | price_low, price_high, newest, popular, distance, relevance |
## How it works
```mermaid
graph LR
Filter["UnifiedPropertyFilter"] --> Builder["PropertyQueryBuilder.build(db)"]
Builder --> Geo["_build_geo_conditions
ST_DWithin, ST_Distance"]
Builder --> FTS["_build_fts_conditions
plainto_tsquery, ts_rank"]
Builder --> Amen["_build_amenity_condition
subquery join"]
Builder --> Conds["conditions list
+ distance_expr + search_meta"]
Conds --> Repo["PropertyRepository.get_properties_filtered"]
Repo --> Sort["_apply_sorting
distance, relevance, popular"]
Sort --> Paginate["offset/limit
or keyset cursor"]
```
`BaseRepository` is generic over a SQLAlchemy model. It owns `get`, `get_with_relations` (eager loads via `selectinload`), `list` (dict filters, offset/limit, order by with `-` prefix for descending), `count`, `create` (flush + refresh), `update` (returning), `delete`, and `exists`. Services that only need simple per-model CRUD can subclass it directly.
`PropertyRepository` extends `BaseRepository[Property]` and adds:
- `get_property_with_owner` — eager loads images, owner, and amenities.
- `get_properties_filtered` — applies geo, filters, sorting, pagination. Pops `latitude`/`longitude`/`radius_km` from the filter dict and builds an `ST_SetSRID(ST_MakePoint(...), 4326)` center point, then `ST_DWithin` for radius and `ST_Distance` for ordering.
- `get_properties_within_radius` — convenience wrapper that orders by distance.
- `count_filtered` — count query using the same filter logic.
- `_apply_filters` — price range, bedrooms/bathrooms (>=), and dynamic `hasattr` equality.
- `_apply_sorting` — maps `SortBy` to order expressions, with `distance_expr` and `relevance_expr` injected by the caller.
`PropertyQueryBuilder` is the modern entry point. Its `build(db, include_unavailable=False)` method returns a tuple of `(conditions, distance_expr, search_meta)`. It handles availability, geo, full-text search (with `ts_rank`), property IDs, type, purpose, price, bedrooms, bathrooms, area, city/locality/pincode, parking, floor, age, amenities (subquery with `HAVING count >= len`), features, gender preference, sharing type, and guests. Its `apply_sort` method handles distance, price, newest, popular, and relevance (with optional `combined_relevance_expr` for hybrid vector+text scoring). The builder is reused by property, swipe, and search services to keep filter/sort logic in one place.
## Integration points
- **Property services** in `app/services/property/` use `PropertyQueryBuilder` and `PropertyRepository`. See [services-layer](systems--services-layer.md) and [features/ghar-core](features--ghar-core.md).
- **Semantic search** layers a vector similarity score on top of the builder's `text_rank_expr` via `combined_relevance_expr`. See [vector-search](systems--vector-search.md).
- **Models** — the repository targets `Property`, `PropertyAmenity`, and `Amenity`. See [models](systems--models.md).
## Entry points for modification
- New filter dimension: add a field to `UnifiedPropertyFilter` and a condition branch in `PropertyQueryBuilder.build`.
- New sort option: add a `SortBy` variant and a branch in `apply_sort` (and the legacy `_apply_sorting` on the repository).
- New model that needs generic CRUD: subclass `BaseRepository[T]`.
## Key source files
| File | Role |
|---|---|
| `app/repositories/base.py` | Generic CRUD base |
| `app/repositories/property_repository.py` | Property repository with geo/filter/sort |
| `app/repositories/property_query_builder.py` | Centralized filter/sort builder |