Search Attributes
A simple example inspired by temporalio/samples-go/searchattributes
example.proto
syntax="proto3";
package example.searchattributes.v1;
import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
import "temporal/v1/temporal.proto";
service Example {
option (temporal.v1.service) = {
task_queue: "searchattributes"
};
rpc SearchAttributes(SearchAttributesInput) returns (google.protobuf.Empty) {
option (temporal.v1.workflow) = {
id: 'search_attributes_${! uuid_v4() }'
search_attributes:
'CustomKeywordField = customKeywordField \n'
'CustomTextField = customTextField \n'
'CustomIntField = customIntField.int64() \n'
'CustomDoubleField = customDoubleField \n'
'CustomBoolField = customBoolField \n'
'CustomDatetimeField = customDatetimeField.ts_parse("2006-01-02T15:04:05Z") \n'
};
}
}
message SearchAttributesInput {
string custom_keyword_field = 1;
string custom_text_field = 2;
int64 custom_int_field = 3;
double custom_double_field = 4;
bool custom_bool_field = 5;
google.protobuf.Timestamp custom_datetime_field = 6;
}
main.go
package main
import (
"log"
"os"
"strings"
"time"
searchattributesv1 "github.com/cludden/protoc-gen-go-temporal/gen/example/searchattributes/v1"
"github.com/urfave/cli/v2"
"go.temporal.io/sdk/client"
"go.temporal.io/sdk/converter"
tlog "go.temporal.io/sdk/log"
"go.temporal.io/sdk/worker"
"go.temporal.io/sdk/workflow"
)
type SearchAttributesWorkflow struct {
*searchattributesv1.SearchAttributesWorkflowInput
log tlog.Logger
}
func NewSearchAttributesWorkflow(ctx workflow.Context, input *searchattributesv1.SearchAttributesWorkflowInput) (searchattributesv1.SearchAttributesWorkflow, error) {
return &SearchAttributesWorkflow{input, workflow.GetLogger(ctx)}, nil
}
func (w *SearchAttributesWorkflow) Execute(ctx workflow.Context) (err error) {
sa := workflow.GetInfo(ctx).SearchAttributes
for _, attr := range strings.Split("CustomBoolField,CustomDatetimeField,CustomDoubleField,CustomIntField,CustomKeywordField,CustomTextField", ",") {
if p, ok := sa.IndexedFields[attr]; ok {
switch attr {
case "CustomBoolField":
var result bool
err = converter.GetDefaultDataConverter().FromPayload(p, &result)
w.log.Info("search attribute", "name", attr, "value", result, "error", err)
case "CustomDatetimeField":
var result time.Time
err = converter.GetDefaultDataConverter().FromPayload(p, &result)
w.log.Info("search attribute", "name", attr, "value", result, "error", err)
case "CustomDoubleField":
var result float64
err = converter.GetDefaultDataConverter().FromPayload(p, &result)
w.log.Info("search attribute", "name", attr, "value", result, "error", err)
case "CustomIntField":
var result int
err = converter.GetDefaultDataConverter().FromPayload(p, &result)
w.log.Info("search attribute", "name", attr, "value", result, "error", err)
case "CustomKeywordField":
var result string
err = converter.GetDefaultDataConverter().FromPayload(p, &result)
w.log.Info("search attribute", "name", attr, "value", result, "error", err)
case "CustomTextField":
var result string
err = converter.GetDefaultDataConverter().FromPayload(p, &result)
w.log.Info("search attribute", "name", attr, "value", result, "error", err)
}
}
}
return nil
}
func main() {
app, err := searchattributesv1.NewExampleCli(
searchattributesv1.NewExampleCliOptions().WithWorker(func(cmd *cli.Context, c client.Client) (worker.Worker, error) {
w := worker.New(c, searchattributesv1.ExampleTaskQueue, worker.Options{})
searchattributesv1.RegisterSearchAttributesWorkflow(w, NewSearchAttributesWorkflow)
return w, nil
}),
)
if err != nil {
log.Fatal(err)
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
Run this example
- Clone the examples
git clone https://github.com/cludden/protoc-gen-go-temporal && cd protoc-gen-go-temporal
- Run a local Temporal server
temporal server start-dev
- In a different shell, register custom search attributes and run the example worker
temporal operator search-attribute create --name CustomDatetimeField --type Datetime
temporal operator search-attribute create --name CustomKeywordField --type Keyword
temporal operator search-attribute create --name CustomTextField --type Text
temporal operator search-attribute create --name CustomIntField --type Int
temporal operator search-attribute create --name CustomDoubleField --type Double
temporal operator search-attribute create --name CustomBoolField --type Bool
go run examples/searchattributes/main.go worker - In a different shell, execute the workflow
go run examples/searchattributes/main.go search-attributes \
--custom-datetime-field=2024-01-01T00:00:00Z \
--custom-keyword-field=foo-bar \
--custom-text-field=foo-bar \
--custom-int-field=42 \
--custom-double-field=42 \
--custom-bool-field=true