Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

Cloud Firestore 数据模型

Cloud Firestore 是一种基于文档的 NoSQL 数据库。与 SQL 数据库不同的是,它没有表或行的概念,而是将数据存储在文档中,并将文档组织为集合。

每个文档包含一组键值对。Cloud Firestore 经过优化,适合用于存储由小型文档组成的大型集合。

所有文档必须存储在集合中。文档可以包含子集合和嵌套对象,两者都可以包含原始字段(如字符串)或复杂对象(如列表)。

在 Cloud Firestore 中,集合和文档是隐式创建的。 只需将数据分配给集合中的文档即可。如果集合或文档不存在,Cloud Firestore 会创建一个。

文档

在 Cloud Firestore 中,存储单元是文档。文档是一个轻量级记录,包含映射到值的字段。每个文档都用一个名称进行标识。

代表用户 alovelace 的文档可能如下所示:

  • alovelace

    first : "Ada"
    last : "Lovelace"
    born : 1815

文档中的复杂嵌套对象称为映射。例如,您可以使用映射来设计上例中用户姓名的结构,如下所示:

  • alovelace

    name :
        first : "Ada"
        last : "Lovelace"
    born : 1815

您可能会注意到,文档看起来很像 JSON。它们其实基本上就是 JSON,但有少许差异(例如,文档支持更多数据类型,大小限制为 1 MB),但总体而言,您可以将文档视为轻量级的 JSON 记录。

集合

文档存在于集合中,集合是文档的容器。例如,您可以使用 users 集合来容纳您的各个用户,每个用户用一个文档来表示:

  • users

    • alovelace

      first : "Ada"
      last : "Lovelace"
      born : 1815

    • aturing

      first : "Alan"
      last : "Turing"
      born : 1912

Cloud Firestore 是无架构的,因此您可以自由选择在每个文档中设置什么字段以及在这些字段中存储什么数据类型。同一集合中的文档可以全部包含不同的字段或在这些字段中存储不同类型的数据。但是,最好在多个文档中使用相同的字段和数据类型,以便您可以更轻松地查询文档。

集合只包含文档,不包含任何其他内容。它不能直接包含带有值的原始字段,也不能包含其他集合。(如需了解如何在 Cloud Firestore 中设计更复杂数据的结构,请参阅分层数据。)

在同一个集合中,每个文档的名称都各不相同。您可以提供自己的键(例如用户 ID),也可以让 Cloud Firestore 自动为您创建随机 ID。

您无需“创建”或“删除”集合。在集合中创建第一个文档之后,集合才会存在。如果删除集合中的所有文档,集合将不再存在。

引用

Cloud Firestore 中的每个文档都按照其在数据库中的位置唯一无二地进行标识。之前的示例展示了集合 users 中的一个文档 alovelace。如需在代码中引用此位置,您可以创建一个指向此位置的引用。

Web 版本 9

import { doc } from "firebase/firestore";

const alovelaceDocumentRef = doc(db, 'users', 'alovelace');

Web 版本 8

var alovelaceDocumentRef = db.collection('users').doc('alovelace');
Swift
let alovelaceDocumentRef = db.collection("users").document("alovelace")
Objective-C
FIRDocumentReference *alovelaceDocumentRef =
    [[self.db collectionWithPath:@"users"] documentWithPath:@"alovelace"];

Java

DocumentReference alovelaceDocumentRef = db.collection("users").document("alovelace");

Kotlin+KTX

val alovelaceDocumentRef = db.collection("users").document("alovelace")
Java
// Reference to a document with id "alovelace" in the collection "users"
DocumentReference document = db.collection("users").document("alovelace");
Python
a_lovelace_ref = db.collection(u'users').document(u'alovelace')

Python

a_lovelace_ref = db.collection("users").document("alovelace")
C++
DocumentReference alovelace_document_reference =
    db->Collection("users").Document("alovelace");
Node.js
const alovelaceDocumentRef = db.collection('users').doc('alovelace');
Go
alovelaceRef := client.Collection("users").Doc("alovelace")
PHP
$document = $db->collection('samples/php/users')->document('lovelace');
Unity
DocumentReference documentRef = db.Collection("users").Document("alovelace");
C#
DocumentReference documentRef = db.Collection("users").Document("alovelace");
Ruby
document_ref = firestore.col("users").doc("alovelace")

引用是一个轻量级对象,它仅指向数据库中的某个位置。无论此位置中是否存在数据,您都可以创建引用,并且创建引用不会执行任何网络操作。

您还可以创建对集合的引用:

Web 版本 9

import { collection } from "firebase/firestore";

const usersCollectionRef = collection(db, 'users');

Web 版本 8

var usersCollectionRef = db.collection('users');
Swift
let usersCollectionRef = db.collection("users")
Objective-C
FIRCollectionReference *usersCollectionRef = [self.db collectionWithPath:@"users"];

Java

CollectionReference usersCollectionRef = db.collection("users");

Kotlin+KTX

val usersCollectionRef = db.collection("users")
Java
// Reference to the collection "users"
CollectionReference collection = db.collection("users");
Python
users_ref = db.collection(u'users')

Python

users_ref = db.collection("users")
C++
CollectionReference users_collection_reference = db->Collection("users");
Node.js
const usersCollectionRef = db.collection('users');
Go
usersRef := client.Collection("users")
PHP
$collection = $db->collection('samples/php/users');
Unity
CollectionReference collectionRef = db.Collection("users");
C#
CollectionReference collectionRef = db.Collection("users");
Ruby
collection_ref = firestore.col "users"

为方便起见,您还可以通过将文档或集合的路径指定为字符串来创建引用,路径组成部分之间用正斜杠 (/) 分隔。例如,如需创建对 alovelace 文档的引用,请执行以下操作:

Web 版本 9

import { doc } from "firebase/firestore";

const alovelaceDocumentRef = doc(db, 'users/alovelace');

Web 版本 8

var alovelaceDocumentRef = db.doc('users/alovelace');
Swift
let aLovelaceDocumentReference = db.document("users/alovelace")
Objective-C
FIRDocumentReference *aLovelaceDocumentReference =
    [self.db documentWithPath:@"users/alovelace"];

Java

DocumentReference alovelaceDocumentRef = db.document("users/alovelace");

Kotlin+KTX

val alovelaceDocumentRef = db.document("users/alovelace")
Java
// Reference to a document with id "alovelace" in the collection "users"
DocumentReference document = db.document("users/alovelace");
Python
a_lovelace_ref = db.document(u'users/alovelace')

Python

a_lovelace_ref = db.document("users/alovelace")
C++
DocumentReference alovelace_document = db->Document("users/alovelace");
Node.js
const alovelaceDocumentRef = db.doc('users/alovelace');
Go
alovelaceRef := client.Doc("users/alovelace")
PHP
$document = $db->document('users/lovelace');
Unity
DocumentReference documentRef = db.Document("users/alovelace");
C#
DocumentReference documentRef = db.Document("users/alovelace");
Ruby
document_path_ref = firestore.doc "users/alovelace"

分层数据

要了解分层数据结构在 Cloud Firestore 中的工作原理,让我们以一个提供消息和聊天室功能的聊天应用来看一看。

您可以创建名为 rooms 的集合来存储不同的聊天室:

  • rooms

    • roomA

      name : "my chat room"

    • roomB

      ...

现在您已经有聊天室了,就可以决定如何存储消息。您可能不想将它们存储在聊天室的文档中。这是因为 Cloud Firestore 中的文档应该是轻量级的,而聊天室可能包含大量消息。 但是,您可以在聊天室的文档中创建额外的集合作为子集合。

子集合

在此例中,存储消息的最佳方式是使用子集合。子集合是指与某个文档相关联的集合。

您可以为 rooms 集合中的每个聊天室文档创建名为 messages 的子集合:

  • rooms

    • roomA

      name : "my chat room"

      • messages

        • message1

          from : "alex"
          msg : "Hello World!"

        • message2

          ...

    • roomB

      ...

在此例中,您将使用以下代码创建对子集合中的消息的引用:

Web 版本 9

import { doc } from "firebase/firestore";

const messageRef = doc(db, "rooms", "roomA", "messages", "message1");

Web 版本 8

var messageRef = db.collection('rooms').doc('roomA')
                .collection('messages').doc('message1');
Swift
let messageRef = db
    .collection("rooms").document("roomA")
    .collection("messages").document("message1")
Objective-C
FIRDocumentReference *messageRef =
    [[[[self.db collectionWithPath:@"rooms"] documentWithPath:@"roomA"]
    collectionWithPath:@"messages"] documentWithPath:@"message1"];

Java

DocumentReference messageRef = db
        .collection("rooms").document("roomA")
        .collection("messages").document("message1");

Kotlin+KTX

val messageRef = db
        .collection("rooms").document("roomA")
        .collection("messages").document("message1")
Java
// Reference to a document in subcollection "messages"
DocumentReference document =
    db.collection("rooms").document("roomA").collection("messages").document("message1");
Python
room_a_ref = db.collection(u'rooms').document(u'roomA')
message_ref = room_a_ref.collection(u'messages').document(u'message1')

Python

room_a_ref = db.collection("rooms").document("roomA")
message_ref = room_a_ref.collection("messages").document("message1")
C++
DocumentReference message_reference = db->Collection("rooms")
    .Document("roomA")
    .Collection("messages")
    .Document("message1");
Node.js
const messageRef = db.collection('rooms').doc('roomA')
  .collection('messages').doc('message1');
Go
messageRef := client.Collection("rooms").Doc("roomA").
	Collection("messages").Doc("message1")
PHP
$document = $db
    ->collection('rooms')
    ->document('roomA')
    ->collection('messages')
    ->document('message1');
Unity
DocumentReference documentRef = db
	.Collection("Rooms").Document("RoomA")
	.Collection("Messages").Document("Message1");
C#
DocumentReference documentRef = db
    .Collection("Rooms").Document("RoomA")
    .Collection("Messages").Document("Message1");
Ruby
message_ref = firestore.col("rooms").doc("roomA").col("messages").doc("message1")

请注意集合和文档的交替模式。您的集合和文档必须始终遵循此模式。您不能在集合中引用集合或在文档中引用文档。

子集合可让您以分层方式设计数据结构,使数据更易于访问。要获取 roomA 中的所有消息,您可以创建对子集合 messages 的集合引用,并像与任何其他集合引用交互一样与其进行交互。

子集合中的文档也可以包含子集合,以便进一步嵌套数据。嵌套数据的深度最多可达 100 级。