Skip to content

Commit 718fd27

Browse files
committed
Add Java SDK for for Feast to get online features
1 parent e7de718 commit 718fd27

15 files changed

Lines changed: 1380 additions & 0 deletions

File tree

sdk/java/pom.xml

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<groupId>com.gojek.feast</groupId>
8+
<artifactId>feast-client</artifactId>
9+
<version>0.3-SNAPSHOT</version>
10+
11+
<properties>
12+
<grpc.version>1.24.0</grpc.version>
13+
<protobuf.version>3.10.0</protobuf.version>
14+
<junit.version>5.5.2</junit.version>
15+
</properties>
16+
17+
<dependencies>
18+
<!-- GRPC and Protobuf -->
19+
<dependency>
20+
<groupId>io.grpc</groupId>
21+
<artifactId>grpc-netty-shaded</artifactId>
22+
<version>${grpc.version}</version>
23+
</dependency>
24+
<dependency>
25+
<groupId>io.grpc</groupId>
26+
<artifactId>grpc-protobuf</artifactId>
27+
<version>${grpc.version}</version>
28+
</dependency>
29+
<dependency>
30+
<groupId>io.grpc</groupId>
31+
<artifactId>grpc-stub</artifactId>
32+
<version>${grpc.version}</version>
33+
</dependency>
34+
<dependency>
35+
<groupId>com.google.protobuf</groupId>
36+
<artifactId>protobuf-java-util</artifactId>
37+
<version>${protobuf.version}</version>
38+
</dependency>
39+
<dependency>
40+
<groupId>com.google.protobuf</groupId>
41+
<artifactId>protobuf-java</artifactId>
42+
<version>${protobuf.version}</version>
43+
</dependency>
44+
45+
<!-- Logging -->
46+
<dependency>
47+
<groupId>org.slf4j</groupId>
48+
<artifactId>slf4j-api</artifactId>
49+
<version>1.8.0-beta4</version>
50+
</dependency>
51+
52+
<!-- JUnit 5 -->
53+
<dependency>
54+
<groupId>org.junit.jupiter</groupId>
55+
<artifactId>junit-jupiter-engine</artifactId>
56+
<version>${junit.version}</version>
57+
<scope>test</scope>
58+
</dependency>
59+
<dependency>
60+
<groupId>org.junit.jupiter</groupId>
61+
<artifactId>junit-jupiter-params</artifactId>
62+
<version>${junit.version}</version>
63+
<scope>test</scope>
64+
</dependency>
65+
66+
</dependencies>
67+
68+
<build>
69+
<extensions>
70+
<extension>
71+
<groupId>kr.motd.maven</groupId>
72+
<artifactId>os-maven-plugin</artifactId>
73+
<version>1.6.2</version>
74+
</extension>
75+
</extensions>
76+
<plugins>
77+
<plugin>
78+
<groupId>org.xolstice.maven.plugins</groupId>
79+
<artifactId>protobuf-maven-plugin</artifactId>
80+
<version>0.6.1</version>
81+
<configuration>
82+
<protocArtifact>com.google.protobuf:protoc:3.10.0:exe:${os.detected.classifier}
83+
</protocArtifact>
84+
<pluginId>grpc-java</pluginId>
85+
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.24.0:exe:${os.detected.classifier}
86+
</pluginArtifact>
87+
</configuration>
88+
<executions>
89+
<execution>
90+
<goals>
91+
<goal>compile</goal>
92+
<goal>compile-custom</goal>
93+
</goals>
94+
</execution>
95+
</executions>
96+
</plugin>
97+
<plugin>
98+
<groupId>org.apache.maven.plugins</groupId>
99+
<artifactId>maven-compiler-plugin</artifactId>
100+
<version>3.8.1</version>
101+
<configuration>
102+
<source>8</source>
103+
<target>8</target>
104+
</configuration>
105+
</plugin>
106+
<!-- For Junit 5 support -->
107+
<plugin>
108+
<groupId>org.apache.maven.plugins</groupId>
109+
<artifactId>maven-surefire-plugin</artifactId>
110+
<version>2.22.0</version>
111+
</plugin>
112+
</plugins>
113+
</build>
114+
115+
</project>
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package com.gojek.feast.v1alpha1;
2+
3+
import feast.serving.ServingAPIProto.GetFeastServingInfoRequest;
4+
import feast.serving.ServingAPIProto.GetFeastServingInfoResponse;
5+
import feast.serving.ServingAPIProto.GetOnlineFeaturesRequest;
6+
import feast.serving.ServingAPIProto.GetOnlineFeaturesRequest.EntityRow;
7+
import feast.serving.ServingAPIProto.GetOnlineFeaturesRequest.FeatureSet;
8+
import feast.serving.ServingAPIProto.GetOnlineFeaturesResponse;
9+
import feast.serving.ServingServiceGrpc;
10+
import io.grpc.ManagedChannel;
11+
import io.grpc.ManagedChannelBuilder;
12+
import java.util.List;
13+
import java.util.concurrent.TimeUnit;
14+
import java.util.stream.Collectors;
15+
import org.slf4j.Logger;
16+
import org.slf4j.LoggerFactory;
17+
18+
@SuppressWarnings("WeakerAccess")
19+
public class FeastClient implements AutoCloseable {
20+
Logger logger = LoggerFactory.getLogger(FeastClient.class);
21+
22+
private static final int CHANNEL_SHUTDOWN_TIMEOUT_SEC = 5;
23+
24+
private final ManagedChannel channel;
25+
private final ServingServiceGrpc.ServingServiceBlockingStub stub;
26+
27+
/**
28+
* Create a client to access Feast
29+
*
30+
* @param host hostname or ip address of Feast serving GRPC server
31+
* @param port port number of Feast serving GRPC server
32+
* @return {@link FeastClient}
33+
*/
34+
public static FeastClient create(String host, int port) {
35+
ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();
36+
return new FeastClient(channel);
37+
}
38+
39+
public GetFeastServingInfoResponse getFeastServingInfo() {
40+
return stub.getFeastServingInfo(GetFeastServingInfoRequest.newBuilder().build());
41+
}
42+
43+
/**
44+
* Get online features from Feast.
45+
*
46+
* <p>See {@link #getOnlineFeatures(List, List, boolean)}
47+
*
48+
* @param featureIds list of feature id to retrieve, feature id follows this format
49+
* [feature_set_name]:[version]:[feature_name]
50+
* @param rows list of {@link Row} to select the entities to retrieve the features for
51+
* @return list of {@link Row} containing features
52+
*/
53+
public List<Row> getOnlineFeatures(List<String> featureIds, List<Row> rows) {
54+
return getOnlineFeatures(featureIds, rows, false);
55+
}
56+
57+
/**
58+
* Get online features from Feast.
59+
*
60+
* <p>Example of retrieving online features for driver feature set, version 1, with features
61+
* driver_id and driver_name
62+
*
63+
* <pre>{@code
64+
* FeastClient client = FeastClient.create("localhost", 6566);
65+
* List<String> requestedFeatureIds = Arrays.asList("driver:1:driver_id", "driver:1:driver_name");
66+
* List<Row> requestedRows =
67+
* Arrays.asList(Row.create().set("driver_id", 123), Row.create().set("driver_id", 456));
68+
* List<Row> retrievedFeatures = client.getOnlineFeatures(requestedFeatureIds, requestedRows);
69+
* retrievedFeatures.forEach(System.out::println);
70+
* }</pre>
71+
*
72+
* @param featureIds list of feature id to retrieve, feature id follows this format
73+
* [feature_set_name]:[version]:[feature_name]
74+
* @param rows list of {@link Row} to select the entities to retrieve the features for
75+
* @param omitEntitiesInResponse if true, the returned {@link Row} will not contain field and
76+
* value for the entity
77+
* @return list of {@link Row} containing features
78+
*/
79+
public List<Row> getOnlineFeatures(
80+
List<String> featureIds, List<Row> rows, boolean omitEntitiesInResponse) {
81+
List<FeatureSet> featureSets = RequestUtil.createFeatureSets(featureIds);
82+
List<EntityRow> entityRows =
83+
rows.stream()
84+
.map(
85+
row ->
86+
EntityRow.newBuilder()
87+
.setEntityTimestamp(row.getEntityTimestamp())
88+
.putAllFields(row.getFields())
89+
.build())
90+
.collect(Collectors.toList());
91+
92+
GetOnlineFeaturesResponse response =
93+
stub.getOnlineFeatures(
94+
GetOnlineFeaturesRequest.newBuilder()
95+
.addAllFeatureSets(featureSets)
96+
.addAllEntityRows(entityRows)
97+
.setOmitEntitiesInResponse(omitEntitiesInResponse)
98+
.build());
99+
100+
return response.getFieldValuesList().stream()
101+
.map(
102+
field -> {
103+
Row row = Row.create();
104+
field.getFieldsMap().forEach(row::set);
105+
return row;
106+
})
107+
.collect(Collectors.toList());
108+
}
109+
110+
private FeastClient(ManagedChannel channel) {
111+
this.channel = channel;
112+
stub = ServingServiceGrpc.newBlockingStub(channel);
113+
}
114+
115+
public void close() throws Exception {
116+
if (channel != null) {
117+
channel.shutdown().awaitTermination(CHANNEL_SHUTDOWN_TIMEOUT_SEC, TimeUnit.SECONDS);
118+
}
119+
}
120+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.gojek.feast.v1alpha1;
2+
3+
import feast.serving.ServingAPIProto.GetOnlineFeaturesRequest.FeatureSet;
4+
import java.util.ArrayList;
5+
import java.util.HashMap;
6+
import java.util.List;
7+
import java.util.Map;
8+
import java.util.stream.Collectors;
9+
import javafx.util.Pair;
10+
11+
@SuppressWarnings("WeakerAccess")
12+
public class RequestUtil {
13+
public static List<FeatureSet> createFeatureSets(List<String> featureIds) {
14+
if (featureIds == null) {
15+
throw new IllegalArgumentException("featureIds cannot be null");
16+
}
17+
18+
// featureSetMap is a map of pair of feature set name and version -> a list of feature names
19+
Map<Pair<String, Integer>, List<String>> featureSetMap = new HashMap<>();
20+
21+
for (String featureId : featureIds) {
22+
String[] parts = featureId.split(":");
23+
if (parts.length < 3) {
24+
throw new IllegalArgumentException(
25+
String.format(
26+
"Feature id '%s' has invalid format. Expected format: <feature_set_name>:<version>:<feature_name>.",
27+
featureId));
28+
}
29+
String featureSetName = parts[0];
30+
int featureSetVersion;
31+
try {
32+
featureSetVersion = Integer.parseInt(parts[1]);
33+
} catch (NumberFormatException e) {
34+
throw new IllegalArgumentException(
35+
String.format(
36+
"Feature id '%s' contains invalid version. Expected format: <feature_set_name>:<version>:<feature_name>.",
37+
parts[1]));
38+
}
39+
40+
Pair<String, Integer> key = new Pair<>(featureSetName, featureSetVersion);
41+
if (!featureSetMap.containsKey(key)) {
42+
featureSetMap.put(key, new ArrayList<>());
43+
}
44+
String featureName = parts[2];
45+
featureSetMap.get(key).add(featureName);
46+
}
47+
48+
return featureSetMap.entrySet().stream()
49+
.map(
50+
entry ->
51+
FeatureSet.newBuilder()
52+
.setName(entry.getKey().getKey())
53+
.setVersion(entry.getKey().getValue())
54+
.addAllFeatureNames(entry.getValue())
55+
.build())
56+
.collect(Collectors.toList());
57+
}
58+
}

0 commit comments

Comments
 (0)