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 integration, and vector search 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 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. The syncConfig.cron property controls when data syncs run; here, features refresh daily at midnight. At each sync, the FeatureView loads the latest feature values for each entity into the FeatureOnlineStore.
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 Group and which features within it to sync. This approach centralizes feature definitions: multiple FeatureViews can reference the same Feature Group, and changes to the Feature Group propagate to all consumers.
Enable vector similarity search for embeddings
Recommendation and search systems store embeddings in BigQuery and need to perform approximate nearest neighbor searches at serving time. FeatureViews can index embedding columns for vector similarity queries.
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 the specified embeddingColumn. The distanceMeasureType determines how similarity is calculated (DOT_PRODUCT_DISTANCE, COSINE, or EUCLIDEAN). The filterColumns property allows you to restrict searches by metadata like country or category. The treeAhConfig.leafNodeEmbeddingCount tunes index structure for your dataset size. This configuration requires a FeatureOnlineStore with optimized storage and embeddingManagement enabled.
Beyond these examples
These snippets focus on specific FeatureView 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, BigQuery datasets and tables with required schema, and Feature Groups and Features for Feature Registry sources. They focus on configuring the FeatureView rather than provisioning the surrounding infrastructure.
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 (tree-AH parameters, crowding columns)
- Entity ID mapping and timestamp handling
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 & Immutability
featureOnlineStore, name, project, region, and vectorSearchConfig. You’ll need to recreate the resource to change any of these.region should be the same as the parent FeatureOnlineStore region.labels field is non-authoritative and only manages labels in your configuration. Use the effectiveLabels output to see all labels present on the resource, including those added by other clients or services.Data Sources
bigQuerySource to extract data directly from a BigQuery table by specifying uri and entityIdColumns. Use featureRegistrySource to reference features already registered in Vertex AI Feature Store by specifying featureGroups with featureGroupId and featureIds.roles/bigquery.dataViewer to the Vertex AI service account service-{project_number}@gcp-sa-aiplatform.iam.gserviceaccount.com on the BigQuery dataset. Set projectNumber in featureRegistrySource to the source project number. Allow 30 seconds for IAM propagation before creating the FeatureView.Sync Configuration
syncConfig, use continuous: true for continuous data sync or cron: '0 0 * * *' for scheduled sync using cron format.Vector Search
embeddingManagement.enabled: true on the parent FeatureOnlineStore. Then configure vectorSearchConfig on the FeatureView with embeddingColumn, embeddingDimension, and optionally filterColumns, crowdingColumn, and distanceMeasureType. Note that vectorSearchConfig is immutable and currently in Beta.DOT_PRODUCT_DISTANCE as the distanceMeasureType in vectorSearchConfig.Import & Resource Management
projects/{project}/locations/{region}/featureOnlineStores/{feature_online_store}/featureViews/{name}, {project}/{region}/{feature_online_store}/{name}, {region}/{feature_online_store}/{name}, or {feature_online_store}/{name}.Using a different cloud?
Explore analytics guides for other cloud providers: