The gcp:vertex/aiFeatureOnlineStoreFeatureview:AiFeatureOnlineStoreFeatureview resource, part of the Pulumi GCP provider, defines how features are synced from BigQuery or Feature Registry into a FeatureOnlineStore for low-latency serving. This guide focuses on three capabilities: BigQuery source configuration, Feature Registry source configuration, and vector search indexing for embeddings.
FeatureViews belong to a FeatureOnlineStore and reference BigQuery tables or Feature Registry resources that must exist separately. The examples are intentionally small. Combine them with your own FeatureOnlineStore, BigQuery tables, and Feature Groups.
Sync features from BigQuery on a schedule
ML pipelines often store feature data in BigQuery tables and need to serve those features with low latency for online prediction. FeatureViews sync data from BigQuery into a FeatureOnlineStore on a schedule.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const featureonlinestore = new gcp.vertex.AiFeatureOnlineStore("featureonlinestore", {
name: "example_feature_view",
labels: {
foo: "bar",
},
region: "us-central1",
bigtable: {
autoScaling: {
minNodeCount: 1,
maxNodeCount: 2,
cpuUtilizationTarget: 80,
},
},
});
const tf_test_dataset = new gcp.bigquery.Dataset("tf-test-dataset", {
datasetId: "example_feature_view",
friendlyName: "test",
description: "This is a test description",
location: "US",
});
const tf_test_table = new gcp.bigquery.Table("tf-test-table", {
deletionProtection: false,
datasetId: tf_test_dataset.datasetId,
tableId: "example_feature_view",
schema: ` [
{
\\"name\\": \\"entity_id\\",
\\"mode\\": \\"NULLABLE\\",
\\"type\\": \\"STRING\\",
\\"description\\": \\"Test default entity_id\\"
},
{
\\"name\\": \\"test_entity_column\\",
\\"mode\\": \\"NULLABLE\\",
\\"type\\": \\"STRING\\",
\\"description\\": \\"test secondary entity column\\"
},
{
\\"name\\": \\"feature_timestamp\\",
\\"mode\\": \\"NULLABLE\\",
\\"type\\": \\"TIMESTAMP\\",
\\"description\\": \\"Default timestamp value\\"
}
]
`,
});
const featureview = new gcp.vertex.AiFeatureOnlineStoreFeatureview("featureview", {
name: "example_feature_view",
region: "us-central1",
featureOnlineStore: featureonlinestore.name,
syncConfig: {
cron: "0 0 * * *",
},
bigQuerySource: {
uri: pulumi.interpolate`bq://${tf_test_table.project}.${tf_test_table.datasetId}.${tf_test_table.tableId}`,
entityIdColumns: ["test_entity_column"],
},
});
const project = gcp.organizations.getProject({});
import pulumi
import pulumi_gcp as gcp
featureonlinestore = gcp.vertex.AiFeatureOnlineStore("featureonlinestore",
name="example_feature_view",
labels={
"foo": "bar",
},
region="us-central1",
bigtable={
"auto_scaling": {
"min_node_count": 1,
"max_node_count": 2,
"cpu_utilization_target": 80,
},
})
tf_test_dataset = gcp.bigquery.Dataset("tf-test-dataset",
dataset_id="example_feature_view",
friendly_name="test",
description="This is a test description",
location="US")
tf_test_table = gcp.bigquery.Table("tf-test-table",
deletion_protection=False,
dataset_id=tf_test_dataset.dataset_id,
table_id="example_feature_view",
schema=""" [
{
\"name\": \"entity_id\",
\"mode\": \"NULLABLE\",
\"type\": \"STRING\",
\"description\": \"Test default entity_id\"
},
{
\"name\": \"test_entity_column\",
\"mode\": \"NULLABLE\",
\"type\": \"STRING\",
\"description\": \"test secondary entity column\"
},
{
\"name\": \"feature_timestamp\",
\"mode\": \"NULLABLE\",
\"type\": \"TIMESTAMP\",
\"description\": \"Default timestamp value\"
}
]
""")
featureview = gcp.vertex.AiFeatureOnlineStoreFeatureview("featureview",
name="example_feature_view",
region="us-central1",
feature_online_store=featureonlinestore.name,
sync_config={
"cron": "0 0 * * *",
},
big_query_source={
"uri": pulumi.Output.all(
project=tf_test_table.project,
dataset_id=tf_test_table.dataset_id,
table_id=tf_test_table.table_id
).apply(lambda resolved_outputs: f"bq://{resolved_outputs['project']}.{resolved_outputs['dataset_id']}.{resolved_outputs['table_id']}")
,
"entity_id_columns": ["test_entity_column"],
})
project = gcp.organizations.get_project()
package main
import (
"fmt"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/bigquery"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/vertex"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
featureonlinestore, err := vertex.NewAiFeatureOnlineStore(ctx, "featureonlinestore", &vertex.AiFeatureOnlineStoreArgs{
Name: pulumi.String("example_feature_view"),
Labels: pulumi.StringMap{
"foo": pulumi.String("bar"),
},
Region: pulumi.String("us-central1"),
Bigtable: &vertex.AiFeatureOnlineStoreBigtableArgs{
AutoScaling: &vertex.AiFeatureOnlineStoreBigtableAutoScalingArgs{
MinNodeCount: pulumi.Int(1),
MaxNodeCount: pulumi.Int(2),
CpuUtilizationTarget: pulumi.Int(80),
},
},
})
if err != nil {
return err
}
tf_test_dataset, err := bigquery.NewDataset(ctx, "tf-test-dataset", &bigquery.DatasetArgs{
DatasetId: pulumi.String("example_feature_view"),
FriendlyName: pulumi.String("test"),
Description: pulumi.String("This is a test description"),
Location: pulumi.String("US"),
})
if err != nil {
return err
}
tf_test_table, err := bigquery.NewTable(ctx, "tf-test-table", &bigquery.TableArgs{
DeletionProtection: pulumi.Bool(false),
DatasetId: tf_test_dataset.DatasetId,
TableId: pulumi.String("example_feature_view"),
Schema: pulumi.String(` [
{
\"name\": \"entity_id\",
\"mode\": \"NULLABLE\",
\"type\": \"STRING\",
\"description\": \"Test default entity_id\"
},
{
\"name\": \"test_entity_column\",
\"mode\": \"NULLABLE\",
\"type\": \"STRING\",
\"description\": \"test secondary entity column\"
},
{
\"name\": \"feature_timestamp\",
\"mode\": \"NULLABLE\",
\"type\": \"TIMESTAMP\",
\"description\": \"Default timestamp value\"
}
]
`),
})
if err != nil {
return err
}
_, err = vertex.NewAiFeatureOnlineStoreFeatureview(ctx, "featureview", &vertex.AiFeatureOnlineStoreFeatureviewArgs{
Name: pulumi.String("example_feature_view"),
Region: pulumi.String("us-central1"),
FeatureOnlineStore: featureonlinestore.Name,
SyncConfig: &vertex.AiFeatureOnlineStoreFeatureviewSyncConfigArgs{
Cron: pulumi.String("0 0 * * *"),
},
BigQuerySource: &vertex.AiFeatureOnlineStoreFeatureviewBigQuerySourceArgs{
Uri: pulumi.All(tf_test_table.Project, tf_test_table.DatasetId, tf_test_table.TableId).ApplyT(func(_args []interface{}) (string, error) {
project := _args[0].(string)
datasetId := _args[1].(string)
tableId := _args[2].(string)
return fmt.Sprintf("bq://%v.%v.%v", project, datasetId, tableId), nil
}).(pulumi.StringOutput),
EntityIdColumns: pulumi.StringArray{
pulumi.String("test_entity_column"),
},
},
})
if err != nil {
return err
}
_, err = organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
return await Deployment.RunAsync(() =>
{
var featureonlinestore = new Gcp.Vertex.AiFeatureOnlineStore("featureonlinestore", new()
{
Name = "example_feature_view",
Labels =
{
{ "foo", "bar" },
},
Region = "us-central1",
Bigtable = new Gcp.Vertex.Inputs.AiFeatureOnlineStoreBigtableArgs
{
AutoScaling = new Gcp.Vertex.Inputs.AiFeatureOnlineStoreBigtableAutoScalingArgs
{
MinNodeCount = 1,
MaxNodeCount = 2,
CpuUtilizationTarget = 80,
},
},
});
var tf_test_dataset = new Gcp.BigQuery.Dataset("tf-test-dataset", new()
{
DatasetId = "example_feature_view",
FriendlyName = "test",
Description = "This is a test description",
Location = "US",
});
var tf_test_table = new Gcp.BigQuery.Table("tf-test-table", new()
{
DeletionProtection = false,
DatasetId = tf_test_dataset.DatasetId,
TableId = "example_feature_view",
Schema = @" [
{
\""name\"": \""entity_id\"",
\""mode\"": \""NULLABLE\"",
\""type\"": \""STRING\"",
\""description\"": \""Test default entity_id\""
},
{
\""name\"": \""test_entity_column\"",
\""mode\"": \""NULLABLE\"",
\""type\"": \""STRING\"",
\""description\"": \""test secondary entity column\""
},
{
\""name\"": \""feature_timestamp\"",
\""mode\"": \""NULLABLE\"",
\""type\"": \""TIMESTAMP\"",
\""description\"": \""Default timestamp value\""
}
]
",
});
var featureview = new Gcp.Vertex.AiFeatureOnlineStoreFeatureview("featureview", new()
{
Name = "example_feature_view",
Region = "us-central1",
FeatureOnlineStore = featureonlinestore.Name,
SyncConfig = new Gcp.Vertex.Inputs.AiFeatureOnlineStoreFeatureviewSyncConfigArgs
{
Cron = "0 0 * * *",
},
BigQuerySource = new Gcp.Vertex.Inputs.AiFeatureOnlineStoreFeatureviewBigQuerySourceArgs
{
Uri = Output.Tuple(tf_test_table.Project, tf_test_table.DatasetId, tf_test_table.TableId).Apply(values =>
{
var project = values.Item1;
var datasetId = values.Item2;
var tableId = values.Item3;
return $"bq://{project}.{datasetId}.{tableId}";
}),
EntityIdColumns = new[]
{
"test_entity_column",
},
},
});
var project = Gcp.Organizations.GetProject.Invoke();
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.vertex.AiFeatureOnlineStore;
import com.pulumi.gcp.vertex.AiFeatureOnlineStoreArgs;
import com.pulumi.gcp.vertex.inputs.AiFeatureOnlineStoreBigtableArgs;
import com.pulumi.gcp.vertex.inputs.AiFeatureOnlineStoreBigtableAutoScalingArgs;
import com.pulumi.gcp.bigquery.Dataset;
import com.pulumi.gcp.bigquery.DatasetArgs;
import com.pulumi.gcp.bigquery.Table;
import com.pulumi.gcp.bigquery.TableArgs;
import com.pulumi.gcp.vertex.AiFeatureOnlineStoreFeatureview;
import com.pulumi.gcp.vertex.AiFeatureOnlineStoreFeatureviewArgs;
import com.pulumi.gcp.vertex.inputs.AiFeatureOnlineStoreFeatureviewSyncConfigArgs;
import com.pulumi.gcp.vertex.inputs.AiFeatureOnlineStoreFeatureviewBigQuerySourceArgs;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var featureonlinestore = new AiFeatureOnlineStore("featureonlinestore", AiFeatureOnlineStoreArgs.builder()
.name("example_feature_view")
.labels(Map.of("foo", "bar"))
.region("us-central1")
.bigtable(AiFeatureOnlineStoreBigtableArgs.builder()
.autoScaling(AiFeatureOnlineStoreBigtableAutoScalingArgs.builder()
.minNodeCount(1)
.maxNodeCount(2)
.cpuUtilizationTarget(80)
.build())
.build())
.build());
var tf_test_dataset = new Dataset("tf-test-dataset", DatasetArgs.builder()
.datasetId("example_feature_view")
.friendlyName("test")
.description("This is a test description")
.location("US")
.build());
var tf_test_table = new Table("tf-test-table", TableArgs.builder()
.deletionProtection(false)
.datasetId(tf_test_dataset.datasetId())
.tableId("example_feature_view")
.schema("""
[
{
\"name\": \"entity_id\",
\"mode\": \"NULLABLE\",
\"type\": \"STRING\",
\"description\": \"Test default entity_id\"
},
{
\"name\": \"test_entity_column\",
\"mode\": \"NULLABLE\",
\"type\": \"STRING\",
\"description\": \"test secondary entity column\"
},
{
\"name\": \"feature_timestamp\",
\"mode\": \"NULLABLE\",
\"type\": \"TIMESTAMP\",
\"description\": \"Default timestamp value\"
}
]
""")
.build());
var featureview = new AiFeatureOnlineStoreFeatureview("featureview", AiFeatureOnlineStoreFeatureviewArgs.builder()
.name("example_feature_view")
.region("us-central1")
.featureOnlineStore(featureonlinestore.name())
.syncConfig(AiFeatureOnlineStoreFeatureviewSyncConfigArgs.builder()
.cron("0 0 * * *")
.build())
.bigQuerySource(AiFeatureOnlineStoreFeatureviewBigQuerySourceArgs.builder()
.uri(Output.tuple(tf_test_table.project(), tf_test_table.datasetId(), tf_test_table.tableId()).applyValue(values -> {
var project = values.t1;
var datasetId = values.t2;
var tableId = values.t3;
return String.format("bq://%s.%s.%s", project,datasetId,tableId);
}))
.entityIdColumns("test_entity_column")
.build())
.build());
final var project = OrganizationsFunctions.getProject(GetProjectArgs.builder()
.build());
}
}
resources:
featureonlinestore:
type: gcp:vertex:AiFeatureOnlineStore
properties:
name: example_feature_view
labels:
foo: bar
region: us-central1
bigtable:
autoScaling:
minNodeCount: 1
maxNodeCount: 2
cpuUtilizationTarget: 80
tf-test-dataset:
type: gcp:bigquery:Dataset
properties:
datasetId: example_feature_view
friendlyName: test
description: This is a test description
location: US
tf-test-table:
type: gcp:bigquery:Table
properties:
deletionProtection: false
datasetId: ${["tf-test-dataset"].datasetId}
tableId: example_feature_view
schema: |2
[
{
\"name\": \"entity_id\",
\"mode\": \"NULLABLE\",
\"type\": \"STRING\",
\"description\": \"Test default entity_id\"
},
{
\"name\": \"test_entity_column\",
\"mode\": \"NULLABLE\",
\"type\": \"STRING\",
\"description\": \"test secondary entity column\"
},
{
\"name\": \"feature_timestamp\",
\"mode\": \"NULLABLE\",
\"type\": \"TIMESTAMP\",
\"description\": \"Default timestamp value\"
}
]
featureview:
type: gcp:vertex:AiFeatureOnlineStoreFeatureview
properties:
name: example_feature_view
region: us-central1
featureOnlineStore: ${featureonlinestore.name}
syncConfig:
cron: 0 0 * * *
bigQuerySource:
uri: bq://${["tf-test-table"].project}.${["tf-test-table"].datasetId}.${["tf-test-table"].tableId}
entityIdColumns:
- test_entity_column
variables:
project:
fn::invoke:
function: gcp:organizations:getProject
arguments: {}
The bigQuerySource property points to your BigQuery table using a bq:// URI format. The entityIdColumns property specifies which columns identify unique entities for feature lookup. The syncConfig property controls when data is refreshed; here, the cron expression 0 0 * * * syncs daily at midnight UTC. At each sync, Vertex AI reads the latest feature values from BigQuery and updates the FeatureOnlineStore for online serving.
Sync features from Feature Registry
Teams using Vertex AI Feature Store organize features into Feature Groups for reuse across models. FeatureViews can sync specific features from the Feature Registry rather than directly from BigQuery.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const featureonlinestore = new gcp.vertex.AiFeatureOnlineStore("featureonlinestore", {
name: "example_feature_view_feature_registry",
labels: {
foo: "bar",
},
region: "us-central1",
bigtable: {
autoScaling: {
minNodeCount: 1,
maxNodeCount: 2,
cpuUtilizationTarget: 80,
},
},
});
const sampleDataset = new gcp.bigquery.Dataset("sample_dataset", {
datasetId: "example_feature_view_feature_registry",
friendlyName: "test",
description: "This is a test description",
location: "US",
});
const sampleTable = new gcp.bigquery.Table("sample_table", {
deletionProtection: false,
datasetId: sampleDataset.datasetId,
tableId: "example_feature_view_feature_registry",
schema: `[
{
\\"name\\": \\"feature_id\\",
\\"type\\": \\"STRING\\",
\\"mode\\": \\"NULLABLE\\"
},
{
\\"name\\": \\"example_feature_view_feature_registry\\",
\\"type\\": \\"STRING\\",
\\"mode\\": \\"NULLABLE\\"
},
{
\\"name\\": \\"feature_timestamp\\",
\\"type\\": \\"TIMESTAMP\\",
\\"mode\\": \\"NULLABLE\\"
}
]
`,
});
const sampleFeatureGroup = new gcp.vertex.AiFeatureGroup("sample_feature_group", {
name: "example_feature_view_feature_registry",
description: "A sample feature group",
region: "us-central1",
labels: {
"label-one": "value-one",
},
bigQuery: {
bigQuerySource: {
inputUri: pulumi.interpolate`bq://${sampleTable.project}.${sampleTable.datasetId}.${sampleTable.tableId}`,
},
entityIdColumns: ["feature_id"],
},
});
const sampleFeature = new gcp.vertex.AiFeatureGroupFeature("sample_feature", {
name: "example_feature_view_feature_registry",
region: "us-central1",
featureGroup: sampleFeatureGroup.name,
description: "A sample feature",
labels: {
"label-one": "value-one",
},
});
const featureviewFeatureregistry = new gcp.vertex.AiFeatureOnlineStoreFeatureview("featureview_featureregistry", {
name: "example_feature_view_feature_registry",
region: "us-central1",
featureOnlineStore: featureonlinestore.name,
syncConfig: {
cron: "0 0 * * *",
},
featureRegistrySource: {
featureGroups: [{
featureGroupId: sampleFeatureGroup.name,
featureIds: [sampleFeature.name],
}],
},
});
import pulumi
import pulumi_gcp as gcp
featureonlinestore = gcp.vertex.AiFeatureOnlineStore("featureonlinestore",
name="example_feature_view_feature_registry",
labels={
"foo": "bar",
},
region="us-central1",
bigtable={
"auto_scaling": {
"min_node_count": 1,
"max_node_count": 2,
"cpu_utilization_target": 80,
},
})
sample_dataset = gcp.bigquery.Dataset("sample_dataset",
dataset_id="example_feature_view_feature_registry",
friendly_name="test",
description="This is a test description",
location="US")
sample_table = gcp.bigquery.Table("sample_table",
deletion_protection=False,
dataset_id=sample_dataset.dataset_id,
table_id="example_feature_view_feature_registry",
schema="""[
{
\"name\": \"feature_id\",
\"type\": \"STRING\",
\"mode\": \"NULLABLE\"
},
{
\"name\": \"example_feature_view_feature_registry\",
\"type\": \"STRING\",
\"mode\": \"NULLABLE\"
},
{
\"name\": \"feature_timestamp\",
\"type\": \"TIMESTAMP\",
\"mode\": \"NULLABLE\"
}
]
""")
sample_feature_group = gcp.vertex.AiFeatureGroup("sample_feature_group",
name="example_feature_view_feature_registry",
description="A sample feature group",
region="us-central1",
labels={
"label-one": "value-one",
},
big_query={
"big_query_source": {
"input_uri": pulumi.Output.all(
project=sample_table.project,
dataset_id=sample_table.dataset_id,
table_id=sample_table.table_id
).apply(lambda resolved_outputs: f"bq://{resolved_outputs['project']}.{resolved_outputs['dataset_id']}.{resolved_outputs['table_id']}")
,
},
"entity_id_columns": ["feature_id"],
})
sample_feature = gcp.vertex.AiFeatureGroupFeature("sample_feature",
name="example_feature_view_feature_registry",
region="us-central1",
feature_group=sample_feature_group.name,
description="A sample feature",
labels={
"label-one": "value-one",
})
featureview_featureregistry = gcp.vertex.AiFeatureOnlineStoreFeatureview("featureview_featureregistry",
name="example_feature_view_feature_registry",
region="us-central1",
feature_online_store=featureonlinestore.name,
sync_config={
"cron": "0 0 * * *",
},
feature_registry_source={
"feature_groups": [{
"feature_group_id": sample_feature_group.name,
"feature_ids": [sample_feature.name],
}],
})
package main
import (
"fmt"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/bigquery"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/vertex"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
featureonlinestore, err := vertex.NewAiFeatureOnlineStore(ctx, "featureonlinestore", &vertex.AiFeatureOnlineStoreArgs{
Name: pulumi.String("example_feature_view_feature_registry"),
Labels: pulumi.StringMap{
"foo": pulumi.String("bar"),
},
Region: pulumi.String("us-central1"),
Bigtable: &vertex.AiFeatureOnlineStoreBigtableArgs{
AutoScaling: &vertex.AiFeatureOnlineStoreBigtableAutoScalingArgs{
MinNodeCount: pulumi.Int(1),
MaxNodeCount: pulumi.Int(2),
CpuUtilizationTarget: pulumi.Int(80),
},
},
})
if err != nil {
return err
}
sampleDataset, err := bigquery.NewDataset(ctx, "sample_dataset", &bigquery.DatasetArgs{
DatasetId: pulumi.String("example_feature_view_feature_registry"),
FriendlyName: pulumi.String("test"),
Description: pulumi.String("This is a test description"),
Location: pulumi.String("US"),
})
if err != nil {
return err
}
sampleTable, err := bigquery.NewTable(ctx, "sample_table", &bigquery.TableArgs{
DeletionProtection: pulumi.Bool(false),
DatasetId: sampleDataset.DatasetId,
TableId: pulumi.String("example_feature_view_feature_registry"),
Schema: pulumi.String(`[
{
\"name\": \"feature_id\",
\"type\": \"STRING\",
\"mode\": \"NULLABLE\"
},
{
\"name\": \"example_feature_view_feature_registry\",
\"type\": \"STRING\",
\"mode\": \"NULLABLE\"
},
{
\"name\": \"feature_timestamp\",
\"type\": \"TIMESTAMP\",
\"mode\": \"NULLABLE\"
}
]
`),
})
if err != nil {
return err
}
sampleFeatureGroup, err := vertex.NewAiFeatureGroup(ctx, "sample_feature_group", &vertex.AiFeatureGroupArgs{
Name: pulumi.String("example_feature_view_feature_registry"),
Description: pulumi.String("A sample feature group"),
Region: pulumi.String("us-central1"),
Labels: pulumi.StringMap{
"label-one": pulumi.String("value-one"),
},
BigQuery: &vertex.AiFeatureGroupBigQueryArgs{
BigQuerySource: &vertex.AiFeatureGroupBigQueryBigQuerySourceArgs{
InputUri: pulumi.All(sampleTable.Project, sampleTable.DatasetId, sampleTable.TableId).ApplyT(func(_args []interface{}) (string, error) {
project := _args[0].(string)
datasetId := _args[1].(string)
tableId := _args[2].(string)
return fmt.Sprintf("bq://%v.%v.%v", project, datasetId, tableId), nil
}).(pulumi.StringOutput),
},
EntityIdColumns: pulumi.StringArray{
pulumi.String("feature_id"),
},
},
})
if err != nil {
return err
}
sampleFeature, err := vertex.NewAiFeatureGroupFeature(ctx, "sample_feature", &vertex.AiFeatureGroupFeatureArgs{
Name: pulumi.String("example_feature_view_feature_registry"),
Region: pulumi.String("us-central1"),
FeatureGroup: sampleFeatureGroup.Name,
Description: pulumi.String("A sample feature"),
Labels: pulumi.StringMap{
"label-one": pulumi.String("value-one"),
},
})
if err != nil {
return err
}
_, err = vertex.NewAiFeatureOnlineStoreFeatureview(ctx, "featureview_featureregistry", &vertex.AiFeatureOnlineStoreFeatureviewArgs{
Name: pulumi.String("example_feature_view_feature_registry"),
Region: pulumi.String("us-central1"),
FeatureOnlineStore: featureonlinestore.Name,
SyncConfig: &vertex.AiFeatureOnlineStoreFeatureviewSyncConfigArgs{
Cron: pulumi.String("0 0 * * *"),
},
FeatureRegistrySource: &vertex.AiFeatureOnlineStoreFeatureviewFeatureRegistrySourceArgs{
FeatureGroups: vertex.AiFeatureOnlineStoreFeatureviewFeatureRegistrySourceFeatureGroupArray{
&vertex.AiFeatureOnlineStoreFeatureviewFeatureRegistrySourceFeatureGroupArgs{
FeatureGroupId: sampleFeatureGroup.Name,
FeatureIds: pulumi.StringArray{
sampleFeature.Name,
},
},
},
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
return await Deployment.RunAsync(() =>
{
var featureonlinestore = new Gcp.Vertex.AiFeatureOnlineStore("featureonlinestore", new()
{
Name = "example_feature_view_feature_registry",
Labels =
{
{ "foo", "bar" },
},
Region = "us-central1",
Bigtable = new Gcp.Vertex.Inputs.AiFeatureOnlineStoreBigtableArgs
{
AutoScaling = new Gcp.Vertex.Inputs.AiFeatureOnlineStoreBigtableAutoScalingArgs
{
MinNodeCount = 1,
MaxNodeCount = 2,
CpuUtilizationTarget = 80,
},
},
});
var sampleDataset = new Gcp.BigQuery.Dataset("sample_dataset", new()
{
DatasetId = "example_feature_view_feature_registry",
FriendlyName = "test",
Description = "This is a test description",
Location = "US",
});
var sampleTable = new Gcp.BigQuery.Table("sample_table", new()
{
DeletionProtection = false,
DatasetId = sampleDataset.DatasetId,
TableId = "example_feature_view_feature_registry",
Schema = @"[
{
\""name\"": \""feature_id\"",
\""type\"": \""STRING\"",
\""mode\"": \""NULLABLE\""
},
{
\""name\"": \""example_feature_view_feature_registry\"",
\""type\"": \""STRING\"",
\""mode\"": \""NULLABLE\""
},
{
\""name\"": \""feature_timestamp\"",
\""type\"": \""TIMESTAMP\"",
\""mode\"": \""NULLABLE\""
}
]
",
});
var sampleFeatureGroup = new Gcp.Vertex.AiFeatureGroup("sample_feature_group", new()
{
Name = "example_feature_view_feature_registry",
Description = "A sample feature group",
Region = "us-central1",
Labels =
{
{ "label-one", "value-one" },
},
BigQuery = new Gcp.Vertex.Inputs.AiFeatureGroupBigQueryArgs
{
BigQuerySource = new Gcp.Vertex.Inputs.AiFeatureGroupBigQueryBigQuerySourceArgs
{
InputUri = Output.Tuple(sampleTable.Project, sampleTable.DatasetId, sampleTable.TableId).Apply(values =>
{
var project = values.Item1;
var datasetId = values.Item2;
var tableId = values.Item3;
return $"bq://{project}.{datasetId}.{tableId}";
}),
},
EntityIdColumns = new[]
{
"feature_id",
},
},
});
var sampleFeature = new Gcp.Vertex.AiFeatureGroupFeature("sample_feature", new()
{
Name = "example_feature_view_feature_registry",
Region = "us-central1",
FeatureGroup = sampleFeatureGroup.Name,
Description = "A sample feature",
Labels =
{
{ "label-one", "value-one" },
},
});
var featureviewFeatureregistry = new Gcp.Vertex.AiFeatureOnlineStoreFeatureview("featureview_featureregistry", new()
{
Name = "example_feature_view_feature_registry",
Region = "us-central1",
FeatureOnlineStore = featureonlinestore.Name,
SyncConfig = new Gcp.Vertex.Inputs.AiFeatureOnlineStoreFeatureviewSyncConfigArgs
{
Cron = "0 0 * * *",
},
FeatureRegistrySource = new Gcp.Vertex.Inputs.AiFeatureOnlineStoreFeatureviewFeatureRegistrySourceArgs
{
FeatureGroups = new[]
{
new Gcp.Vertex.Inputs.AiFeatureOnlineStoreFeatureviewFeatureRegistrySourceFeatureGroupArgs
{
FeatureGroupId = sampleFeatureGroup.Name,
FeatureIds = new[]
{
sampleFeature.Name,
},
},
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.vertex.AiFeatureOnlineStore;
import com.pulumi.gcp.vertex.AiFeatureOnlineStoreArgs;
import com.pulumi.gcp.vertex.inputs.AiFeatureOnlineStoreBigtableArgs;
import com.pulumi.gcp.vertex.inputs.AiFeatureOnlineStoreBigtableAutoScalingArgs;
import com.pulumi.gcp.bigquery.Dataset;
import com.pulumi.gcp.bigquery.DatasetArgs;
import com.pulumi.gcp.bigquery.Table;
import com.pulumi.gcp.bigquery.TableArgs;
import com.pulumi.gcp.vertex.AiFeatureGroup;
import com.pulumi.gcp.vertex.AiFeatureGroupArgs;
import com.pulumi.gcp.vertex.inputs.AiFeatureGroupBigQueryArgs;
import com.pulumi.gcp.vertex.inputs.AiFeatureGroupBigQueryBigQuerySourceArgs;
import com.pulumi.gcp.vertex.AiFeatureGroupFeature;
import com.pulumi.gcp.vertex.AiFeatureGroupFeatureArgs;
import com.pulumi.gcp.vertex.AiFeatureOnlineStoreFeatureview;
import com.pulumi.gcp.vertex.AiFeatureOnlineStoreFeatureviewArgs;
import com.pulumi.gcp.vertex.inputs.AiFeatureOnlineStoreFeatureviewSyncConfigArgs;
import com.pulumi.gcp.vertex.inputs.AiFeatureOnlineStoreFeatureviewFeatureRegistrySourceArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var featureonlinestore = new AiFeatureOnlineStore("featureonlinestore", AiFeatureOnlineStoreArgs.builder()
.name("example_feature_view_feature_registry")
.labels(Map.of("foo", "bar"))
.region("us-central1")
.bigtable(AiFeatureOnlineStoreBigtableArgs.builder()
.autoScaling(AiFeatureOnlineStoreBigtableAutoScalingArgs.builder()
.minNodeCount(1)
.maxNodeCount(2)
.cpuUtilizationTarget(80)
.build())
.build())
.build());
var sampleDataset = new Dataset("sampleDataset", DatasetArgs.builder()
.datasetId("example_feature_view_feature_registry")
.friendlyName("test")
.description("This is a test description")
.location("US")
.build());
var sampleTable = new Table("sampleTable", TableArgs.builder()
.deletionProtection(false)
.datasetId(sampleDataset.datasetId())
.tableId("example_feature_view_feature_registry")
.schema("""
[
{
\"name\": \"feature_id\",
\"type\": \"STRING\",
\"mode\": \"NULLABLE\"
},
{
\"name\": \"example_feature_view_feature_registry\",
\"type\": \"STRING\",
\"mode\": \"NULLABLE\"
},
{
\"name\": \"feature_timestamp\",
\"type\": \"TIMESTAMP\",
\"mode\": \"NULLABLE\"
}
]
""")
.build());
var sampleFeatureGroup = new AiFeatureGroup("sampleFeatureGroup", AiFeatureGroupArgs.builder()
.name("example_feature_view_feature_registry")
.description("A sample feature group")
.region("us-central1")
.labels(Map.of("label-one", "value-one"))
.bigQuery(AiFeatureGroupBigQueryArgs.builder()
.bigQuerySource(AiFeatureGroupBigQueryBigQuerySourceArgs.builder()
.inputUri(Output.tuple(sampleTable.project(), sampleTable.datasetId(), sampleTable.tableId()).applyValue(values -> {
var project = values.t1;
var datasetId = values.t2;
var tableId = values.t3;
return String.format("bq://%s.%s.%s", project,datasetId,tableId);
}))
.build())
.entityIdColumns("feature_id")
.build())
.build());
var sampleFeature = new AiFeatureGroupFeature("sampleFeature", AiFeatureGroupFeatureArgs.builder()
.name("example_feature_view_feature_registry")
.region("us-central1")
.featureGroup(sampleFeatureGroup.name())
.description("A sample feature")
.labels(Map.of("label-one", "value-one"))
.build());
var featureviewFeatureregistry = new AiFeatureOnlineStoreFeatureview("featureviewFeatureregistry", AiFeatureOnlineStoreFeatureviewArgs.builder()
.name("example_feature_view_feature_registry")
.region("us-central1")
.featureOnlineStore(featureonlinestore.name())
.syncConfig(AiFeatureOnlineStoreFeatureviewSyncConfigArgs.builder()
.cron("0 0 * * *")
.build())
.featureRegistrySource(AiFeatureOnlineStoreFeatureviewFeatureRegistrySourceArgs.builder()
.featureGroups(AiFeatureOnlineStoreFeatureviewFeatureRegistrySourceFeatureGroupArgs.builder()
.featureGroupId(sampleFeatureGroup.name())
.featureIds(sampleFeature.name())
.build())
.build())
.build());
}
}
resources:
featureonlinestore:
type: gcp:vertex:AiFeatureOnlineStore
properties:
name: example_feature_view_feature_registry
labels:
foo: bar
region: us-central1
bigtable:
autoScaling:
minNodeCount: 1
maxNodeCount: 2
cpuUtilizationTarget: 80
sampleDataset:
type: gcp:bigquery:Dataset
name: sample_dataset
properties:
datasetId: example_feature_view_feature_registry
friendlyName: test
description: This is a test description
location: US
sampleTable:
type: gcp:bigquery:Table
name: sample_table
properties:
deletionProtection: false
datasetId: ${sampleDataset.datasetId}
tableId: example_feature_view_feature_registry
schema: |
[
{
\"name\": \"feature_id\",
\"type\": \"STRING\",
\"mode\": \"NULLABLE\"
},
{
\"name\": \"example_feature_view_feature_registry\",
\"type\": \"STRING\",
\"mode\": \"NULLABLE\"
},
{
\"name\": \"feature_timestamp\",
\"type\": \"TIMESTAMP\",
\"mode\": \"NULLABLE\"
}
]
sampleFeatureGroup:
type: gcp:vertex:AiFeatureGroup
name: sample_feature_group
properties:
name: example_feature_view_feature_registry
description: A sample feature group
region: us-central1
labels:
label-one: value-one
bigQuery:
bigQuerySource:
inputUri: bq://${sampleTable.project}.${sampleTable.datasetId}.${sampleTable.tableId}
entityIdColumns:
- feature_id
sampleFeature:
type: gcp:vertex:AiFeatureGroupFeature
name: sample_feature
properties:
name: example_feature_view_feature_registry
region: us-central1
featureGroup: ${sampleFeatureGroup.name}
description: A sample feature
labels:
label-one: value-one
featureviewFeatureregistry:
type: gcp:vertex:AiFeatureOnlineStoreFeatureview
name: featureview_featureregistry
properties:
name: example_feature_view_feature_registry
region: us-central1
featureOnlineStore: ${featureonlinestore.name}
syncConfig:
cron: 0 0 * * *
featureRegistrySource:
featureGroups:
- featureGroupId: ${sampleFeatureGroup.name}
featureIds:
- ${sampleFeature.name}
The featureRegistrySource property replaces bigQuerySource when pulling from Feature Registry. The featureGroups array specifies which Feature Groups and Features to sync. This approach lets you curate features once in the registry and reference them across multiple FeatureViews, rather than duplicating BigQuery source configuration.
Enable vector similarity search for embeddings
Recommendation and search systems store embeddings in BigQuery and need to perform approximate nearest neighbor lookups at serving time. FeatureViews can index embeddings for vector search.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const featureonlinestore = new gcp.vertex.AiFeatureOnlineStore("featureonlinestore", {
name: "example_feature_view_vector_search",
labels: {
foo: "bar",
},
region: "us-central1",
optimized: {},
embeddingManagement: {
enabled: true,
},
});
const tf_test_dataset = new gcp.bigquery.Dataset("tf-test-dataset", {
datasetId: "example_feature_view_vector_search",
friendlyName: "test",
description: "This is a test description",
location: "US",
});
const tf_test_table = new gcp.bigquery.Table("tf-test-table", {
deletionProtection: false,
datasetId: tf_test_dataset.datasetId,
tableId: "example_feature_view_vector_search",
schema: `[
{
\\"name\\": \\"test_primary_id\\",
\\"mode\\": \\"NULLABLE\\",
\\"type\\": \\"STRING\\",
\\"description\\": \\"primary test id\\"
},
{
\\"name\\": \\"embedding\\",
\\"mode\\": \\"REPEATED\\",
\\"type\\": \\"FLOAT\\",
\\"description\\": \\"embedding column for primary_id column\\"
},
{
\\"name\\": \\"country\\",
\\"mode\\": \\"NULLABLE\\",
\\"type\\": \\"STRING\\",
\\"description\\": \\"country\\"
},
{
\\"name\\": \\"test_crowding_column\\",
\\"mode\\": \\"NULLABLE\\",
\\"type\\": \\"INTEGER\\",
\\"description\\": \\"test crowding column\\"
},
{
\\"name\\": \\"entity_id\\",
\\"mode\\": \\"NULLABLE\\",
\\"type\\": \\"STRING\\",
\\"description\\": \\"Test default entity_id\\"
},
{
\\"name\\": \\"test_entity_column\\",
\\"mode\\": \\"NULLABLE\\",
\\"type\\": \\"STRING\\",
\\"description\\": \\"test secondary entity column\\"
},
{
\\"name\\": \\"feature_timestamp\\",
\\"mode\\": \\"NULLABLE\\",
\\"type\\": \\"TIMESTAMP\\",
\\"description\\": \\"Default timestamp value\\"
}
]
`,
});
const featureviewVectorSearch = new gcp.vertex.AiFeatureOnlineStoreFeatureview("featureview_vector_search", {
name: "example_feature_view_vector_search",
region: "us-central1",
featureOnlineStore: featureonlinestore.name,
syncConfig: {
cron: "0 0 * * *",
},
bigQuerySource: {
uri: pulumi.interpolate`bq://${tf_test_table.project}.${tf_test_table.datasetId}.${tf_test_table.tableId}`,
entityIdColumns: ["test_entity_column"],
},
vectorSearchConfig: {
embeddingColumn: "embedding",
filterColumns: ["country"],
crowdingColumn: "test_crowding_column",
distanceMeasureType: "DOT_PRODUCT_DISTANCE",
treeAhConfig: {
leafNodeEmbeddingCount: "1000",
},
embeddingDimension: 2,
},
});
const project = gcp.organizations.getProject({});
import pulumi
import pulumi_gcp as gcp
featureonlinestore = gcp.vertex.AiFeatureOnlineStore("featureonlinestore",
name="example_feature_view_vector_search",
labels={
"foo": "bar",
},
region="us-central1",
optimized={},
embedding_management={
"enabled": True,
})
tf_test_dataset = gcp.bigquery.Dataset("tf-test-dataset",
dataset_id="example_feature_view_vector_search",
friendly_name="test",
description="This is a test description",
location="US")
tf_test_table = gcp.bigquery.Table("tf-test-table",
deletion_protection=False,
dataset_id=tf_test_dataset.dataset_id,
table_id="example_feature_view_vector_search",
schema="""[
{
\"name\": \"test_primary_id\",
\"mode\": \"NULLABLE\",
\"type\": \"STRING\",
\"description\": \"primary test id\"
},
{
\"name\": \"embedding\",
\"mode\": \"REPEATED\",
\"type\": \"FLOAT\",
\"description\": \"embedding column for primary_id column\"
},
{
\"name\": \"country\",
\"mode\": \"NULLABLE\",
\"type\": \"STRING\",
\"description\": \"country\"
},
{
\"name\": \"test_crowding_column\",
\"mode\": \"NULLABLE\",
\"type\": \"INTEGER\",
\"description\": \"test crowding column\"
},
{
\"name\": \"entity_id\",
\"mode\": \"NULLABLE\",
\"type\": \"STRING\",
\"description\": \"Test default entity_id\"
},
{
\"name\": \"test_entity_column\",
\"mode\": \"NULLABLE\",
\"type\": \"STRING\",
\"description\": \"test secondary entity column\"
},
{
\"name\": \"feature_timestamp\",
\"mode\": \"NULLABLE\",
\"type\": \"TIMESTAMP\",
\"description\": \"Default timestamp value\"
}
]
""")
featureview_vector_search = gcp.vertex.AiFeatureOnlineStoreFeatureview("featureview_vector_search",
name="example_feature_view_vector_search",
region="us-central1",
feature_online_store=featureonlinestore.name,
sync_config={
"cron": "0 0 * * *",
},
big_query_source={
"uri": pulumi.Output.all(
project=tf_test_table.project,
dataset_id=tf_test_table.dataset_id,
table_id=tf_test_table.table_id
).apply(lambda resolved_outputs: f"bq://{resolved_outputs['project']}.{resolved_outputs['dataset_id']}.{resolved_outputs['table_id']}")
,
"entity_id_columns": ["test_entity_column"],
},
vector_search_config={
"embedding_column": "embedding",
"filter_columns": ["country"],
"crowding_column": "test_crowding_column",
"distance_measure_type": "DOT_PRODUCT_DISTANCE",
"tree_ah_config": {
"leaf_node_embedding_count": "1000",
},
"embedding_dimension": 2,
})
project = gcp.organizations.get_project()
package main
import (
"fmt"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/bigquery"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/vertex"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
featureonlinestore, err := vertex.NewAiFeatureOnlineStore(ctx, "featureonlinestore", &vertex.AiFeatureOnlineStoreArgs{
Name: pulumi.String("example_feature_view_vector_search"),
Labels: pulumi.StringMap{
"foo": pulumi.String("bar"),
},
Region: pulumi.String("us-central1"),
Optimized: &vertex.AiFeatureOnlineStoreOptimizedArgs{},
EmbeddingManagement: &vertex.AiFeatureOnlineStoreEmbeddingManagementArgs{
Enabled: pulumi.Bool(true),
},
})
if err != nil {
return err
}
tf_test_dataset, err := bigquery.NewDataset(ctx, "tf-test-dataset", &bigquery.DatasetArgs{
DatasetId: pulumi.String("example_feature_view_vector_search"),
FriendlyName: pulumi.String("test"),
Description: pulumi.String("This is a test description"),
Location: pulumi.String("US"),
})
if err != nil {
return err
}
tf_test_table, err := bigquery.NewTable(ctx, "tf-test-table", &bigquery.TableArgs{
DeletionProtection: pulumi.Bool(false),
DatasetId: tf_test_dataset.DatasetId,
TableId: pulumi.String("example_feature_view_vector_search"),
Schema: pulumi.String(`[
{
\"name\": \"test_primary_id\",
\"mode\": \"NULLABLE\",
\"type\": \"STRING\",
\"description\": \"primary test id\"
},
{
\"name\": \"embedding\",
\"mode\": \"REPEATED\",
\"type\": \"FLOAT\",
\"description\": \"embedding column for primary_id column\"
},
{
\"name\": \"country\",
\"mode\": \"NULLABLE\",
\"type\": \"STRING\",
\"description\": \"country\"
},
{
\"name\": \"test_crowding_column\",
\"mode\": \"NULLABLE\",
\"type\": \"INTEGER\",
\"description\": \"test crowding column\"
},
{
\"name\": \"entity_id\",
\"mode\": \"NULLABLE\",
\"type\": \"STRING\",
\"description\": \"Test default entity_id\"
},
{
\"name\": \"test_entity_column\",
\"mode\": \"NULLABLE\",
\"type\": \"STRING\",
\"description\": \"test secondary entity column\"
},
{
\"name\": \"feature_timestamp\",
\"mode\": \"NULLABLE\",
\"type\": \"TIMESTAMP\",
\"description\": \"Default timestamp value\"
}
]
`),
})
if err != nil {
return err
}
_, err = vertex.NewAiFeatureOnlineStoreFeatureview(ctx, "featureview_vector_search", &vertex.AiFeatureOnlineStoreFeatureviewArgs{
Name: pulumi.String("example_feature_view_vector_search"),
Region: pulumi.String("us-central1"),
FeatureOnlineStore: featureonlinestore.Name,
SyncConfig: &vertex.AiFeatureOnlineStoreFeatureviewSyncConfigArgs{
Cron: pulumi.String("0 0 * * *"),
},
BigQuerySource: &vertex.AiFeatureOnlineStoreFeatureviewBigQuerySourceArgs{
Uri: pulumi.All(tf_test_table.Project, tf_test_table.DatasetId, tf_test_table.TableId).ApplyT(func(_args []interface{}) (string, error) {
project := _args[0].(string)
datasetId := _args[1].(string)
tableId := _args[2].(string)
return fmt.Sprintf("bq://%v.%v.%v", project, datasetId, tableId), nil
}).(pulumi.StringOutput),
EntityIdColumns: pulumi.StringArray{
pulumi.String("test_entity_column"),
},
},
VectorSearchConfig: &vertex.AiFeatureOnlineStoreFeatureviewVectorSearchConfigArgs{
EmbeddingColumn: pulumi.String("embedding"),
FilterColumns: pulumi.StringArray{
pulumi.String("country"),
},
CrowdingColumn: pulumi.String("test_crowding_column"),
DistanceMeasureType: pulumi.String("DOT_PRODUCT_DISTANCE"),
TreeAhConfig: &vertex.AiFeatureOnlineStoreFeatureviewVectorSearchConfigTreeAhConfigArgs{
LeafNodeEmbeddingCount: pulumi.String("1000"),
},
EmbeddingDimension: pulumi.Int(2),
},
})
if err != nil {
return err
}
_, err = organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
return await Deployment.RunAsync(() =>
{
var featureonlinestore = new Gcp.Vertex.AiFeatureOnlineStore("featureonlinestore", new()
{
Name = "example_feature_view_vector_search",
Labels =
{
{ "foo", "bar" },
},
Region = "us-central1",
Optimized = null,
EmbeddingManagement = new Gcp.Vertex.Inputs.AiFeatureOnlineStoreEmbeddingManagementArgs
{
Enabled = true,
},
});
var tf_test_dataset = new Gcp.BigQuery.Dataset("tf-test-dataset", new()
{
DatasetId = "example_feature_view_vector_search",
FriendlyName = "test",
Description = "This is a test description",
Location = "US",
});
var tf_test_table = new Gcp.BigQuery.Table("tf-test-table", new()
{
DeletionProtection = false,
DatasetId = tf_test_dataset.DatasetId,
TableId = "example_feature_view_vector_search",
Schema = @"[
{
\""name\"": \""test_primary_id\"",
\""mode\"": \""NULLABLE\"",
\""type\"": \""STRING\"",
\""description\"": \""primary test id\""
},
{
\""name\"": \""embedding\"",
\""mode\"": \""REPEATED\"",
\""type\"": \""FLOAT\"",
\""description\"": \""embedding column for primary_id column\""
},
{
\""name\"": \""country\"",
\""mode\"": \""NULLABLE\"",
\""type\"": \""STRING\"",
\""description\"": \""country\""
},
{
\""name\"": \""test_crowding_column\"",
\""mode\"": \""NULLABLE\"",
\""type\"": \""INTEGER\"",
\""description\"": \""test crowding column\""
},
{
\""name\"": \""entity_id\"",
\""mode\"": \""NULLABLE\"",
\""type\"": \""STRING\"",
\""description\"": \""Test default entity_id\""
},
{
\""name\"": \""test_entity_column\"",
\""mode\"": \""NULLABLE\"",
\""type\"": \""STRING\"",
\""description\"": \""test secondary entity column\""
},
{
\""name\"": \""feature_timestamp\"",
\""mode\"": \""NULLABLE\"",
\""type\"": \""TIMESTAMP\"",
\""description\"": \""Default timestamp value\""
}
]
",
});
var featureviewVectorSearch = new Gcp.Vertex.AiFeatureOnlineStoreFeatureview("featureview_vector_search", new()
{
Name = "example_feature_view_vector_search",
Region = "us-central1",
FeatureOnlineStore = featureonlinestore.Name,
SyncConfig = new Gcp.Vertex.Inputs.AiFeatureOnlineStoreFeatureviewSyncConfigArgs
{
Cron = "0 0 * * *",
},
BigQuerySource = new Gcp.Vertex.Inputs.AiFeatureOnlineStoreFeatureviewBigQuerySourceArgs
{
Uri = Output.Tuple(tf_test_table.Project, tf_test_table.DatasetId, tf_test_table.TableId).Apply(values =>
{
var project = values.Item1;
var datasetId = values.Item2;
var tableId = values.Item3;
return $"bq://{project}.{datasetId}.{tableId}";
}),
EntityIdColumns = new[]
{
"test_entity_column",
},
},
VectorSearchConfig = new Gcp.Vertex.Inputs.AiFeatureOnlineStoreFeatureviewVectorSearchConfigArgs
{
EmbeddingColumn = "embedding",
FilterColumns = new[]
{
"country",
},
CrowdingColumn = "test_crowding_column",
DistanceMeasureType = "DOT_PRODUCT_DISTANCE",
TreeAhConfig = new Gcp.Vertex.Inputs.AiFeatureOnlineStoreFeatureviewVectorSearchConfigTreeAhConfigArgs
{
LeafNodeEmbeddingCount = "1000",
},
EmbeddingDimension = 2,
},
});
var project = Gcp.Organizations.GetProject.Invoke();
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.vertex.AiFeatureOnlineStore;
import com.pulumi.gcp.vertex.AiFeatureOnlineStoreArgs;
import com.pulumi.gcp.vertex.inputs.AiFeatureOnlineStoreOptimizedArgs;
import com.pulumi.gcp.vertex.inputs.AiFeatureOnlineStoreEmbeddingManagementArgs;
import com.pulumi.gcp.bigquery.Dataset;
import com.pulumi.gcp.bigquery.DatasetArgs;
import com.pulumi.gcp.bigquery.Table;
import com.pulumi.gcp.bigquery.TableArgs;
import com.pulumi.gcp.vertex.AiFeatureOnlineStoreFeatureview;
import com.pulumi.gcp.vertex.AiFeatureOnlineStoreFeatureviewArgs;
import com.pulumi.gcp.vertex.inputs.AiFeatureOnlineStoreFeatureviewSyncConfigArgs;
import com.pulumi.gcp.vertex.inputs.AiFeatureOnlineStoreFeatureviewBigQuerySourceArgs;
import com.pulumi.gcp.vertex.inputs.AiFeatureOnlineStoreFeatureviewVectorSearchConfigArgs;
import com.pulumi.gcp.vertex.inputs.AiFeatureOnlineStoreFeatureviewVectorSearchConfigTreeAhConfigArgs;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var featureonlinestore = new AiFeatureOnlineStore("featureonlinestore", AiFeatureOnlineStoreArgs.builder()
.name("example_feature_view_vector_search")
.labels(Map.of("foo", "bar"))
.region("us-central1")
.optimized(AiFeatureOnlineStoreOptimizedArgs.builder()
.build())
.embeddingManagement(AiFeatureOnlineStoreEmbeddingManagementArgs.builder()
.enabled(true)
.build())
.build());
var tf_test_dataset = new Dataset("tf-test-dataset", DatasetArgs.builder()
.datasetId("example_feature_view_vector_search")
.friendlyName("test")
.description("This is a test description")
.location("US")
.build());
var tf_test_table = new Table("tf-test-table", TableArgs.builder()
.deletionProtection(false)
.datasetId(tf_test_dataset.datasetId())
.tableId("example_feature_view_vector_search")
.schema("""
[
{
\"name\": \"test_primary_id\",
\"mode\": \"NULLABLE\",
\"type\": \"STRING\",
\"description\": \"primary test id\"
},
{
\"name\": \"embedding\",
\"mode\": \"REPEATED\",
\"type\": \"FLOAT\",
\"description\": \"embedding column for primary_id column\"
},
{
\"name\": \"country\",
\"mode\": \"NULLABLE\",
\"type\": \"STRING\",
\"description\": \"country\"
},
{
\"name\": \"test_crowding_column\",
\"mode\": \"NULLABLE\",
\"type\": \"INTEGER\",
\"description\": \"test crowding column\"
},
{
\"name\": \"entity_id\",
\"mode\": \"NULLABLE\",
\"type\": \"STRING\",
\"description\": \"Test default entity_id\"
},
{
\"name\": \"test_entity_column\",
\"mode\": \"NULLABLE\",
\"type\": \"STRING\",
\"description\": \"test secondary entity column\"
},
{
\"name\": \"feature_timestamp\",
\"mode\": \"NULLABLE\",
\"type\": \"TIMESTAMP\",
\"description\": \"Default timestamp value\"
}
]
""")
.build());
var featureviewVectorSearch = new AiFeatureOnlineStoreFeatureview("featureviewVectorSearch", AiFeatureOnlineStoreFeatureviewArgs.builder()
.name("example_feature_view_vector_search")
.region("us-central1")
.featureOnlineStore(featureonlinestore.name())
.syncConfig(AiFeatureOnlineStoreFeatureviewSyncConfigArgs.builder()
.cron("0 0 * * *")
.build())
.bigQuerySource(AiFeatureOnlineStoreFeatureviewBigQuerySourceArgs.builder()
.uri(Output.tuple(tf_test_table.project(), tf_test_table.datasetId(), tf_test_table.tableId()).applyValue(values -> {
var project = values.t1;
var datasetId = values.t2;
var tableId = values.t3;
return String.format("bq://%s.%s.%s", project,datasetId,tableId);
}))
.entityIdColumns("test_entity_column")
.build())
.vectorSearchConfig(AiFeatureOnlineStoreFeatureviewVectorSearchConfigArgs.builder()
.embeddingColumn("embedding")
.filterColumns("country")
.crowdingColumn("test_crowding_column")
.distanceMeasureType("DOT_PRODUCT_DISTANCE")
.treeAhConfig(AiFeatureOnlineStoreFeatureviewVectorSearchConfigTreeAhConfigArgs.builder()
.leafNodeEmbeddingCount("1000")
.build())
.embeddingDimension(2)
.build())
.build());
final var project = OrganizationsFunctions.getProject(GetProjectArgs.builder()
.build());
}
}
resources:
featureonlinestore:
type: gcp:vertex:AiFeatureOnlineStore
properties:
name: example_feature_view_vector_search
labels:
foo: bar
region: us-central1
optimized: {}
embeddingManagement:
enabled: true
tf-test-dataset:
type: gcp:bigquery:Dataset
properties:
datasetId: example_feature_view_vector_search
friendlyName: test
description: This is a test description
location: US
tf-test-table:
type: gcp:bigquery:Table
properties:
deletionProtection: false
datasetId: ${["tf-test-dataset"].datasetId}
tableId: example_feature_view_vector_search
schema: |
[
{
\"name\": \"test_primary_id\",
\"mode\": \"NULLABLE\",
\"type\": \"STRING\",
\"description\": \"primary test id\"
},
{
\"name\": \"embedding\",
\"mode\": \"REPEATED\",
\"type\": \"FLOAT\",
\"description\": \"embedding column for primary_id column\"
},
{
\"name\": \"country\",
\"mode\": \"NULLABLE\",
\"type\": \"STRING\",
\"description\": \"country\"
},
{
\"name\": \"test_crowding_column\",
\"mode\": \"NULLABLE\",
\"type\": \"INTEGER\",
\"description\": \"test crowding column\"
},
{
\"name\": \"entity_id\",
\"mode\": \"NULLABLE\",
\"type\": \"STRING\",
\"description\": \"Test default entity_id\"
},
{
\"name\": \"test_entity_column\",
\"mode\": \"NULLABLE\",
\"type\": \"STRING\",
\"description\": \"test secondary entity column\"
},
{
\"name\": \"feature_timestamp\",
\"mode\": \"NULLABLE\",
\"type\": \"TIMESTAMP\",
\"description\": \"Default timestamp value\"
}
]
featureviewVectorSearch:
type: gcp:vertex:AiFeatureOnlineStoreFeatureview
name: featureview_vector_search
properties:
name: example_feature_view_vector_search
region: us-central1
featureOnlineStore: ${featureonlinestore.name}
syncConfig:
cron: 0 0 * * *
bigQuerySource:
uri: bq://${["tf-test-table"].project}.${["tf-test-table"].datasetId}.${["tf-test-table"].tableId}
entityIdColumns:
- test_entity_column
vectorSearchConfig:
embeddingColumn: embedding
filterColumns:
- country
crowdingColumn: test_crowding_column
distanceMeasureType: DOT_PRODUCT_DISTANCE
treeAhConfig:
leafNodeEmbeddingCount: '1000'
embeddingDimension: '2'
variables:
project:
fn::invoke:
function: gcp:organizations:getProject
arguments: {}
The vectorSearchConfig property enables ANN search on your embeddings. The embeddingColumn property specifies which BigQuery column contains the embedding vectors (must be REPEATED FLOAT type). The distanceMeasureType property controls similarity calculation (DOT_PRODUCT_DISTANCE, COSINE_DISTANCE, or EUCLIDEAN_DISTANCE). The treeAhConfig property tunes the index structure; leafNodeEmbeddingCount controls how many embeddings are stored per leaf node, affecting search speed and accuracy. Optional filterColumns and crowdingColumn properties enable filtered search and diversity controls.
Beyond these examples
These snippets focus on specific FeatureView-level features: BigQuery and Feature Registry sync sources, scheduled and continuous sync modes, and vector search indexing for embeddings. They’re intentionally minimal rather than full ML serving pipelines.
The examples reference pre-existing infrastructure such as FeatureOnlineStore instances (Bigtable or Optimized), BigQuery datasets and tables with feature data, and Feature Groups and Features (for Feature Registry source). They focus on configuring the FeatureView rather than provisioning the underlying storage or feature definitions.
To keep things focused, common FeatureView patterns are omitted, including:
- Cross-project feature access and IAM configuration
- Continuous sync mode (syncConfig.continuous)
- Vector search tuning (embedding dimension, leaf node count)
- Filter and crowding column configuration for search
These omissions are intentional: the goal is to illustrate how each FeatureView feature is wired, not provide drop-in ML serving modules. See the FeatureOnlineStoreFeatureview resource reference for all available configuration options.
Let's configure GCP Vertex AI Feature Online Store Feature Views
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Configuration Basics
featureOnlineStore, name, region, project, and vectorSearchConfig properties are immutable and require resource recreation if changed.region property should be the same as the parent featureonlinestore region.name can be up to 60 characters using [a-z0-9_], and the first character cannot be a number.Data Sources
bigQuerySource to extract data directly from BigQuery tables, or featureRegistrySource to load features from Vertex AI Feature Registry. These options are mutually exclusive.bigQuerySource with uri in bq://project.dataset.table format and specify entityIdColumns as an array of column names.featureRegistrySource with a featureGroups array containing featureGroupId and featureIds for each feature group.Sync Configuration
syncConfig.cron with a cron expression (e.g., 0 0 * * * for daily) for scheduled syncs, or set syncConfig.continuous to true for continuous data synchronization.Advanced Features
vectorSearchConfig with embeddingColumn, embeddingDimension, and distanceMeasureType. The parent feature store must have embeddingManagement.enabled set to true.roles/bigquery.dataViewer to the Vertex AI service account (service-PROJECT_NUMBER@gcp-sa-aiplatform.iam.gserviceaccount.com), set projectNumber in featureRegistrySource, and add wait times (30-60 seconds) for IAM propagation using dependsOn.Troubleshooting
labels field is non-authoritative and only manages labels in your configuration. Use the effectiveLabels output to see all labels present on the resource.Using a different cloud?
Explore analytics guides for other cloud providers: