Define and materialize feature views
For the complete documentation index see: llms.txt
All documentation pages available in markdown.
The demo files in this section follow the Aerospike online store reference and the pinned aerospike.py implementation. Open that implementation to explore how partial upserts land in Map CDT bins. For how feature views share a record, see Benefits and boundaries.
On this page, you create one driver entity and two feature views, then materialize both into Aerospike. The driver_stats view contains rating and trip count, and pricing contains a surge multiplier. Each view reads from a separate Parquet file that represents the batch source its owning team uses in production.
Create the feature repository
Create the project directory, feature store configuration, entity and feature view definitions, and synthetic driver data.
-
Create a project directory with a
datafolder:Terminal window mkdir -p feast-aerospike-demo/datacd feast-aerospike-demo -
Create
feature_store.yaml, pointing the online store at your local Aerospike instance (the Docker container from Install and connect):feature_store.yaml project: driver_featuresregistry: data/registry.dbprovider: localonline_store:type: aerospikehosts:- ["127.0.0.1", 3000]namespace: testttl_seconds: 259200entity_key_serialization_version: 3registry: data/registry.dbis Feast’s local metadata store. It holds definitions of entities, feature views, and data sources so commands likefeast applyandfeast materializeknow what to create or read.namespace: testmatches the default namespace in the Aerospike Docker image you started earlier.ttl_seconds: 259200sets Aerospike record expiry (three days). Feature view lookback is configured separately indriver_repo.py.provider: localtells Feast to handle all orchestration on the local machine as opposed to cloud infrastructure.entity_key_serialization_version: 3controls how Feast encodes entity join key values into Aerospike record keys. Version 3 is required for the Aerospike online store and must match across all Feast commands in the same project.hostsis a list of[host, port]pairs. This tutorial uses one local entry (["127.0.0.1", 3000]). Production clusters list multiple seed nodes. -
Create
driver_repo.pywith the entity and both feature views:Each
FileSourcestands in for a batch pipeline output in production. Here it points at a local Parquet file.driver_repo.py from datetime import timedeltafrom feast import Entity, FeatureView, Field, FileSourcefrom feast.types import Float32, Int64from feast.value_type import ValueTypedriver = Entity(name="driver",join_keys=["driver_id"],value_type=ValueType.INT64,)driver_stats_source = FileSource(name="driver_stats_source",path="data/driver_stats.parquet",timestamp_field="event_timestamp",)pricing_source = FileSource(name="pricing_source",path="data/pricing.parquet",timestamp_field="event_timestamp",)driver_stats_fv = FeatureView(name="driver_stats",entities=[driver],ttl=timedelta(days=3),schema=[Field(name="rating", dtype=Float32),Field(name="trips_last_7d", dtype=Int64),],source=driver_stats_source,)pricing_fv = FeatureView(name="pricing",entities=[driver],ttl=timedelta(days=3),schema=[Field(name="surge_multiplier", dtype=Float32),],source=pricing_source,)driver_statsandpricingshare the samedriverentity, so both land on the same Aerospike record per driver, as described in What the record holds. Thettl=timedelta(days=3)argument controls Feast materialization lookback and historical point-in-time retrieval. It does not set online storage expiry. Seettl_secondsinfeature_store.yamlabove. -
Create
generate_data.pyto produce synthetic Parquet sources for three drivers using the pandas Python library:generate_data.py from datetime import timedeltaimport pandas as pddef main():driver_ids = [1001, 1002, 1003]event_timestamp = pd.Timestamp.now(tz="UTC").floor("s") - timedelta(minutes=10)pd.DataFrame({"driver_id": driver_ids,"event_timestamp": [event_timestamp] * len(driver_ids),"rating": [4.91, 4.72, 4.85],"trips_last_7d": [132, 98, 210],}).to_parquet("data/driver_stats.parquet")pd.DataFrame({"driver_id": driver_ids,"event_timestamp": [event_timestamp] * len(driver_ids),"surge_multiplier": [1.2, 1.0, 1.4],}).to_parquet("data/pricing.parquet")print("Wrote data/driver_stats.parquet and data/pricing.parquet")if __name__ == "__main__":main() -
Run the data generation script:
Terminal window python3 generate_data.pyExpected result Wrote data/driver_stats.parquet and data/pricing.parquet
Register and materialize
Register both feature views in the Feast registry with feast apply, then copy the latest values from the Parquet files into Aerospike with feast materialize-incremental.
-
Register the entity and feature views:
Terminal window feast applyExpected result No project found in the repository. Using project name driver_features defined in feature_store.yamlApplying changes for project driver_featuresDeploying infrastructure for driver_statsDeploying infrastructure for pricing -
Confirm registration:
feast applyregistered the entity and both feature views in the local registry.- Output includes
Applying changes for project driver_features. - Output includes
Deploying infrastructure for driver_statsandDeploying infrastructure for pricing.
- Output includes
-
Materialize both feature views into Aerospike:
feast applyregisters definitions in the local registry only. Materialization copies feature values from the offline sources into the Aerospike online store soget_online_featurescan read them at inference time.feast materialize-incrementaltakes an end timestamp and loads new or updated rows from each feature view’s Parquet file up to that time. The$(date -u +"%Y-%m-%dT%H:%M:%S")argument supplies the current UTC time. On the first run, Feast materializes every row still inside each feature view’s TTL window (three days in this demo).For each feature view, Feast reads
data/driver_stats.parquetordata/pricing.parquet, groups rows bydriver_id, and writes them to Aerospike. Because both views share thedriverentity, rows for the same driver land on one Aerospike record under separate map keys, as described in What the record holds.That copy runs one way. After materialization,
get_online_featuresreads Aerospike only and never opens the Parquet files. Parquet is still the batch source for future updates: edit the file and materialize again to refresh Aerospike. Feast does not sync data from Aerospike back into Parquet.Terminal window feast materialize-incremental $(date -u +"%Y-%m-%dT%H:%M:%S")Expected result Materializing 2 feature views to 2026-07-14 21:54:28+00:00 into the aerospike online store.driver_stats from 2026-07-11 21:54:31+00:00 to 2026-07-14 21:54:28+00:00:pricing from 2026-07-11 21:54:31+00:00 to 2026-07-14 21:54:28+00:00:In that output:
Materializing 2 feature views to ... into the aerospike online storenames the end timestamp and confirms both views wrote to Aerospike.driver_stats from ... to ...is the time window Feast read fromdata/driver_stats.parquet.pricing from ... to ...is the same fordata/pricing.parquet.
Feast does not print a row count on this command. You confirm the three driver rows landed in Aerospike on the Verify collocation and partial upserts page. A
DeprecationWarningaboutmeta["ttl"]can appear without blocking materialization. Your timestamps differ from this example. -
Confirm materialization:
- Output includes
Materializing 2 feature viewsandinto the aerospike online store. - Output includes a
driver_stats fromline and apricing fromline. - The command exits without an error after both views finish.
- Output includes
feast apply registered one entity and two feature views. feast materialize-incremental then read each feature view’s Parquet source and wrote its rows into Aerospike, one write per feature view. On the Verify collocation and partial upserts page, you read both feature views in one get_online_features call, inspect the Aerospike record directly, and observe a targeted update to one feature view.