文档结构 #

一、文档概述 #

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