学习BoltDB的工具包Storm

254 阅读4分钟

BoltDB用[]byte 阵列来处理一切,这很简单、直接、快速,但在可用性方面有一些取舍,因为它使复杂的查询和嵌套结构变得有点麻烦。

这一次,我们将看看storm,它是BoltDB的一个工具包,提供索引、高级查询系统和更多的工具来处理复杂的使用情况。

当然,你可能会说,如果你想解决的问题不能很好地映射到BoltDB上,那么完全使用另一个数据存储是有意义的,你会是对的。然而,在现实世界中,我们并不总是在项目开始时就知道我们以后会需要什么,如果我们决定使用Bolt,但在一些情况下它不是最佳选择,那么使用像storm这样的抽象,而不是添加第二个数据存储,可能会更好。

在这篇文章中,我们将看一下storm ,为我们需要比键/值访问多一点的情况提供一些API改进。

让我们从我们想使用的数据模型开始。

数据模型

我们将再次使用相同的数据模型,看看风暴如何帮助我们。

type Config struct {
    ID int `storm:"id, increment"`
    Birthday time.Time
    Height float
}

配置 - 我们总是想使用最新的配置。

type Weight struct {
    ID int `storm:"id, increment"`
    Date time.Time
    Weight float64
}

重量时间线。最新的体重条目是当前的体重。

type Entry struct {
    ID int `storm:"id, increment"`
    Date time.Time
    Food string
    Calories int
}

条目,按天显示。应该可以按时间跨度进行查询。

实施

正如你从数据模型结构中看到的,我们在每个模型的ID上添加了一个storm:"id, increment" 标签。这并不令人惊讶,它为这些模型创建了一个自我递增的PK。

设置数据库的方法基本上是一样的。

db, err := storm.Open("test.db")
if err != nil {
    log.Fatal(err)
}
defer db.Close()

然而,不是为我们的每一个模型创建Buckets,而是storm根据我们的模型声明为我们处理这个问题。有了storm,我们也不需要为了保存模型而对其进行序列化。

同样,让我们从配置开始。添加一个新的条目只包括创建模型对象和调用db.Save ,很简单。不需要在序列化或桶上做手脚。

func addConfig(db *storm.DB, height float64, birthday time.Time) error {
    config := Config{Height: height, Birthday: birthday}
    err := db.Save(&config)
    if err != nil {
     return fmt.Errorf("could not save config, %v", err)
    }
    return nil
}

查询也是很简单的。我们想要最新的条目,有多种方法来实现这一点,一种方法是按相反的顺序查询,并限制在1。

    var configs []Config
    err = db.All(&configs, storm.Limit(1), storm.Reverse())
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(configs)

接下来是权重,这也没有什么不同。添加的方式与配置的方式相同。

func addWeight(db *storm.DB, value float64, date time.Time) error {
    weight := Weight{Weight: value, Date: date}
    err := db.Save(&weight)
    if err != nil {
        return fmt.Errorf("could not save weight, %v", err)
    }
    return nil
}

为了查询权重的时间线,我们只需按倒序查询所有权重。当前的权重将与最新配置的查询相同。

    var weights []Weight
    err = db.All(&weights, storm.Reverse())
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(weights)

在关于BoltDB的帖子中,条目更加复杂,因为我们必须使用RFC3339 日期格式来保存它们。有了storm,我们只要用普通的time.Time ,就可以创建我们的数据模型,一切都很好。

func addEntry(db *storm.DB, food string, calories int, date time.Time) error {
    entry := Entry{Food: food, Calories: calories, Date: date}
    err := db.Save(&entry)
    if err != nil {
        return fmt.Errorf("could not save entry, %v", err)
    }
    return nil
}

在一个时间范围内获取条目,例如:显示一天的所有条目,或一周内的条目,也是相当麻烦的。Storm提供了几种方法来实现这一点,使用它的简单和高级查询API。

例如,使用db.Range() 来获取今天的条目。

    var today []Entry
    err = db.Range("Date", time.Now().AddDate(0, 0, -1), time.Now().AddDate(0, 0, 1), &today)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Entries from Today:")
    fmt.Println(today)

或者,使用q 查询API获取3天到1天前的所有条目。

    var twoDaysAgo []Entry
    query := db.Select(q.Gt("Date", time.Now().AddDate(0, 0, -3)), q.Lt("Date", time.Now().AddDate(0, 0, -1)))
    err = query.Find(&twoDaysAgo)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Entries from Two Days Ago:")
    fmt.Println(twoDaysAgo)

相当整洁,日期处理也是开箱即用。我们还可以使用q.And(),q.Not(),q.Or() 等添加更多的子句来进行更细粒度的查询。

使用风暴,使用查询API的db.Update,db.UpdateFielddb.DeleteStruct 方法来更新和/或删除数据条目也是很简单的。

总结

即使在这个相当简单的例子之后,我必须说我喜欢风暴API,以及它比单独使用BoltDB做复杂的查询要容易得多。

然而,这篇文章几乎没有触及风暴的高级查询API的表面,而且我们没有使用任何索引,所以风暴还有很多东西比我们的小例子所展示的更多。

正如我在一开始写的那样--如果手头的问题不适合BoltDB,你可能应该使用另一个数据存储,但是对于一个漂亮的查询API可以节省时间,而且抽象成本也不那么重要的情况,storm似乎是一个可靠的、记录良好的解决方案。