Skip to content

Commit bff4b6f

Browse files
authored
Typed search attributes (#1782)
1 parent 0985041 commit bff4b6f

33 files changed

+1177
-52
lines changed

temporal-sdk/src/main/java/io/temporal/client/WorkflowExecutionMetadata.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import io.temporal.api.common.v1.WorkflowExecution;
2626
import io.temporal.api.enums.v1.WorkflowExecutionStatus;
2727
import io.temporal.api.workflow.v1.WorkflowExecutionInfo;
28+
import io.temporal.common.SearchAttributes;
2829
import io.temporal.common.converter.DataConverter;
2930
import io.temporal.internal.common.ProtobufTimeUtils;
3031
import io.temporal.internal.common.SearchAttributesUtil;
@@ -96,11 +97,21 @@ public WorkflowExecution getParentExecution() {
9697
return info.hasParentExecution() ? info.getParentExecution() : null;
9798
}
9899

100+
/**
101+
* @deprecated use {@link #getTypedSearchAttributes} instead.
102+
*/
103+
@Deprecated
99104
@Nonnull
100105
public Map<String, List<?>> getSearchAttributes() {
101106
return Collections.unmodifiableMap(SearchAttributesUtil.decode(info.getSearchAttributes()));
102107
}
103108

109+
/** Get search attributes as a typed set. */
110+
@Nonnull
111+
public SearchAttributes getTypedSearchAttributes() {
112+
return SearchAttributesUtil.decodeTyped(info.getSearchAttributes());
113+
}
114+
104115
@Nullable
105116
public <T> Object getMemo(String key, Class<T> valueClass) {
106117
return getMemo(key, valueClass, valueClass);

temporal-sdk/src/main/java/io/temporal/client/WorkflowOptions.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import io.temporal.common.CronSchedule;
2626
import io.temporal.common.MethodRetry;
2727
import io.temporal.common.RetryOptions;
28+
import io.temporal.common.SearchAttributes;
2829
import io.temporal.common.context.ContextPropagator;
2930
import io.temporal.internal.common.OptionsUtils;
3031
import io.temporal.worker.WorkerFactory;
@@ -72,6 +73,7 @@ public static WorkflowOptions merge(
7273
.setCronSchedule(OptionsUtils.merge(cronAnnotation, o.getCronSchedule(), String.class))
7374
.setMemo(o.getMemo())
7475
.setSearchAttributes(o.getSearchAttributes())
76+
.setTypedSearchAttributes(o.getTypedSearchAttributes())
7577
.setContextPropagators(o.getContextPropagators())
7678
.setDisableEagerExecution(o.isDisableEagerExecution())
7779
.validateBuildWithDefaults();
@@ -99,6 +101,8 @@ public static final class Builder {
99101

100102
private Map<String, ?> searchAttributes;
101103

104+
private SearchAttributes typedSearchAttributes;
105+
102106
private List<ContextPropagator> contextPropagators;
103107

104108
private boolean disableEagerExecution;
@@ -119,6 +123,7 @@ private Builder(WorkflowOptions options) {
119123
this.cronSchedule = options.cronSchedule;
120124
this.memo = options.memo;
121125
this.searchAttributes = options.searchAttributes;
126+
this.typedSearchAttributes = options.typedSearchAttributes;
122127
this.contextPropagators = options.contextPropagators;
123128
this.disableEagerExecution = options.disableEagerExecution;
124129
}
@@ -265,13 +270,40 @@ public Builder setMemo(Map<String, Object> memo) {
265270
* <li>OffsetDateTime
266271
* <li>{@link Collection} of the types above
267272
* </ul>
273+
*
274+
* @deprecated use {@link #setTypedSearchAttributes} instead.
268275
*/
269276
// Workflow#upsertSearchAttributes docs needs to be kept in sync with this method
277+
@Deprecated
270278
public Builder setSearchAttributes(Map<String, ?> searchAttributes) {
279+
if (searchAttributes != null
280+
&& !searchAttributes.isEmpty()
281+
&& this.typedSearchAttributes != null) {
282+
throw new IllegalArgumentException(
283+
"Cannot have search attributes and typed search attributes");
284+
}
271285
this.searchAttributes = searchAttributes;
272286
return this;
273287
}
274288

289+
/**
290+
* Specifies Search Attributes that will be attached to the Workflow. Search Attributes are
291+
* additional indexed information attributed to workflow and used for search and visibility.
292+
*
293+
* <p>The search attributes can be used in query of List/Scan/Count workflow APIs. The key and
294+
* its value type must be registered on Temporal server side.
295+
*/
296+
public Builder setTypedSearchAttributes(SearchAttributes typedSearchAttributes) {
297+
if (typedSearchAttributes != null
298+
&& searchAttributes != null
299+
&& !searchAttributes.isEmpty()) {
300+
throw new IllegalArgumentException(
301+
"Cannot have typed search attributes and search attributes");
302+
}
303+
this.typedSearchAttributes = typedSearchAttributes;
304+
return this;
305+
}
306+
275307
/**
276308
* This list of context propagators overrides the list specified on {@link
277309
* WorkflowClientOptions#getContextPropagators()}. <br>
@@ -319,6 +351,7 @@ public WorkflowOptions build() {
319351
cronSchedule,
320352
memo,
321353
searchAttributes,
354+
typedSearchAttributes,
322355
contextPropagators,
323356
disableEagerExecution);
324357
}
@@ -338,6 +371,7 @@ public WorkflowOptions validateBuildWithDefaults() {
338371
cronSchedule,
339372
memo,
340373
searchAttributes,
374+
typedSearchAttributes,
341375
contextPropagators,
342376
disableEagerExecution);
343377
}
@@ -363,6 +397,8 @@ public WorkflowOptions validateBuildWithDefaults() {
363397

364398
private final Map<String, ?> searchAttributes;
365399

400+
private final SearchAttributes typedSearchAttributes;
401+
366402
private final List<ContextPropagator> contextPropagators;
367403

368404
private final boolean disableEagerExecution;
@@ -378,6 +414,7 @@ private WorkflowOptions(
378414
String cronSchedule,
379415
Map<String, Object> memo,
380416
Map<String, ?> searchAttributes,
417+
SearchAttributes typedSearchAttributes,
381418
List<ContextPropagator> contextPropagators,
382419
boolean disableEagerExecution) {
383420
this.workflowId = workflowId;
@@ -390,6 +427,7 @@ private WorkflowOptions(
390427
this.cronSchedule = cronSchedule;
391428
this.memo = memo;
392429
this.searchAttributes = searchAttributes;
430+
this.typedSearchAttributes = typedSearchAttributes;
393431
this.contextPropagators = contextPropagators;
394432
this.disableEagerExecution = disableEagerExecution;
395433
}
@@ -430,10 +468,18 @@ public Map<String, Object> getMemo() {
430468
return memo;
431469
}
432470

471+
/**
472+
* @deprecated use {@link #getTypedSearchAttributes} instead.
473+
*/
474+
@Deprecated
433475
public Map<String, ?> getSearchAttributes() {
434476
return searchAttributes;
435477
}
436478

479+
public SearchAttributes getTypedSearchAttributes() {
480+
return typedSearchAttributes;
481+
}
482+
437483
/**
438484
* @return the list of context propagators to use during this workflow. This list overrides the
439485
* list specified on {@link WorkflowClientOptions#getContextPropagators()}, {@code null} means
@@ -466,6 +512,7 @@ public boolean equals(Object o) {
466512
&& Objects.equal(cronSchedule, that.cronSchedule)
467513
&& Objects.equal(memo, that.memo)
468514
&& Objects.equal(searchAttributes, that.searchAttributes)
515+
&& Objects.equal(typedSearchAttributes, that.typedSearchAttributes)
469516
&& Objects.equal(contextPropagators, that.contextPropagators)
470517
&& Objects.equal(disableEagerExecution, that.disableEagerExecution);
471518
}
@@ -483,6 +530,7 @@ public int hashCode() {
483530
cronSchedule,
484531
memo,
485532
searchAttributes,
533+
typedSearchAttributes,
486534
contextPropagators,
487535
disableEagerExecution);
488536
}
@@ -513,6 +561,8 @@ public String toString() {
513561
+ memo
514562
+ ", searchAttributes="
515563
+ searchAttributes
564+
+ ", typedSearchAttributes="
565+
+ typedSearchAttributes
516566
+ ", contextPropagators="
517567
+ contextPropagators
518568
+ ", disableEagerExecution="

temporal-sdk/src/main/java/io/temporal/common/SearchAttribute.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
import java.util.List;
2525
import java.util.Map;
2626

27+
/**
28+
* @deprecated typed search attributes should be used instead.
29+
*/
30+
@Deprecated
2731
public class SearchAttribute {
2832
/**
2933
* Passing this value as a search attribute value into {@link
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/*
2+
* Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
3+
*
4+
* Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5+
*
6+
* Modifications copyright (C) 2017 Uber Technologies, Inc.
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this material except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
21+
package io.temporal.common;
22+
23+
import com.google.common.reflect.TypeToken;
24+
import io.temporal.api.enums.v1.IndexedValueType;
25+
import java.lang.reflect.Type;
26+
import java.time.OffsetDateTime;
27+
import java.util.List;
28+
import java.util.Objects;
29+
import javax.annotation.Nonnull;
30+
31+
/** Representation of a typed search attribute key. */
32+
public class SearchAttributeKey<T> implements Comparable<SearchAttributeKey<T>> {
33+
private static final Type KEYWORD_LIST_REFLECT_TYPE = new TypeToken<List<String>>() {}.getType();
34+
35+
/** Create a search attribute key for a text attribute type. */
36+
public static SearchAttributeKey<String> forText(String name) {
37+
return new SearchAttributeKey<>(name, IndexedValueType.INDEXED_VALUE_TYPE_TEXT, String.class);
38+
}
39+
40+
/** Create a search attribute key for a keyword attribute type. */
41+
public static SearchAttributeKey<String> forKeyword(String name) {
42+
return new SearchAttributeKey<>(
43+
name, IndexedValueType.INDEXED_VALUE_TYPE_KEYWORD, String.class);
44+
}
45+
46+
/** Create a search attribute key for an int attribute type. */
47+
public static SearchAttributeKey<Long> forLong(String name) {
48+
return new SearchAttributeKey<>(name, IndexedValueType.INDEXED_VALUE_TYPE_INT, Long.class);
49+
}
50+
51+
/** Create a search attribute key for a double attribute type. */
52+
public static SearchAttributeKey<Double> forDouble(String name) {
53+
return new SearchAttributeKey<>(name, IndexedValueType.INDEXED_VALUE_TYPE_DOUBLE, Double.class);
54+
}
55+
56+
/** Create a search attribute key for a boolean attribute type. */
57+
public static SearchAttributeKey<Boolean> forBoolean(String name) {
58+
return new SearchAttributeKey<>(name, IndexedValueType.INDEXED_VALUE_TYPE_BOOL, Boolean.class);
59+
}
60+
61+
/** Create a search attribute key for a datetime attribute type. */
62+
public static SearchAttributeKey<OffsetDateTime> forOffsetDateTime(String name) {
63+
return new SearchAttributeKey<>(
64+
name, IndexedValueType.INDEXED_VALUE_TYPE_DATETIME, OffsetDateTime.class);
65+
}
66+
67+
/** Create a search attribute key for a keyword list attribute type. */
68+
public static SearchAttributeKey<List<String>> forKeywordList(String name) {
69+
return new SearchAttributeKey<>(
70+
name,
71+
IndexedValueType.INDEXED_VALUE_TYPE_KEYWORD_LIST,
72+
List.class,
73+
KEYWORD_LIST_REFLECT_TYPE);
74+
}
75+
76+
private final String name;
77+
private final IndexedValueType valueType;
78+
private final Class<? super T> valueClass;
79+
private final Type valueReflectType;
80+
81+
private SearchAttributeKey(String name, IndexedValueType valueType, Class<? super T> valueClass) {
82+
this(name, valueType, valueClass, valueClass);
83+
}
84+
85+
private SearchAttributeKey(
86+
String name, IndexedValueType valueType, Class<? super T> valueClass, Type valueReflectType) {
87+
this.name = name;
88+
this.valueType = valueType;
89+
this.valueClass = valueClass;
90+
this.valueReflectType = valueReflectType;
91+
}
92+
93+
/** Get the name of the search attribute. */
94+
public String getName() {
95+
return name;
96+
}
97+
98+
/** Get the search attribute value type. */
99+
public IndexedValueType getValueType() {
100+
return valueType;
101+
}
102+
103+
/** Get the class that the search attribute value will be. */
104+
public Class<? super T> getValueClass() {
105+
return valueClass;
106+
}
107+
108+
/**
109+
* Get the reflect type that the search attribute will be. For all key types except keyword list,
110+
* this is the same as {@link #getValueType}.
111+
*/
112+
public Type getValueReflectType() {
113+
return valueReflectType;
114+
}
115+
116+
/** Create an update that sets a value for this key. */
117+
public SearchAttributeUpdate<T> valueSet(@Nonnull T value) {
118+
return SearchAttributeUpdate.valueSet(this, value);
119+
}
120+
121+
/** Create an update that unsets a value for this key. */
122+
public SearchAttributeUpdate<T> valueUnset() {
123+
return SearchAttributeUpdate.valueUnset(this);
124+
}
125+
126+
@Override
127+
public boolean equals(Object o) {
128+
if (this == o) return true;
129+
if (o == null || getClass() != o.getClass()) return false;
130+
SearchAttributeKey<?> that = (SearchAttributeKey<?>) o;
131+
return name.equals(that.name)
132+
&& valueType == that.valueType
133+
&& valueClass.equals(that.valueClass);
134+
}
135+
136+
@Override
137+
public int hashCode() {
138+
return Objects.hash(name, valueType, valueClass);
139+
}
140+
141+
@Override
142+
public int compareTo(SearchAttributeKey<T> o) {
143+
int c = name.compareTo(o.name);
144+
if (c == 0) {
145+
c = valueType.compareTo(o.valueType);
146+
}
147+
if (c == 0) {
148+
c = valueClass.getName().compareTo(o.valueClass.getName());
149+
}
150+
return c;
151+
}
152+
}

0 commit comments

Comments
 (0)