使用Firebase / Firestore具有重新排序功能的任务列表

时间:2019-04-09 19:39:24

标签: swift firebase firebase-realtime-database database-design google-cloud-firestore

我想列出可以更改其顺序的任务,但是我不确定如何将其存储在数据库中。

我不想使用数组,因为将来必须做一些查询。

这是我的数据库的屏幕截图:

Database screenshot

我正在尝试制作Trello之类的东西,用户可以添加任务,并可以根据任务的优先级上下移动任务。我还需要更改任务在数据库中的位置以及维护记录。我无法理解如何在任何数据库中执行此操作。我是一位经验丰富的开发人员,我曾使用过mongodb和firebase,但这对我来说是独一无二的。

这里是创建和获取所有任务的代码。当我尝试移动集合中的某些任务时。我在每个任务中都有一个索引。 假设当我将任务从索引5的位置移动到索引2时,我必须按+1编辑所有即将到来的索引,是否有更好的方法?

代码示例

class taskManager {
    static let shared = taskManager()
    typealias TasksCompletion = (_ tasks:[Task],_ error:String?)->Void
    typealias SucessCompletion = (_ error:String?)->Void

    func addTask(task:Task,completion:@escaping SucessCompletion){
        Firestore.firestore().collection("tasks").addDocument(data: task.toDic) { (err) in
            if err != nil {
                print(err?.localizedDescription as Any)
            }
            completion(nil)
        }
    }

    func getAllTask(completion:@escaping TasksCompletion){
        Firestore.firestore().collection("tasks")
            .addSnapshotListener { taskSnap, error in
                taskSnap?.documentChanges.forEach({ (task) in
                    let object = task.document.data()
                    let json = try! JSONSerialization.data(withJSONObject: object, options: .prettyPrinted)
                    var taskData = try! JSONDecoder().decode(Task.self, from: json)
                    taskData.id = task.document.documentID

                    if (task.type == .added) {
                        Task.shared.append(taskData)
                    }
                    if (task.type == .modified) {
                        let index = Task.shared.firstIndex(where: { $0.id ==  taskData.id})!
                        Task.shared[index] = taskData
                    }
                })
                if error == nil{
                    completion(Task.shared,nil)
                }else{
                    completion([],error?.localizedDescription)
                }
            }
    }
}

3 个答案:

答案 0 :(得分:1)

您可能会采用几种方法来实现这种功能,

方法1:


您可以给任务指定较远的位置,而不是连续的位置,例如:

日期:2019年4月10日 名称:“某些任务名称” 指数:10 ... 指数:20 ... 索引:30

这里共有3个任务,分别位于位置10、20、30。现在,您想将第三个任务移到中间,只需将位置更改为15,现在您有三个任务,分别位于位置10、15、20,I确保从数据库中获取所有任务时您可以根据位置进行排序,并且我还假设您可以获取任务的位置,因为用户将在移动应用程序或Web应用程序上重新安排任务,因此您可以轻松地获取位置并计算周围任务的中间位置, 现在假设您想将第一个任务(现在的位置索引为10)移到中间,只需获取周围任务的位置15和20,然后计算中间任务的位置为17.5((20-15)/ 2 = 17.5),您现在就可以进入位置15、17.5、20

有人说1和2之间有无穷大,所以我不会计算我们的数字,但仍然有一部分人认为您很快就会除以除法,您可以增加差值,并可以使它达到100000。 ..00而不是10

方法2:


您可以将所有任务保存在同一文档中,而不是以分层json格式保存单独的文档,如下所示:

任务:'[{名称:“ some name”,date:“ some date”},{name:“ some name”,date:“ some date”},{name:“ some name”,date:“一些日期“}]'

通过执行此操作,您将立即在屏幕上获得所有任务,并将json解析为本地数组,当用户重新排列任务时,您只需在本地更改该数组元素的位置并保存任务的分层版本在数据库中,这种方法也有一些弊端,如果您使用分页,可能很难做到这一点,但希望您不会在任务管理应用程序中使用分页,并且您可能希望在屏幕上显示所有任务就像Trello一样

答案 1 :(得分:0)

我认为您要提出的问题更多是关于数据库设计

如果您希望能够对一组商品进行排序同时又能够对其进行重新排序,则需要一列来保持订单。

如果尝试顺序订购它们,则会遇到问题。

示例

例如,如果您想将Item1移到Item4之后:

之前

具有订购索引的商品。

 1. Item1, order: 1
 2. Item2, order: 2
 3. Item3, order: 3
 4. Item4, order: 4
 5. Item5, order: 5
 6. Item6, order: 6

之后

问题:我们必须更新要移动的项目与放置位置之间的每条记录。

为什么会出现问题:这是一个大O(n)-对于我们移动的每个空间,我们都必须更新那么多记录。随着您完成更多任务,这将成为一个问题,因为它将花费更长的时间并且扩展性不佳。拥有一个大O(1)会很高兴,因为我们可以不断进行更改,或者更改次数尽可能少。

 1. Item2, order: 1 - Updated
 2. Item3, order: 2 - Updated
 3. Item4, order: 3 - Updated
 4. Item1, order: 4 - Updated
 5. Item5, order: 5
 6. Item6, order: 6

可能的解决方案1(也许可以吗?)-间距

您可以尝试提出一种巧妙的方法,在此方法中,您尝试将订单编号间隔开,以便在不更新多个记录的情况下填补空白。

这可能会很棘手,您可能会想,“为什么不按顺序存储Item1:4.5”,我在下面添加了一个related question来说明这个想法以及为什么应该避免它。

您也许可以验证订单客户端的安全性,并避免访问数据库来确定移动的新订单ID。

这也有局限性,因为您可能必须重新平衡间距,或者项目用尽了所有数字。您可能必须检查是否存在冲突,当发生冲突时,您需要对所有内容进行重新平衡,或者递归地处理冲突周围的项目,以确保其他平衡更新不会引起更多冲突,并且可以解决其他冲突。

 1. Item2, order: 200
 2. Item3, order: 300
 3. Item4, order: 400
 4. Item1, order: 450 - Updated
 5. Item5, order: 500
 6. Item6, order: 600

可能的解决方案2(更好)-链接列表

related link below中所述,您可以使用链表等数据结构。这将保留不变数量的更改以进行更新,因此它是Big O(1)。如果您还没有使用数据结构,我将稍作介绍。

正如您在下面看到的那样,此更改仅需要进行3次更新,我相信最大值将为5,如Expected Updates所示。您可能会想:“第一个原始问题/示例花了那么多钱!”事实是,与原始方法[Big O(n)]相比,这总是最多可以进行5次更新。

 1. Item2, previous: null, next: Item3 - Updated // previous is now null
 2. Item3, previous: Item2, next: Item4
 3. Item4, previous: Item3, next: Item1 - Updated // next is now Item1
 4. Item1, previous: Item4, next: Item5 - Updated // previous & next updated
 5. Item5, previous: Item1, next: Item4 - Updated // previous is now Item1
 6. Item6, previous: Item6, next: null

预期更新

  1. 要移动的项目(上一个,下一个)
  2. 上一个旧项目的下一个
  3. 下一个旧项目的上一个
  4. 上一个新项目的下一个
  5. 下一个新项目的上一个

链接列表

我猜我使用了double linked list。您可能只使用一个没有previous属性而只有next属性的链接列表就可以摆脱这种麻烦。

链接列表背后的想法是将其视为一个链链接,当您要移动一项时,可以将其与它前面和后面的链接分离,然后将这些链接链接在一起。接下来,您将打开要在其之间放置的位置,现在它的每一侧都有新链接,对于这些新链接,它们现在将链接到新链接,而不是彼此链接。

可能的解决方案#3-文档/ Json /阵列存储

您说过要远离阵列,但可以利用文档存储。您仍然可以有一个可搜索的项目表,然后每个项目集合将只有一个项目ID /参考的数组。

物品表

 - Item1, id: 1
 - Item2, id: 2
 - Item3, id: 3
 - Item4, id: 4
 - Item5, id: 5
 - Item6, id: 6

物品收藏

 [2, 3, 4, 1, 5, 6]

相关问题

Big O上的资源

其他注意事项

您的数据库设计将取决于您要完成的工作。项目可以属于多个板或用户吗?

您可以将一些订单转移给客户端,并允许它告诉服务器新订单是什么吗?您仍然应该避免在客户端使用效率低下的排序算法,但是如果您信任它们,可以让他们完成一些肮脏的工作,并且如果多个人同时在同一项目上工作,则不会出现数据完整性问题。时间(这些是其他设计问题,可能与数据库无关,取决于您的处理方式。)

答案 2 :(得分:0)

我在同一个问题上停留了很长时间。我发现最好的解决方案是按字典顺序

排序。

尝试管理小数位(1、2、3、4 ...)会遇到很多问题,所有其他问题都在此问题中提到。相反,我将等级存储为字符串('aaa','bbb','ccc'...),并且使用字符串中的字符的字符代码在进行调整时找到等级之间的位置

例如,我有:

{
    item: "Star Wars",
    rank: "bbb"
},
{
    item: "Lord of the Rings",
    rank: "ccc"
},
{
    item: "Harry Potter",
    rank: "ddd"
},
{
    item: "Star Trek",
    rank: "eee"
},
{
    item: "Game of Thrones",
    rank: "fff"
}

现在,我想将"Game of Thrones"移动到第三个插槽,分别位于"Lord of the Rings"'ccc'以下和"Harry Potter"'ddd')以上。

因此,我使用'ccc''ddd'的字符代码在数学上找到两个字符串之间的平均值。在这种情况下,最终是'cpp',我将文档更新为:

{
    item: "Game of Thrones",
    rank: "cpp"
}

现在我有:

{
    item: "Star Wars",
    rank: "bbb"
},
{
    item: "Lord of the Rings",
    rank: "ccc"
},
{
    item: "Game of Thrones",
    rank: "cpp"
},
{
    item: "Harry Potter",
    rank: "ddd"
},
{
    item: "Star Trek",
    rank: "eee"
}

如果我在两个等级之间的空间不足,则只需在字符串的末尾添加一个字母即可;因此,我可以在'bbb''bbc'之间插入'bbbn'

这比十进制排名有好处。

注意事项

请勿将'aaa''zzz'分配给任何项目。需要保留这些内容,以轻松地将项目移动到列表的顶部或底部。如果"Star Wars"的排名为'aaa',而我想在其上方移动一些内容,则会出现问题。可解决的问题,但是如果您从排名'bbb'开始,这很容易避免。然后,如果您想将某些内容移到最高排名之上,则只需找到'bbb''aaa'之间的平均值即可。

如果您的列表经常被改组,则最好定期刷新排名。如果将事物移动到列表中的同一位置数千次,则可能会得到一个长字符串,如'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbn'。当字符串达到一定长度时,您可能需要刷新列表。

实施

here可以找到实现该效果的算法和功能说明。该想法归功于该文章的作者。