Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/database/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Swift FireStore-如何绕过阵列“;不包含“;询问_Swift_Database_Firebase_Google Cloud Firestore_Nosql - Fatal编程技术网

Swift FireStore-如何绕过阵列“;不包含“;询问

Swift FireStore-如何绕过阵列“;不包含“;询问,swift,database,firebase,google-cloud-firestore,nosql,Swift,Database,Firebase,Google Cloud Firestore,Nosql,经过一些研究,似乎很清楚我不能使用FireStore来查询给定数组不包含的项。有人对此用例有解决方法吗 用户注册后,应用程序会获取一堆卡片,每个卡片在FireStore中都有相应的“卡片”文档。用户与卡交互后,卡文档将用户的uid添加到字段数组中(例如:usersWhoHaveSeenThisCard:[userUID]),“用户”文档将卡的uid添加到字段数组中(例如:cardsThisUserHasSeen:[cardUID])。“用户”文档位于“用户”集合中,“卡片”文档位于“卡片”集合中

经过一些研究,似乎很清楚我不能使用FireStore来查询给定数组不包含的项。有人对此用例有解决方法吗

用户注册后,应用程序会获取一堆卡片,每个卡片在FireStore中都有相应的“卡片”文档。用户与卡交互后,卡文档将用户的uid添加到字段数组中(例如:usersWhoHaveSeenThisCard:[userUID]),“用户”文档将卡的uid添加到字段数组中(例如:cardsThisUserHasSeen:[cardUID])。“用户”文档位于“用户”集合中,“卡片”文档位于“卡片”集合中

目前,我想获取所有用户尚未交互的卡。但是,这是有问题的,因为我只知道用户与之交互的卡,所以.whereField(usersWhoHaveSeenThisCard,arrayContains:currentUserUID)将不起作用,因为我需要一个不存在的“arrayDoesNotContain”语句

最后,用户不能拥有一张卡,因此我不能在卡文档中创建一个true/false boolian字段(例如:UserHassenThisCard:false)并搜索该条件

我能想到的唯一解决方案是,在卡片文档上创建一个新的字段数组,其中包括所有没有看到卡片的用户(例如:没有看到卡片的用户:[userUID]),但这意味着每个注册的用户都必须将其uid写入1000多个卡片文档,这将消耗我的数据

我可能只是运气不好,但我希望更熟悉NOSQL/FireStore的人能提供一些见解

// If any code sample would help, please let me know and I'll update - I think this is largely conceptual as of now

正如您从查询限制中发现的,仅使用CloudFireStore并没有简单的解决方法。您需要以某种方式存储所看到的文档列表,将其加载到客户端应用程序的内存中,然后手动从所有潜在文档的查询结果中减去这些文档

你可能想考虑用另一个数据库来扩展你的应用程序,这样可以更干净地进行这种操作(比如SQL数据库,可以执行连接和子查询),并将它们并行维护。


要么如此,要么要求以可预测的顺序查看所有文档,例如按时间戳。然后,您只需存储最后一次看到的文档的时间戳,并使用该时间戳过滤结果。

有一个公认的好答案,但是,它并不能直接解决问题,所以这里是。。。(这可能有帮助,也可能没有帮助,但确实有效)

我不知道你的Firestore结构是什么,所以我的假设是:

cards
   card_id_0
      usersWhoHaveSeenThisCard
         0: uid_0
         1: uid_1
         2: uid_2
   card_id_1
      usersWhoHaveSeenThisCard
         0: uid_2
         1: uid_3
   card_id_2
      usersWhoHaveSeenThisCard
         0: uid_1
         1: uid_3
假设我们想知道哪些卡uid_2没有看到——在本例中,哪个是卡uid_2

func findCardsUserHasNotSeen(uidToCheck: String, completion: @escaping ( ([String]) -> Void ) ) {
    let ref = self.db.collection("cards")

    ref.getDocuments(completion: { snapshot, err in
        if let err = err {
            print(err.localizedDescription)
            return
        }

        guard let docs = snapshot?.documents else {
            print("no docs")
            return
        }
        var documentsIdsThatDoNotContainThisUser = [String]()
        for doc in docs {
            let uidArray = doc.get("usersWhoHaveSeenThisCard") as! [String]
            let x = uidArray.contains(uidToCheck)
            if x == false {
                documentsIdsThatDoNotContainThisUser.append(doc.documentID)
            }
        }
        completion(documentsIdsThatDoNotContainThisUser)
    })
}
然后,像这样的用例

func checkUserAction() {
    let uid = "uid_2" //the user id to check
    self.findCardsUserHasNotSeen(uidToCheck: uid, completion: { result in
        if result.count == 0 {
            print("user: \(uid) has seen all cards")
            return
        }
        for docId in result {
            print("user: \(uid) has not seen: \(docId)")
        }
    })
}
以及输出

user: uid_2 has not seen: card_id_2
这段代码遍历文档,获取存储在每个文档中的uid数组,这些文档用户都有seenThiscard节点,并确定uid是否在数组中。如果没有,它会将该documentID添加到documentSidst中,而documentSidst不包含此用户数组。检查完所有文档后,将返回不包含用户id的documentID数组


知道Firestore的速度有多快,我在一个大数据集上运行了代码,结果很快就返回了,所以对于大多数用例来说,它不会造成任何延迟。

谢谢Doug,不幸的是,我现在无法设置额外的db,但时间戳的想法可能会奏效。所以基本上是按最早的顺序排序,添加一个.whereField(“timestamp”,大于:lastTimestampSeen),并将用户的lastTimestampSeen属性保存到用户文档中。它不是完美的,但应该足以满足您的需要now@bhersh90我认为我们可以做到这一点,而无需添加或维护额外的数据库,甚至不需要更改结构。我给出了一个答案-可能对你的用例有用,也可能不有用。谢谢Jay-我真的很喜欢这种方法。我唯一担心的是这可能会造成大量不必要的阅读。我不太确定FireStore是如何决定获取数据的顺序的,但看起来顺序非常相似(即:cardud)₁, 卡杜德₂, 等等)。如果是这样的话,那么用户在看到任何新的东西之前,必须对他们看到的所有东西进行循环,当他们浏览了数百张卡片时,这会很糟糕。让我知道你的想法-否则我真的很喜欢这种方法。我应该澄清一下,我只想一次提取一批10张卡,目前在我的提取中有一个.limit(10)子句query@bhersh90在这个用例中,读取是确定用户没有看到哪些卡所必需的。如果数据集包含数千张卡片,则影响应该最小。至于一个限制,应该是非常直接的,包括一个计数器,当它达到10时退出for循环。在for-in循环中,每个文档需要读取一次,对吗?如果普通用户看到了90张卡片,因此必须遍历100个文档才能得到一批10张新卡片,那么在500次这样的获取请求之后,我就达到了我的50k读取限制。平均而言,每个会话用户将调用大约5次回迁,因此在计费开始之前,这将支持大约100个每日用户。把它打出来,其实还不错。我想我可以换成这个解决方案。再次感谢!