Skip to main content

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

  1. Clone the examples
    git clone https://github.com/cludden/protoc-gen-go-temporal && cd protoc-gen-go-temporal
  2. Run a local Temporal server
    temporal server start-dev
  3. 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
  4. 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