blob: 93f7a052aad7d9f9da1bff9f2f39c20551e1a548 [file] [view]
---
sidebar_position: 7
---
# Prefix Lookup
Prefix lookup returns all rows whose primary key starts with a given prefix. It is enabled by choosing a **bucket key that is a strict prefix of the primary key** rows sharing the same bucket-key prefix land in the same bucket, so one bucket lookup returns them all.
## Table Requirements
- The table must have a primary key.
- The bucket key must be a strict prefix of the primary key (on partitioned tables, of the *non-partition* portion of the primary key).
- The bucket key cannot equal the full primary key that is a normal primary-key lookup; use [`Lookuper`](./primary-key-tables.md#looking-up-records) instead.
- The columns passed to `NewPrefixLookup()` must equal `partition_keys ++ bucket_key` (in that order, if partitioned).
`NewPrefixLookup()` validates these rules and returns a non-`Ok` `Result` (with `error_code == ErrorCode::CLIENT_ERROR`) describing the violation.
## Non-Partitioned Table
Pick a schema where the bucket key is a prefix of the primary key:
```cpp
auto schema = fluss::Schema::NewBuilder()
.AddColumn("user_id", fluss::DataType::Int())
.AddColumn("session_id", fluss::DataType::String())
.AddColumn("event_seq", fluss::DataType::BigInt())
.AddColumn("event_data", fluss::DataType::String())
.SetPrimaryKeys({"user_id", "session_id", "event_seq"})
.Build();
// Bucket key (user_id, session_id) is a prefix of the primary key.
auto descriptor = fluss::TableDescriptor::NewBuilder()
.SetSchema(schema)
.SetBucketKeys({"user_id", "session_id"})
.SetBucketCount(3)
.Build();
fluss::TablePath table_path("fluss", "sessions");
admin.CreateTable(table_path, descriptor, true);
```
Create the lookuper with the prefix columns, then call `PrefixLookup`:
```cpp
fluss::Table table;
conn.GetTable(table_path, table);
fluss::PrefixLookuper prefix_lookuper;
table.NewPrefixLookup({"user_id", "session_id"}, prefix_lookuper);
auto prefix = table.NewRow();
prefix.Set("user_id", 1);
prefix.Set("session_id", "sess-a");
fluss::PrefixLookupResult result;
prefix_lookuper.PrefixLookup(prefix, result);
for (size_t i = 0; i < result.Size(); ++i) {
auto row = result.GetRow(i);
std::cout << "seq=" << row.GetInt64("event_seq")
<< ", data=" << row.GetString("event_data") << std::endl;
}
```
Unlike primary-key lookup (which returns a single row via `LookupResult::Found()`), prefix lookup returns zero or more rows via `Size()` / `GetRow(i)`, in primary-key order.
Each `GetRow(i)` is a `PrefixRowView` with the same getters as a scan `RowView`: scalars by index or name, and complex (`ARRAY` / `MAP` / `ROW`) columns via `GetValue(...)` see [Reading Complex Columns](../data-types.md#reading-complex-columns-array--map--row).
## Partitioned Table
On a partitioned table, the partition columns are stripped from the primary key before the bucket-prefix rule is evaluated. The lookup key must still carry the partition values so the client can route the request to the right partition so the columns passed to `NewPrefixLookup()` are `partition_keys ++ bucket_key`.
```cpp
auto schema = fluss::Schema::NewBuilder()
.AddColumn("region", fluss::DataType::String())
.AddColumn("user_id", fluss::DataType::Int())
.AddColumn("session_id", fluss::DataType::String())
.AddColumn("event_seq", fluss::DataType::BigInt())
.AddColumn("event_data", fluss::DataType::String())
.SetPrimaryKeys({"region", "user_id", "session_id", "event_seq"})
.Build();
auto descriptor = fluss::TableDescriptor::NewBuilder()
.SetSchema(schema)
.SetPartitionKeys({"region"})
// Bucket key (user_id, session_id) is a prefix of the PK minus the partition column.
.SetBucketKeys({"user_id", "session_id"})
.SetBucketCount(3)
.Build();
```
```cpp
fluss::PrefixLookuper prefix_lookuper;
// lookup columns = partition keys ++ bucket key
table.NewPrefixLookup({"region", "user_id", "session_id"}, prefix_lookuper);
auto prefix = table.NewRow();
prefix.Set("region", "US"); // partition column
prefix.Set("user_id", 1);
prefix.Set("session_id", "sess-a");
fluss::PrefixLookupResult result;
prefix_lookuper.PrefixLookup(prefix, result);
for (size_t i = 0; i < result.Size(); ++i) {
auto row = result.GetRow(i);
std::cout << "seq=" << row.GetInt64("event_seq")
<< ", data=" << row.GetString("event_data") << std::endl;
}
```