如何使用CockroachDB在Go应用程序中对JSON数据进行建模

243 阅读2分钟

许多应用程序都受益于用关系结构表示一些数据,而用JSON这样更灵活的文档结构表示一些数据。利用关系型数据库中的JSON功能可以减少对专用对象数据库的需求,最大限度地减少基础设施和应用的复杂性,并提高性能。

现代SQL数据库,如PostgreSQL和CockroachDB支持这两种模式,并对JSON文档的存储、构建和操作提供本地支持。Go语言和pgx数据库驱动也包括处理关系型和文档型数据的功能。

在这个例子中,我们将在一个使用CockroachDB作为数据存储的简单Go应用程序中建立JSON数据模型。我们将使用一个简化的产品表,用于购物应用。每个产品都有一个SKU和一个名称。但是产品还有很多其他的属性,比如颜色、尺寸、重量、格式和容量,只适用于某些产品。我们将使用一列来表示所有这些额外的属性。

create table products (
	sku text primary key,
	name text not null,
	extra_attributes jsonb not null
);

insert into products (sku, name, extra_attributes)
values
	('A1000-BK', 'A1000 Battery', '{"color": "black", "capacity": 4000}'),
	('A1200-RD', 'A1200 Battery', '{"color": "red", "capacity": 5500}'),
	('TBLTCASE-10', 'Tablet Case', '{"size": "10 inch", "material": "Leather"}')
;

下面的代码片断将假定已经建立了数据库连接。关于连接设置的更多信息,请参阅www.cockroachlabs.com/docs/v20.2/…

我们可以使用string[]byte 来读取或写入一个数据库jsonb 类型。然后我们可以使用encoding/json 包将其转换为我们的应用程序数据类型。然而,有了pgx,这就没有必要了。pgx可以自动地将数值marshal和unmarshal成JSON。

	type Product struct {
		SKU             string
		Name            string
		ExtraAttributes map[string]interface{}
	}

	// ...

	var product Product
	err = conn.QueryRow(
		context.Background(),
		"select * from products where sku = $1",
		"A1000-BK",
	).Scan(&product.SKU, &product.Name, &product.ExtraAttributes)
	if err != nil {
		// handle error
	}

	fmt.Printf("%#v\n", product.ExtraAttributes) // => map[string]interface {}{"capacity":4000, "color":"black"}

一个map[string]interface{} ,可以处理任何JSON对象,但在工作中可能有点尴尬。如果我们提前知道对象的结构,我们可以直接读入Go结构。

	type BatteryAttributes struct {
		Color    string `json:"color"`
		Capacity int32  `json:"capacity"`
	}

	type Battery struct {
		SKU             string
		Name            string
		ExtraAttributes BatteryAttributes
	}

	// ...

	err = conn.QueryRow(
		context.Background(),
		"select * from products where sku = $1",
		"A1000-BK",
	).Scan(&battery.SKU, &battery.Name, &battery.ExtraAttributes)
	if err != nil {
		// handle error
	}

	fmt.Printf("%#v\n", battery.ExtraAttributes)
  // => BatteryAttributes{Color:"black", Capacity:4000}

我们也可以直接从数据库中的关系数据建立JSON文档。例如,我们可以使用jsonb_aggjsonb_build_object 函数来建立一个列出所有产品的 JSON 文档。

	var buf []byte
	err = conn.QueryRow(
		context.Background(),
		"select jsonb_agg(jsonb_build_object('sku', sku, 'name', name)) from products",
	).Scan(&buf)
	if err != nil {
		// handle error
	}

	fmt.Println(string(buf))
  // => [{"name": "A1000 Battery", "sku": "A1000-BK"}, {"name": "A1200 Battery", "sku": "A1200-RD"}, {"name": "Tablet Case", "sku": "TBLTCASE-10"}]

这种方法既可以简化Go层,又可以提高性能。

总之,利用像CockroachDB这样的关系型数据库中的JSON功能可以减少对专用对象数据库的需求,最大限度地减少基础设施和应用的复杂性,并提高性能。