【问题标题】:Firestore transactions conditional update?Firestore 事务有条件更新?
【发布时间】:2018-05-19 18:46:40
【问题描述】:

我正在尝试检查保存在数据库中的值是否与用户选择匹配,如果匹配则更新它,否则不执行任何操作。

我尝试了以下方法,但出现异常:

原因:com.google.firebase.firestore.FirebaseFirestoreException:事务中读取的每个文档也必须写入。

这是我的代码:

//update toolbar titles if they match
final DocumentReference adminDocRef = mDbase.collection("admins").document(adminID);
mDbase.runTransaction(new Transaction.Function<Void>() {
    @Override
    public Void apply(@NonNull Transaction transaction) throws FirebaseFirestoreException {
        DocumentSnapshot adminSnapshot = transaction.get(adminDocRef);
        String toolbarTitle = adminSnapshot.getString("displayedUser");
        if (userName.equals(toolbarTitle)) {
            transaction.update(adminDocRef, "displayedUser", userName);
        }
        // Success
        return null;
    }
})
        .addOnSuccessListener(new OnSuccessListener<Void>() {
    @Override
    public void onSuccess(Void aVoid) {
        Log.d(TAG, "Transaction success!");
    }
})
        .addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                Log.w(TAG, "Transaction failure.", e);
            }
        });  

交易可以有条件吗?如果不是,你将如何解决这个问题?必须做一个单独的 get 然后在其中嵌套一个更新似乎很愚蠢。

任何帮助将不胜感激。

谢谢!

【问题讨论】:

  • 你试过在查询之外添加条件吗?
  • 感谢阿姆的反馈。你能更具体一点吗?条件基于我要更新的字段中当前的内容。我能看到这样做的唯一方法是在字段上执行单独的查询/get(),然后根据结果执行事务,但这似乎否定了事务的使用开始......在那种情况下,我我想我会定期更新。

标签: android firebase transactions google-cloud-firestore


【解决方案1】:

有一个类似的answer 来自Alex ,来自documentation

交易可能因以下原因而失败:

  • 事务在写操作之后包含读操作。读操作必须始终在任何写操作之前。 //not your case
  • 事务读取了在事务之外修改的文档。在这种情况下,事务会自动再次运行。事务被重试有限次。

在你的情况下,我会尝试

if (userName.equals(toolbarTitle)) {
//transaction operations 
}

看看有没有用!

【讨论】:

  • 感谢阿姆的回复。但是,如果您从我的代码示例中看到,为了获取 toolbarTitle 值,我需要从我尝试更新的快照中获取信息。根据亚历克斯的回答,我怀疑目前似乎事务在某些类型的条件方面存在问题,如果它涉及不保证写入操作。我为此发布了一个功能请求,我还将向 Firestore 论坛中的窥视者询问它。谢谢。
  • @BrianBegun 你有没有找到解决方案?
【解决方案2】:

因此,为了有条件地更新我的工具栏标题,我必须以这种方式级联我的 Firestore 操作:

DocumentReference adminDocRef = mDbase.collection("admins").document(adminID);
    adminDocRef.get().addOnCompleteListener(activity, new OnCompleteListener<DocumentSnapshot>() {
        @Override
        public void onComplete(@NonNull Task<DocumentSnapshot> task) {
            if (task.isSuccessful()) {
                DocumentSnapshot document = task.getResult();
                if (document.exists()) {
                    //assign document value to toolbarTitle
                    toolbarTitle = document.getString("displayedUser");
                    if (userName.equals(toolbarTitle)) {
                        DocumentReference adminDocRef = mDbase.collection("admins").document(adminID);
                        adminDocRef
                                .update("displayedUser", toolbarTitle)
                                .addOnSuccessListener(new OnSuccessListener<Void>() {
                                    @Override
                                    public void onSuccess(Void aVoid) {
                                        Log.d(TAG, "Displayed child has been updated");
                                    }
                                })
                                .addOnFailureListener(new OnFailureListener() {
                                    @Override
                                    public void onFailure(@NonNull Exception e) {
                                        Log.w(TAG, "Error: updating displayed child", e);
                                    }
                                });
                        Log.d(TAG, "DocumentSnapshot data: " + document.getData());
                    }

                } else {
                    Log.d(TAG, "No such document");
                }
            } else {
                Log.d(TAG, "get failed with ", task.getException());
            }
        }
    });

我不得不这样做有点令人失望,因为将它包装在事务中似乎更有意义。我希望他们允许事务中的条件行为。

【讨论】:

    【解决方案3】:

    实际上,google 有一个名为 Tasks 的类,您可以使用它来使 firestore 调用同步。

    因此,按照您的示例,您可以将其修复为:

    try {
            DocumentSnapshot adminDocument = Tasks.await(adminDocRef.get());
            if (adminDocument.exists()) {
                String toolbarTitle = adminDocument.getString("displayedUser");
                if (userName.equals(toolbarTitle)) {
                    Tasks.await(adminDocRef.update("displayedUser", userName));
                    //Success
                }
            }
        } catch (ExecutionException e) {
            //Error
            e.printStackTrace();
        } catch (InterruptedException e) {
            //Error
            e.printStackTrace();
        }
    

    这样你不需要监听器,同时它是一个有条件的更新。我知道这已经晚了,但希望它会帮助某人。

    [更新]: 请注意,不应在主线程上调用 Tasks.await(...),解决方案是使用 AsyncTask

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-05
      • 2019-07-20
      • 1970-01-01
      • 2019-12-30
      • 2018-05-11
      • 2021-05-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多