Skip to content

Commit e9d7e8a

Browse files
authored
Merge branch 'master' into fix/dynamodb-projection-expression
2 parents a872a88 + 728aa2e commit e9d7e8a

51 files changed

Lines changed: 6112 additions & 4628 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/publish_images.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ on:
1111
required: true
1212
default: ""
1313
type: string
14+
ref:
15+
description: 'Git ref to checkout (branch, tag, or SHA). Defaults to the triggering ref. Use to rebuild images from a different branch (e.g., master) when the release tag has a broken Dockerfile.'
16+
required: false
17+
type: string
1418
workflow_call: # Allows trigger of the workflow from another workflow
1519
inputs:
1620
custom_version: # Optional input for a custom version
@@ -36,6 +40,7 @@ jobs:
3640
steps:
3741
- uses: actions/checkout@v4
3842
with:
43+
ref: ${{ github.event.inputs.ref || github.ref }}
3944
submodules: 'true'
4045
- id: get-version
4146
uses: ./.github/actions/get-semantic-release-version

docs/getting-started/concepts/feast-types.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Feast supports the following categories of data types:
1212
- **UUID types**: `Uuid` and `TimeUuid` for universally unique identifiers. Stored as strings at the proto level but deserialized to `uuid.UUID` objects in Python.
1313
- **Array types**: ordered lists of any primitive type, e.g. `Array(Int64)`, `Array(String)`, `Array(Uuid)`.
1414
- **Set types**: unordered collections of unique values for any primitive type, e.g. `Set(String)`, `Set(Int64)`. Set types are not inferred by any backend and must be explicitly declared. They are best suited for online serving use cases.
15-
- **Map types**: dictionary-like structures with string keys and values that can be any supported Feast type (including nested maps), e.g. `Map`, `Array(Map)`.
15+
- **Map types**: dictionary-like structures. `Map` has string keys and values that can be any supported Feast type (including nested maps), e.g. `Map`, `Array(Map)`. `ScalarMap` has non-string scalar keys (int, float, bool, UUID, Decimal, bytes, datetime) — Feast infers `ScalarMap` automatically when the first key is not a string. `ScalarMap` must be explicitly declared in schema and is not inferred by any backend.
1616
- **JSON type**: opaque JSON data stored as a string at the proto level but semantically distinct from `String` — backends use native JSON types (`jsonb`, `VARIANT`, etc.), e.g. `Json`, `Array(Json)`.
1717
- **Struct type**: schema-aware structured type with named, typed fields. Unlike `Map` (which is schema-free), a `Struct` declares its field names and their types, enabling schema validation, e.g. `Struct({"name": String, "age": Int32})`.
1818

@@ -41,8 +41,8 @@ Map, JSON, and Struct types are supported across all major Feast backends:
4141
| Spark | `struct<...>` | `Struct` |
4242
| Spark | `array<struct<...>>` | `Array(Struct(...))` |
4343
| MSSQL | `nvarchar(max)` | `Map`, `Json`, `Struct` |
44-
| DynamoDB | Proto bytes | `Map`, `Json`, `Struct` |
45-
| Redis | Proto bytes | `Map`, `Json`, `Struct` |
44+
| DynamoDB | Proto bytes | `Map`, `Json`, `Struct`, `ScalarMap` |
45+
| Redis | Proto bytes | `Map`, `Json`, `Struct`, `ScalarMap` |
4646
| Milvus | `VARCHAR` (serialized) | `Map`, `Json`, `Struct` |
4747

4848
**Note**: When the backend native type is ambiguous (e.g., `jsonb` could be `Map`, `Json`, or `Struct`), the **schema-declared Feast type takes precedence**. The backend-to-Feast type mappings above are only used for schema inference when no explicit type is provided.

docs/reference/type-system.md

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,13 @@ Map types allow storing dictionary-like data structures:
116116
|------------|-------------|-------------|
117117
| `Map` | `Dict[str, Any]` | Dictionary with string keys and values of any supported Feast type (including nested maps) |
118118
| `Array(Map)` | `List[Dict[str, Any]]` | List of dictionaries |
119+
| `ScalarMap` | `Dict[Any, Any]` | Dictionary with non-string scalar keys (int, float, bool, UUID, Decimal, bytes, datetime) and values of any supported Feast type |
119120

120-
**Note:** Map keys must always be strings. Map values can be any supported Feast type, including primitives, arrays, or nested maps at the proto level. However, the PyArrow representation is `map<string, string>`, which means backends that rely on PyArrow schemas (e.g., during materialization) treat Map as string-to-string.
121+
**Note:** `Map` keys must always be strings. `ScalarMap` supports non-string scalar keys — Feast infers `ScalarMap` automatically when the first key of a dict is not a string. Map values can be any supported Feast type, including primitives, arrays, or nested maps at the proto level. However, the PyArrow representation is `map<string, string>`, which means backends that rely on PyArrow schemas (e.g., during materialization) treat Map as string-to-string.
122+
123+
{% hint style="warning" %}
124+
`ScalarMap` is **not** inferred from any backend schema. You must declare it explicitly in your feature view schema. It is best suited for online serving use cases where the online store serializes proto bytes directly (e.g., Redis, DynamoDB, SQLite).
125+
{% endhint %}
121126

122127
**Backend support for Map:**
123128

@@ -129,7 +134,7 @@ Map types allow storing dictionary-like data structures:
129134
| Spark | `map<string,string>` | `map<>``Map`, `array<map<>>``Array(Map)` |
130135
| Athena | `map` | Inferred as `Map` |
131136
| MSSQL | `nvarchar(max)` | Serialized as string |
132-
| DynamoDB / Redis | Proto bytes | Full proto Map support |
137+
| DynamoDB / Redis | Proto bytes | Full proto Map and ScalarMap support |
133138

134139
### JSON Type
135140

@@ -197,7 +202,7 @@ from datetime import timedelta
197202
from feast import Entity, FeatureView, Field, FileSource
198203
from feast.types import (
199204
Int32, Int64, Float32, Float64, String, Bytes, Bool, UnixTimestamp,
200-
Uuid, TimeUuid, Decimal, Array, Set, Map, Json, Struct
205+
Uuid, TimeUuid, Decimal, Array, Set, Map, ScalarMap, Json, Struct
201206
)
202207

203208
# Define a data source
@@ -257,6 +262,7 @@ user_features = FeatureView(
257262
Field(name="user_preferences", dtype=Map),
258263
Field(name="metadata", dtype=Map),
259264
Field(name="activity_log", dtype=Array(Map)),
265+
Field(name="event_counts", dtype=ScalarMap), # non-string keys, e.g. {1001: 5, 1002: 12}
260266

261267
# Nested collection types
262268
Field(name="weekly_scores", dtype=Array(Array(Float64))),
@@ -383,7 +389,7 @@ Field(name="grouped_tags", dtype=Array(Set(Array(String))))
383389
Maps can store complex nested data structures:
384390

385391
```python
386-
# Simple map
392+
# Simple map (string keys)
387393
user_preferences = {
388394
"theme": "dark",
389395
"language": "en",
@@ -411,6 +417,44 @@ activity_log = [
411417
]
412418
```
413419

420+
### ScalarMap Type Usage Examples
421+
422+
`ScalarMap` supports non-string keys. Feast infers it automatically when the first dict key is not a string:
423+
424+
```python
425+
import uuid
426+
import decimal
427+
428+
# Integer keys — e.g., category ID → item count
429+
event_counts = {1001: 5, 1002: 12, 1003: 0}
430+
431+
# UUID keys — e.g., session ID → score
432+
import uuid
433+
session_scores = {
434+
uuid.UUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8"): 0.95,
435+
uuid.UUID("a8098c1a-f86e-11da-bd1a-00112444be1e"): 0.87,
436+
}
437+
438+
# Decimal keys — e.g., price bucket → product name
439+
price_tier = {
440+
decimal.Decimal("9.99"): "budget",
441+
decimal.Decimal("49.99"): "standard",
442+
decimal.Decimal("99.99"): "premium",
443+
}
444+
445+
# Type inference: Feast automatically picks SCALAR_MAP when the key is non-string
446+
from feast.type_map import python_type_to_feast_value_type
447+
from feast.value_type import ValueType
448+
449+
python_type_to_feast_value_type({1: "a"}) # → ValueType.SCALAR_MAP
450+
python_type_to_feast_value_type({"a": 1}) # → ValueType.MAP
451+
python_type_to_feast_value_type({}) # → ValueType.MAP (empty dict defaults to MAP)
452+
```
453+
454+
{% hint style="warning" %}
455+
`ScalarMap` must be **explicitly declared** in your feature view schema — it is never inferred from backend type schemas. It is best suited for online serving via stores that use proto byte serialization (e.g., Redis, DynamoDB, SQLite). Materialization paths that use PyArrow (e.g., BigQuery, Snowflake, Redshift, Spark) do not have native `ScalarMap` support.
456+
{% endhint %}
457+
414458
### JSON Type Usage Examples
415459

416460
Feast's `Json` type stores values as JSON strings at the proto level. You can pass either a
@@ -461,7 +505,7 @@ Each of these columns must be associated with a Feast type, which requires conve
461505
* `source_datatype_to_feast_value_type` calls the appropriate method in `type_map.py`. For example, if a `SnowflakeSource` is being examined, `snowflake_python_type_to_feast_value_type` from `type_map.py` will be called.
462506

463507
{% hint style="info" %}
464-
**Types that cannot be inferred:** `Set`, `Json`, `Struct`, `Decimal`, `PdfBytes`, and `ImageBytes` types are never inferred from backend schemas. If you use these types, you must declare them explicitly in your feature view schema.
508+
**Types that cannot be inferred:** `Set`, `Json`, `Struct`, `Decimal`, `ScalarMap`, `PdfBytes`, and `ImageBytes` types are never inferred from backend schemas. If you use these types, you must declare them explicitly in your feature view schema.
465509
{% endhint %}
466510

467511
### Materialization

go/infra/docker/feature-server/Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
FROM golang:1.24.12
1+
FROM golang:1.25
2+
ENV GOTOOLCHAIN=auto
23

34
# Update the package list and install the ca-certificates package
45
RUN apt-get update && apt-get install -y ca-certificates

infra/feast-operator/Dockerfile

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@ ARG TARGETARCH
55
ENV GOTOOLCHAIN=auto
66

77
# Copy the Go Modules manifests
8-
COPY go.mod go.mod
9-
COPY go.sum go.sum
8+
COPY --chown=1001:0 go.mod go.mod
9+
COPY --chown=1001:0 go.sum go.sum
1010
# cache deps before building and copying source so that we don't need to re-download as much
1111
# and so that source changes don't invalidate our downloaded layer
1212
RUN go mod download
1313

1414
# Copy the go source
15-
COPY cmd/main.go cmd/main.go
16-
COPY api/ api/
17-
COPY internal/controller/ internal/controller/
15+
COPY --chown=1001:0 cmd/main.go cmd/main.go
16+
COPY --chown=1001:0 api/ api/
17+
COPY --chown=1001:0 internal/controller/ internal/controller/
1818

1919
# Build
2020
# the GOARCH has not a default value to allow the binary be built according to the host where the command

infra/feast-operator/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ ENVSUBST = $(LOCALBIN)/envsubst
241241
## Tool Versions
242242
KUSTOMIZE_VERSION ?= v5.4.3
243243
CONTROLLER_TOOLS_VERSION ?= v0.18.0
244-
CRD_REF_DOCS_VERSION ?= v0.3.0
244+
CRD_REF_DOCS_VERSION ?= v0.2.0
245245
ENVTEST_VERSION ?= release-0.21
246246
GOLANGCI_LINT_VERSION ?= v2.1.0
247247
ENVSUBST_VERSION ?= v1.4.2

infra/feast-operator/go.mod

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ require (
77
github.com/onsi/gomega v1.36.2
88
github.com/openshift/api v0.0.0-20240912201240-0a8800162826 // release-4.17
99
gopkg.in/yaml.v3 v3.0.1
10-
k8s.io/api v0.33.0
11-
k8s.io/apimachinery v0.33.0
12-
k8s.io/client-go v0.33.0
10+
k8s.io/api v0.33.1
11+
k8s.io/apimachinery v0.33.1
12+
k8s.io/client-go v0.33.1
1313
sigs.k8s.io/controller-runtime v0.21.0
1414
)
1515

1616
require (
17-
github.com/prometheus-operator/prometheus-operator/pkg/client v0.75.0
18-
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738
17+
github.com/prometheus-operator/prometheus-operator/pkg/client v0.83.0
18+
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979
1919
)
2020

2121
require (
@@ -26,17 +26,17 @@ require (
2626
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
2727
github.com/cespare/xxhash/v2 v2.3.0 // indirect
2828
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
29-
github.com/emicklei/go-restful/v3 v3.12.1 // indirect
29+
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
3030
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
3131
github.com/felixge/httpsnoop v1.0.4 // indirect
3232
github.com/fsnotify/fsnotify v1.7.0 // indirect
33-
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
33+
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
3434
github.com/go-logr/logr v1.4.2 // indirect
3535
github.com/go-logr/stdr v1.2.2 // indirect
3636
github.com/go-logr/zapr v1.3.0 // indirect
37-
github.com/go-openapi/jsonpointer v0.21.0 // indirect
37+
github.com/go-openapi/jsonpointer v0.21.1 // indirect
3838
github.com/go-openapi/jsonreference v0.21.0 // indirect
39-
github.com/go-openapi/swag v0.23.0 // indirect
39+
github.com/go-openapi/swag v0.23.1 // indirect
4040
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
4141
github.com/gogo/protobuf v1.3.2 // indirect
4242
github.com/google/btree v1.1.3 // indirect
@@ -49,12 +49,12 @@ require (
4949
github.com/inconshreveable/mousetrap v1.1.0 // indirect
5050
github.com/josharian/intern v1.0.0 // indirect
5151
github.com/json-iterator/go v1.1.12 // indirect
52-
github.com/mailru/easyjson v0.7.7 // indirect
52+
github.com/mailru/easyjson v0.9.0 // indirect
5353
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
5454
github.com/modern-go/reflect2 v1.0.2 // indirect
5555
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
5656
github.com/pkg/errors v0.9.1 // indirect
57-
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.75.0 // indirect
57+
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.83.0 // indirect
5858
github.com/prometheus/client_golang v1.22.0 // indirect
5959
github.com/prometheus/client_model v0.6.1 // indirect
6060
github.com/prometheus/common v0.62.0 // indirect
@@ -75,29 +75,29 @@ require (
7575
go.uber.org/multierr v1.11.0 // indirect
7676
go.uber.org/zap v1.27.0 // indirect
7777
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
78-
golang.org/x/net v0.38.0 // indirect
79-
golang.org/x/oauth2 v0.27.0 // indirect
80-
golang.org/x/sync v0.12.0 // indirect
81-
golang.org/x/sys v0.31.0 // indirect
82-
golang.org/x/term v0.30.0 // indirect
83-
golang.org/x/text v0.23.0 // indirect
84-
golang.org/x/time v0.9.0 // indirect
78+
golang.org/x/net v0.40.0 // indirect
79+
golang.org/x/oauth2 v0.30.0 // indirect
80+
golang.org/x/sync v0.14.0 // indirect
81+
golang.org/x/sys v0.33.0 // indirect
82+
golang.org/x/term v0.32.0 // indirect
83+
golang.org/x/text v0.25.0 // indirect
84+
golang.org/x/time v0.11.0 // indirect
8585
golang.org/x/tools v0.28.0 // indirect
8686
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
8787
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect
8888
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect
8989
google.golang.org/grpc v1.68.1 // indirect
90-
google.golang.org/protobuf v1.36.5 // indirect
90+
google.golang.org/protobuf v1.36.6 // indirect
9191
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
9292
gopkg.in/inf.v0 v0.9.1 // indirect
93-
k8s.io/apiextensions-apiserver v0.33.0 // indirect
94-
k8s.io/apiserver v0.33.0 // indirect
95-
k8s.io/component-base v0.33.0 // indirect
93+
k8s.io/apiextensions-apiserver v0.33.1 // indirect
94+
k8s.io/apiserver v0.33.1 // indirect
95+
k8s.io/component-base v0.33.1 // indirect
9696
k8s.io/klog/v2 v2.130.1 // indirect
9797
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
9898
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect
99-
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
99+
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
100100
sigs.k8s.io/randfill v1.0.0 // indirect
101-
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
101+
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
102102
sigs.k8s.io/yaml v1.4.0 // indirect
103103
)

0 commit comments

Comments
 (0)