|
| 1 | +--- |
| 2 | +title: "使用 Index 快速查询配置数据" |
| 3 | +description: "介绍 Tableau 的 Index 功能,实现对配置表的高效索引查询。" |
| 4 | +lead: "介绍 Tableau 的 Index 功能,实现对配置表的高效索引查询。" |
| 5 | +date: 2026-03-30T15:00:00+08:00 |
| 6 | +lastmod: 2026-03-30T15:00:00+08:00 |
| 7 | +draft: false |
| 8 | +contributors: ["Wenchy"] |
| 9 | +--- |
| 10 | + |
| 11 | +## 背景 |
| 12 | + |
| 13 | +在游戏或业务配置中,我们经常需要根据**非主键字段**快速查找数据。例如: |
| 14 | + |
| 15 | +- 查找所有 `Type == 1` 的商店 |
| 16 | +- 查找某个道具下所有 `PropID == 2` 的属性 |
| 17 | + |
| 18 | +如果每次都遍历整张表,性能开销较大。Tableau 提供了 **Index** metasheet 选项,让 loader 插件在加载时自动构建索引,实现 O(1) 的快速查询。 |
| 19 | + |
| 20 | +--- |
| 21 | + |
| 22 | +## Index |
| 23 | + |
| 24 | +### 配置方式 |
| 25 | + |
| 26 | +在 `@TABLEAU` metasheet 中,通过 `Index` 列为指定 sheet 设置索引列。 |
| 27 | + |
| 28 | +**格式:** |
| 29 | + |
| 30 | +- 单列 Index:`Column@IndexName` |
| 31 | +- 多列 Index(Composite Index):`(Column1,Column2)@IndexName` |
| 32 | +- 多个 Index 用逗号分隔:`Type@ThemeShop, (ID,Type)@SpecialShop` |
| 33 | + |
| 34 | +> **注意**:被索引的列必须是同一 struct(message)内的 scalar 或 enum 字段。 |
| 35 | +
|
| 36 | +### 示例 |
| 37 | + |
| 38 | +*HelloWorld.xlsx* 中的 `List` sheet(list 布局)以及 `@TABLEAU` metasheet 配置: |
| 39 | + |
| 40 | +{{< spreadsheet "HelloWorld.xlsx" List "@TABLEAU" >}} |
| 41 | + |
| 42 | +{{< sheet colored >}} |
| 43 | + |
| 44 | +| ID | Type | Desc | |
| 45 | +| ----------- | ----------- | ------------- | |
| 46 | +| [Shop]int32 | int32 | string | |
| 47 | +| Shop's ID | Shop's type | Shop's desc | |
| 48 | +| 1 | 1 | Shoes shop. | |
| 49 | +| 2 | 1 | T-Shirt shop. | |
| 50 | +| 3 | 2 | Fruite shop. | |
| 51 | + |
| 52 | +{{< /sheet >}} |
| 53 | + |
| 54 | +{{< sheet colored1 >}} |
| 55 | + |
| 56 | +| Sheet | Index | |
| 57 | +| ----- | ---------------------------------------------- | |
| 58 | +| List | ID@Shop, Type@ThemeShop, (ID,Type)@SpecialShop | |
| 59 | + |
| 60 | +{{< /sheet >}} |
| 61 | + |
| 62 | +{{< /spreadsheet >}} |
| 63 | + |
| 64 | +这样会生成三个索引: |
| 65 | + |
| 66 | +| Index 名 | 索引键 | 说明 | |
| 67 | +| ------------- | ----------- | ------------------- | |
| 68 | +| `Shop` | `ID` | 按 ID 查找商店 | |
| 69 | +| `ThemeShop` | `Type` | 按类型查找商店列表 | |
| 70 | +| `SpecialShop` | `(ID,Type)` | 按 ID+Type 组合查找 | |
| 71 | + |
| 72 | +### 生成的 API(Go 示例) |
| 73 | + |
| 74 | +```go |
| 75 | +// 获取完整索引 map |
| 76 | +indexMap := conf.FindThemeShopMap() |
| 77 | + |
| 78 | +// 按 Type 查找所有匹配的商店(返回 slice) |
| 79 | +shops := conf.FindThemeShop(1) |
| 80 | + |
| 81 | +// 按 Type 查找第一个匹配的商店 |
| 82 | +shop := conf.FindFirstThemeShop(1) |
| 83 | +``` |
| 84 | + |
| 85 | +对于嵌套在上层 map 中的 struct,还会生成带作用域的查询 API: |
| 86 | + |
| 87 | +```go |
| 88 | +// 在指定上层 map key 范围内查找 |
| 89 | +shops := conf.FindThemeShop1(itemID, shopType) |
| 90 | +``` |
| 91 | + |
| 92 | +--- |
| 93 | + |
| 94 | +## 复杂嵌套示例:map → list → map → list |
| 95 | + |
| 96 | +当索引目标深埋在多层嵌套结构中时,Tableau 会为每一层上级 map 自动生成**带作用域的查询 API**,方便在指定范围内精确查找。 |
| 97 | + |
| 98 | +### 数据结构 |
| 99 | + |
| 100 | +以 `ChapterConf` sheet 为例,结构为: |
| 101 | + |
| 102 | +```bash |
| 103 | +ChapterConf |
| 104 | +└── map<uint32, Chapter> # 第1层:章 map(key: ChapterID) |
| 105 | + └── [Section]uint32 # 第2层:节 list(key: SectionID) |
| 106 | + └── map<int32, Prop> # 第3层:属性 map(key: PropID) |
| 107 | + └── [Tag]int32 # 第4层:标签 list(key: TagID) |
| 108 | + ├── TagID |
| 109 | + └── Type ← 对此字段建立 Index |
| 110 | +``` |
| 111 | + |
| 112 | +### 配置示例 |
| 113 | + |
| 114 | +{{< spreadsheet "HelloWorld.xlsx" ChapterConf "@TABLEAU" >}} |
| 115 | + |
| 116 | +{{< sheet colored >}} |
| 117 | + |
| 118 | +| ChapterID | SectionID | PropID | TagID | Type | |
| 119 | +| -------------------- | --------------- | ---------------- | ---------- | ---------- | |
| 120 | +| map<uint32, Chapter> | [Section]uint32 | map<int32, Prop> | [Tag]int32 | int32 | |
| 121 | +| Chapter's ID | Section's ID | Prop's ID | Tag's ID | Tag's type | |
| 122 | +| 1 | 1 | 1 | 1 | 10 | |
| 123 | +| 1 | 1 | 1 | 2 | 20 | |
| 124 | +| 1 | 1 | 2 | 3 | 10 | |
| 125 | +| 1 | 2 | 1 | 4 | 30 | |
| 126 | +| 2 | 1 | 1 | 5 | 10 | |
| 127 | + |
| 128 | +{{< /sheet >}} |
| 129 | + |
| 130 | +{{< sheet colored1 >}} |
| 131 | + |
| 132 | +| Sheet | Index | |
| 133 | +| ----------- | -------------- | |
| 134 | +| ChapterConf | Type@TagByType | |
| 135 | + |
| 136 | +{{< /sheet >}} |
| 137 | + |
| 138 | +{{< /spreadsheet >}} |
| 139 | + |
| 140 | +`Type@TagByType` 表示:对 `Tag` struct 中的 `Type` 字段建立名为 `TagByType` 的索引。 |
| 141 | + |
| 142 | +### 生成的 API(Go 示例) |
| 143 | + |
| 144 | +由于 `Tag` 嵌套在 **2 层上级 map**(`Chapter` map 和 `Prop` map)中,Tableau 会生成全局查询 API 以及带作用域的 API: |
| 145 | + |
| 146 | +```go |
| 147 | +// 全局查询:跨所有章节和属性,按 Type 查找所有 Tag |
| 148 | +tags := conf.FindTagByType(10) |
| 149 | + |
| 150 | +// 按 Type 查找第一个 Tag(全局) |
| 151 | +tag := conf.FindFirstTagByType(10) |
| 152 | + |
| 153 | +// 带作用域查询(1 层上级 map:Prop map) |
| 154 | +// 在指定 ChapterID + SectionID + PropID 范围内,按 Type 查找 |
| 155 | +tags1 := conf.FindTagByType1(chapterID, sectionID, propID, 10) |
| 156 | + |
| 157 | +// 带作用域查询(2 层上级 map:Chapter map) |
| 158 | +// 在指定 ChapterID 范围内,按 Type 查找 |
| 159 | +tags2 := conf.FindTagByType2(chapterID, 10) |
| 160 | +``` |
| 161 | + |
| 162 | +> **说明**:`FindTagByTypeN` 中的 `N` 表示从最近的上级 map 往上数第 N 层。`N=1` 为直接父级 map(`Prop` map),`N=2` 为祖父级 map(`Chapter` map)。 |
| 163 | +
|
| 164 | +--- |
| 165 | + |
| 166 | +## 支持的列类型 |
| 167 | + |
| 168 | +Index 和 OrderedIndex 均支持以下列类型: |
| 169 | + |
| 170 | +- **scalar**:数值、布尔值、字符串、bytes |
| 171 | +- **enum**:如 `enum<.FruitType>` |
| 172 | +- **incell scalar list**:如 `[]int32` |
| 173 | +- **incell enum list**:如 `[]enum<.FruitType>` |
| 174 | + |
| 175 | +--- |
| 176 | + |
| 177 | +## 参考文档 |
| 178 | + |
| 179 | +- [Metasheet 选项:Index](../../docs/excel/metasheet/#选项-index) |
| 180 | + |
| 181 | +- [Go Loader API: OrderedIndex](../../docs/api/loader/go/#orderedindex) |
0 commit comments