Skip to content

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.

  1. Create a project directory with a data folder:

    Terminal window
    mkdir -p feast-aerospike-demo/data
    cd feast-aerospike-demo
  2. 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_features
    registry: data/registry.db
    provider: local
    online_store:
    type: aerospike
    hosts:
    - ["127.0.0.1", 3000]
    namespace: test
    ttl_seconds: 259200
    entity_key_serialization_version: 3

    registry: data/registry.db is Feast’s local metadata store. It holds definitions of entities, feature views, and data sources so commands like feast apply and feast materialize know what to create or read. namespace: test matches the default namespace in the Aerospike Docker image you started earlier.

    ttl_seconds: 259200 sets Aerospike record expiry (three days). Feature view lookback is configured separately in driver_repo.py.

    provider: local tells Feast to handle all orchestration on the local machine as opposed to cloud infrastructure. entity_key_serialization_version: 3 controls 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. hosts is a list of [host, port] pairs. This tutorial uses one local entry (["127.0.0.1", 3000]). Production clusters list multiple seed nodes.

  3. Create driver_repo.py with the entity and both feature views:

    Each FileSource stands in for a batch pipeline output in production. Here it points at a local Parquet file.

    driver_repo.py
    from datetime import timedelta
    from feast import Entity, FeatureView, Field, FileSource
    from feast.types import Float32, Int64
    from feast.value_type import ValueType
    driver = 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_stats and pricing share the same driver entity, so both land on the same Aerospike record per driver, as described in What the record holds. The ttl=timedelta(days=3) argument controls Feast materialization lookback and historical point-in-time retrieval. It does not set online storage expiry. See ttl_seconds in feature_store.yaml above.

  4. Create generate_data.py to produce synthetic Parquet sources for three drivers using the pandas Python library:

    generate_data.py
    from datetime import timedelta
    import pandas as pd
    def 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()
  5. Run the data generation script:

    Terminal window
    python3 generate_data.py
    Expected 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.

  1. Register the entity and feature views:

    Terminal window
    feast apply
    Expected result
    No project found in the repository. Using project name driver_features defined in feature_store.yaml
    Applying changes for project driver_features
    Deploying infrastructure for driver_stats
    Deploying infrastructure for pricing
  2. Confirm registration:

    feast apply registered the entity and both feature views in the local registry.

    • Output includes Applying changes for project driver_features.
    • Output includes Deploying infrastructure for driver_stats and Deploying infrastructure for pricing.
  3. Materialize both feature views into Aerospike:

    feast apply registers definitions in the local registry only. Materialization copies feature values from the offline sources into the Aerospike online store so get_online_features can read them at inference time.

    feast materialize-incremental takes 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.parquet or data/pricing.parquet, groups rows by driver_id, and writes them to Aerospike. Because both views share the driver entity, 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_features reads 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 store names the end timestamp and confirms both views wrote to Aerospike.
    • driver_stats from ... to ... is the time window Feast read from data/driver_stats.parquet.
    • pricing from ... to ... is the same for data/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 DeprecationWarning about meta["ttl"] can appear without blocking materialization. Your timestamps differ from this example.

  4. Confirm materialization:

    • Output includes Materializing 2 feature views and into the aerospike online store.
    • Output includes a driver_stats from line and a pricing from line.
    • The command exits without an error after both views finish.

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.