文档结构 #
一、文档概述 #
1.1 什么是文档 #
text
文档特点:
├── 类似JSON对象
├── 包含数据和元数据
├── 自动生成唯一ID
├── 支持嵌套结构
├── 自动时间戳
└── 支持历史版本
1.2 文档结构 #
javascript
// 文档完整结构
{
ref: Ref(Collection("users"), "340293847938473857"), // 文档引用
ts: 1704067200000000, // 时间戳(微秒)
data: { // 数据字段
name: "Tom",
email: "tom@example.com",
age: 30
}
}
二、文档引用(Ref) #
2.1 Ref结构 #
javascript
// Ref组成
Ref(Collection("users"), "340293847938473857")
// ├── 集合引用
// └── 文档ID
// 获取Ref属性
Let(
{
ref: Ref(Collection("users"), "123456")
},
{
id: Var("ref").id, // "123456"
collection: Var("ref").collection // Collection("users")
}
)
2.2 创建Ref #
javascript
// 从字符串创建
Ref(Collection("users"), "340293847938473857")
// 从文档获取
Let(
{
doc: Create(Collection("users"), {
data: { name: "Tom" }
})
},
Select(["ref"], Var("doc"))
)
2.3 Ref操作 #
javascript
// 检查Ref是否存在
Exists(Ref(Collection("users"), "123456"))
// 获取Ref指向的文档
Get(Ref(Collection("users"), "123456"))
// 删除Ref指向的文档
Delete(Ref(Collection("users"), "123456"))
三、时间戳(ts) #
3.1 时间戳说明 #
text
时间戳特点:
├── 微秒精度
├── 自动生成
├── 每次更新都会变化
├── 用于时间旅行查询
└── 用于乐观并发控制
3.2 时间戳操作 #
javascript
// 获取文档时间戳
Select(["ts"], Get(Ref(Collection("users"), "123456")))
// 转换为可读时间
Time(ToString(Select(["ts"], doc)))
// 使用时间戳查询历史版本
Get(
Ref(Collection("users"), "123456"),
Time("2024-01-01T00:00:00Z")
)
四、数据字段(data) #
4.1 数据结构 #
javascript
// 简单数据
{
data: {
name: "Tom",
age: 30,
active: true
}
}
// 嵌套数据
{
data: {
name: "Tom",
address: {
city: "Beijing",
country: "China",
location: {
lat: 39.9042,
lng: 116.4074
}
},
tags: ["developer", "admin"],
profile: {
avatar: "avatar.png",
bio: "Software Engineer"
}
}
}
// 包含引用的数据
{
data: {
name: "Order #001",
user: Ref(Collection("users"), "123456"), // 引用其他文档
products: [
Ref(Collection("products"), "111"),
Ref(Collection("products"), "222")
]
}
}
4.2 访问数据 #
javascript
// 访问顶级字段
Select(["data", "name"], doc)
// 访问嵌套字段
Select(["data", "address", "city"], doc)
// 带默认值访问
Select(["data", "middleName"], doc, "N/A")
// 安全访问(避免错误)
Let(
{
doc: Get(Ref(Collection("users"), "123456"))
},
{
name: Select(["data", "name"], Var("doc")),
email: Select(["data", "email"], Var("doc"), null),
phone: Select(["data", "phone"], Var("doc"), "Not provided")
}
)
五、文档ID #
5.1 自动生成ID #
javascript
// FaunaDB自动生成唯一ID
Create(Collection("users"), {
data: { name: "Tom" }
})
// 返回的ref包含自动生成的ID
// ref: Ref(Collection("users"), "340293847938473857")
5.2 自定义ID #
javascript
// 使用NewID生成ID
Let(
{
id: NewId()
},
Create(Ref(Collection("users"), Var("id")), {
data: { name: "Tom" }
})
)
// 使用指定ID
Create(Ref(Collection("users"), "custom_id_001"), {
data: { name: "Tom" }
})
5.3 ID最佳实践 #
text
ID使用建议:
├── 让FaunaDB自动生成(推荐)
├── 自定义ID需要确保唯一性
├── 避免使用敏感信息作为ID
├── ID创建后不可更改
└── ID长度限制:256字符
六、文档操作 #
6.1 创建文档 #
javascript
// 基本创建
Create(Collection("users"), {
data: {
name: "Tom",
email: "tom@example.com"
}
})
// 带元数据创建
Create(Collection("users"), {
data: {
name: "Tom",
email: "tom@example.com"
},
metadata: {
createdBy: "admin",
source: "import"
}
})
// 创建并返回特定字段
Let(
{
doc: Create(Collection("users"), {
data: { name: "Tom" }
})
},
{
id: Select(["ref", "id"], Var("doc")),
name: Select(["data", "name"], Var("doc"))
}
)
6.2 读取文档 #
javascript
// 通过Ref读取
Get(Ref(Collection("users"), "123456"))
// 通过索引读取
Get(Match(Index("users_by_email"), "tom@example.com"))
// 读取特定时间点的文档
Get(
Ref(Collection("users"), "123456"),
Time("2024-01-01T00:00:00Z")
)
6.3 更新文档 #
javascript
// 部分更新(合并)
Update(Ref(Collection("users"), "123456"), {
data: {
age: 31
}
})
// 替换整个data
Replace(Ref(Collection("users"), "123456"), {
data: {
name: "Tom Updated",
email: "tom.updated@example.com"
}
})
6.4 删除文档 #
javascript
// 删除文档
Delete(Ref(Collection("users"), "123456"))
// 条件删除
If(
Equals(Select(["data", "status"], Get(userRef)), "inactive"),
Delete(userRef),
Abort("Cannot delete active user")
)
七、文档关系 #
7.1 引用关系 #
javascript
// 用户文档
{
ref: Ref(Collection("users"), "user_001"),
data: {
name: "Tom"
}
}
// 订单文档(引用用户)
{
ref: Ref(Collection("orders"), "order_001"),
data: {
orderNumber: "ORD-001",
user: Ref(Collection("users"), "user_001"), // 引用用户
total: 99.99
}
}
7.2 解引用 #
javascript
// 获取订单及其用户
Let(
{
order: Get(Ref(Collection("orders"), "order_001")),
userRef: Select(["data", "user"], Var("order")),
user: Get(Var("userRef"))
},
{
order: Var("order"),
user: Var("user")
}
)
// 批量解引用
Map(
Paginate(Match(Index("all_orders"))),
Lambda("orderRef",
Let(
{
order: Get(Var("orderRef")),
userRef: Select(["data", "user"], Var("order")),
user: Get(Var("userRef"))
},
Merge(Var("order"), {
user: Var("user")
})
)
)
)
7.3 嵌入vs引用 #
javascript
// 嵌入数据(适合不常变化的数据)
{
data: {
name: "Order #001",
shippingAddress: { // 嵌入地址
street: "123 Main St",
city: "Beijing"
}
}
}
// 引用数据(适合需要独立管理的数据)
{
data: {
name: "Order #001",
address: Ref(Collection("addresses"), "addr_001") // 引用地址
}
}
八、文档验证 #
8.1 集合级验证 #
javascript
// 创建带验证的集合
CreateCollection({
name: "products",
document_schema: {
validate: Query(
Lambda("doc",
And(
// 验证name是字符串
IsString(Select(["data", "name"], Var("doc"))),
// 验证price是正数
And(
IsNumber(Select(["data", "price"], Var("doc"))),
GT(Select(["data", "price"], Var("doc")), 0)
)
)
)
)
}
})
8.2 创建时验证 #
javascript
// 在创建时验证数据
Create(Collection("products"), {
data: Let(
{
name: Var("inputName"),
price: Var("inputPrice")
},
If(
And(
IsString(Var("name")),
And(IsNumber(Var("price")), GT(Var("price"), 0))
),
{
name: Var("name"),
price: Var("price")
},
Abort("Invalid product data")
)
)
})
九、文档历史 #
9.1 历史版本 #
text
历史版本特点:
├── 自动保留文档历史
├── 保留天数由history_days控制
├── 可查询任意时间点的状态
└── 支持时间旅行查询
9.2 查询历史 #
javascript
// 获取文档历史
Paginate(
Events(Ref(Collection("users"), "123456")),
{ size: 100 }
)
// 获取特定时间点的文档
Get(
Ref(Collection("users"), "123456"),
Time("2024-01-01T00:00:00Z")
)
// 获取时间范围内的变更
Paginate(
Events(Ref(Collection("users"), "123456")),
{
start: Time("2024-01-01T00:00:00Z"),
end: Time("2024-01-31T23:59:59Z")
}
)
十、文档最佳实践 #
10.1 数据建模 #
javascript
// 用户文档示例
{
data: {
// 基本信息
email: "tom@example.com",
name: "Tom",
// 嵌入不常变化的数据
profile: {
avatar: "avatar.png",
bio: "Developer"
},
// 引用需要独立管理的数据
organization: Ref(Collection("organizations"), "org_001"),
// 数组引用
roles: [
Ref(Collection("roles"), "role_admin"),
Ref(Collection("roles"), "role_user")
],
// 时间戳
createdAt: Time("2024-01-01T00:00:00Z"),
updatedAt: Now()
}
}
10.2 字段命名 #
text
字段命名建议:
├── 使用camelCase
├── 描述性名称
├── 避免保留字
├── 保持一致性
└── 示例:firstName, createdAt, isActive
10.3 数据类型选择 #
javascript
// 正确的类型选择
{
data: {
// 字符串
name: "Tom",
email: "tom@example.com",
// 数字
age: 30,
balance: 1234.56,
// 布尔
isActive: true,
// 数组
tags: ["developer", "admin"],
// 对象
address: {
city: "Beijing",
country: "China"
},
// 引用
organization: Ref(Collection("organizations"), "org_001"),
// 时间
createdAt: Now(),
birthDate: Date("1990-01-01")
}
}
十一、常见问题 #
11.1 文档不存在 #
javascript
// 安全获取文档
If(
Exists(Ref(Collection("users"), "123456")),
Get(Ref(Collection("users"), "123456")),
null
)
11.2 字段不存在 #
javascript
// 安全访问字段
Let(
{
doc: Get(userRef)
},
{
name: Select(["data", "name"], Var("doc"), "Unknown"),
phone: Select(["data", "phone"], Var("doc"), null)
}
)
11.3 并发更新 #
javascript
// 使用时间戳进行乐观锁
Let(
{
doc: Get(userRef),
currentTs: Select(["ts"], Var("doc"))
},
If(
Equals(Var("currentTs"), Var("expectedTs")),
Update(userRef, { data: { name: "Updated" } }),
Abort("Document was modified by another transaction")
)
)
十二、总结 #
文档结构要点:
| 组成部分 | 说明 |
|---|---|
| ref | 文档唯一引用 |
| ts | 时间戳(微秒) |
| data | 数据字段 |
| metadata | 元数据(可选) |
下一步,让我们学习索引机制!
最后更新:2026-03-27