Google cloud firestore Firestore唯一索引还是唯一约束?

Google cloud firestore Firestore唯一索引还是唯一约束?,google-cloud-firestore,Google Cloud Firestore,在Firestore中是否可以定义具有唯一约束的索引?如果没有,如何在文档字段上强制唯一性(不使用文档ID)?是的,这可以使用两个集合、Firestore规则和批处理写入的组合 简单的想法是,使用批处理写入,将文档写入“数据”集合,同时写入单独的“索引”集合,在该集合中为要唯一的字段值编制索引 然后,使用Firestore规则,您可以确保“数据”集合只能在文档字段的值也存在于索引集合中时写入文档,反之亦然,如果索引中的值与数据集合中的值匹配,则只能将索引集合写入 示例 假设我们有一个用户集合,

在Firestore中是否可以定义具有唯一约束的索引?如果没有,如何在文档字段上强制唯一性(不使用文档ID)?

是的,这可以使用两个集合、Firestore规则和批处理写入的组合

简单的想法是,使用批处理写入,将文档写入“数据”集合,同时写入单独的“索引”集合,在该集合中为要唯一的字段值编制索引

然后,使用Firestore规则,您可以确保“数据”集合只能在文档字段的值也存在于索引集合中时写入文档,反之亦然,如果索引中的值与数据集合中的值匹配,则只能将索引集合写入

示例

假设我们有一个
用户
集合,我们希望确保
用户名
字段是唯一的

我们的
用户
集合只包含
用户名

/User/{id}
{
  username: String 
}
我们的
索引
集合将在路径中包含用户名和一个
属性,该属性包含被索引用户的id

/Index/User/username/{username}
{
  value: User.id
}
为了创建我们的
用户
,我们使用批写入同时创建
用户
文档和
索引
文档

const firebaseApp=…构建您的firebase应用程序
const createUser=async(用户名)=>{
const database=firebaseApp.firestore()
const batch=database.batch()
const Collection=database.Collection('用户')
const ref=Collection.doc()
批次设置(参考{
用户名
})
const Index=database.collection('Index')
const indexRef=Index.doc(`User/username/${username}`)
batch.set(索引外部参照{
值:ref.id
})
等待批处理。提交()
}
要更新我们的
用户
的用户名,我们使用批写入来更新
用户
文档,删除以前的
索引
文档,并同时创建一个新的
索引
文档

const firebaseApp=…构建您的firebase应用程序
const updateUser=async(id,username)=>{
const database=firebaseApp.firestore()
const batch=database.batch()
const Collection=database.Collection('用户')
const ref=Collection.doc(id)
const refDoc=wait ref.get()
const prevData=refDoc.data()
批次更新(参考{
用户名
})
const Index=database.collection('Index')
const prevIndexRef=Index.doc(`User/username/${prevData.username}`)
const indexRef=Index.doc(`User/username/${username}`)
batch.delete(prevIndexRef)
batch.set(索引外部参照{
值:ref.id
})
等待批处理。提交()
}
要删除
用户
,我们使用批写入同时删除
用户
文档和
索引
文档

const firebaseApp=…构建您的firebase应用程序
const deleteUser=async(id)=>{
const database=firebaseApp.firestore()
const batch=database.batch()
const Collection=database.Collection('用户')
const ref=Collection.doc(id)
const refDoc=wait ref.get()
const prevData=refDoc.data()
批处理删除(参考)
const Index=database.collection('Index')
const indexRef=Index.doc(`User/username/${prevData.username}`)
批处理.删除(索引外部参照)
等待批处理。提交()
}
然后,我们设置Firestore规则,使其仅允许在未为不同的
用户
编制用户名索引的情况下创建
用户
用户
的用户名只有在用户名的
索引
不存在时才能更新,而
用户
只有在
索引
也被删除时才能删除。如果已经存在具有相同
用户名的
用户
,则创建和更新将失败,并出现“缺少或权限不足”错误

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {


    // Index collection helper methods

    function getIndexAfter(path) {
      return getAfter(/databases/$(database)/documents/Index/$(path))
    }

    function getIndexBefore(path) {
      return get(/databases/$(database)/documents/Index/$(path))
    }

    function indexExistsAfter(path) {
      return existsAfter(/databases/$(database)/documents/Index/$(path))
    }

    function indexExistsBefore(path) {
      return exists(/databases/$(database)/documents/Index/$(path))
    }


    // User collection helper methods

    function getUserAfter(id) {
      return getAfter(/databases/$(database)/documents/User/$(id))
    }

    function getUserBefore(id) {
      return get(/databases/$(database)/documents/User/$(id))
    }

    function userExistsAfter(id) {
      return existsAfter(/databases/$(database)/documents/User/$(id))
    }


    match /User/{id} {
      allow read: true;

      allow create: if
        getIndexAfter(/User/username/$(getUserAfter(id).data.username)).data.value == id;

      allow update: if
        getIndexAfter(/User/username/$(getUserAfter(id).data.username)).data.value == id &&
        !indexExistsBefore(/User/username/$(getUserAfter(id).data.username));

      allow delete: if
        !indexExistsAfter(/User/username/$(getUserBefore(id).data.username));
    }

    match /Index/User/username/{username} {
      allow read: if true;

      allow create: if
        getUserAfter(getIndexAfter(/User/username/$(username)).data.value).data.username == username;

      allow delete: if 
        !userExistsAfter(getIndexBefore(/User/username/$(username)).data.value) || 
        getUserAfter(getIndexBefore(/User/username/$(username)).data.value).data.username != username;
    }
  }
}

[这不是一个完美的解决方案,但有效]

我用钥匙做了这个独特的钥匙。。。 我希望我的表具有唯一的日期值。所以我把它作为我文件的关键。 我能得到所有的文件吗

db.collection('sensors').doc(sensorId).collection("data").doc(date).set(dataObj).then(() => {
    response.send(dataObj);
});

基于本节中的文档

向集合添加文档对象时,只需添加自定义标识符,如下所示:

const data = {
  name: 'Los Angeles',
  state: 'CA',
  country: 'USA'
};

// Add a new document in collection "cities" with ID 'LA'
const res = await db.collection('cities').doc('LA').set(data);

使用此项作为参考当您在收藏中使用set作为方法时,您可以为此类文档指定一个id,当您需要自动生成id时,您只需在收藏中使用add方法即可

否,目前不可能这样做。在这里查看我的答案。这是一个非常聪明的解决方案,但是,要处理一个非常基本的缺失功能肯定需要大量的工作。出于某种原因,在一个空的firestore中,我在每次创建尝试时都会遇到“缺失或权限不足”错误。如果临时删除规则集,则可以创建索引和用户记录。如果我再次添加规则集,则每个创建请求都会失败。有什么想法/建议吗?清楚的解决方案,但您可以为文档设置标识符,而不是使用批处理。请看下面我的解决方案。我回答的问题是:“不使用文档ID”,因为我不能这样做。我回答的问题是:“不使用文档ID”,因为我不能这样做。