Browse Source

clickhouse

- refactoring
Alexey Kim 2 years ago
parent
commit
9f4df70255
3 changed files with 95 additions and 109 deletions
  1. 17 11
      cache/sql.go
  2. 17 17
      db/clickhouse.go
  3. 61 81
      db/product.go

+ 17 - 11
cache/sql.go

@@ -8,23 +8,25 @@ import (
 )
 
 type SqlKey struct {
-	format string
-	ttl    time.Duration
-	table  string
-	clause string
-	args   []any
+	format    string
+	ttl       time.Duration
+	table     string
+	selection []string
+	clause    string
+	args      []any
 }
 
-func NewSQLKey(tName string, t time.Duration, clause string, args ...any) *SqlKey {
+func NewSQLKey(tName string, t time.Duration, selection []string, clause string, args ...any) *SqlKey {
 	h := fnv.New32a()
 	_, _ = h.Write([]byte(tName))
 
 	return &SqlKey{
-		format: fmt.Sprintf("sql_%s_%s", strconv.Itoa(int(h.Sum32())), "%v"),
-		ttl:    t,
-		table:  tName,
-		clause: clause,
-		args:   args,
+		format:    fmt.Sprintf("sql_%s_%s_%v", strconv.Itoa(int(h.Sum32())), "%v", selection),
+		ttl:       t,
+		table:     tName,
+		selection: selection,
+		clause:    clause,
+		args:      args,
 	}
 }
 
@@ -32,6 +34,10 @@ func (k *SqlKey) Table() string {
 	return k.table
 }
 
+func (k *SqlKey) Selection() []string {
+	return k.selection
+}
+
 func (k *SqlKey) Clause() string {
 	return k.clause
 }

+ 17 - 17
db/clickhouse.go

@@ -98,11 +98,11 @@ func (db *clickhouse) Collection(ln model.LanguageCode, handle *string, id *stri
 		vars = append(vars, *handle)
 	}
 
-	key = productCollectionKey(clause.String(), vars...)
+	key = productCollectionKey(ln, clause.String(), vars...)
 	loader = func(ttl *ttlcache.Cache[string, any], _ string) *ttlcache.Item[string, any] {
 		var o relation.ProductCollection
 		rows, err := db.session.
-			Select(productCollectionSelection(ln)...).
+			Select(key.Selection()...).
 			From(key.Table()).
 			Where(key.Clause(), key.Args()...).
 			Limit(1).
@@ -126,12 +126,12 @@ func (db *clickhouse) Collection(ln model.LanguageCode, handle *string, id *stri
 func (db *clickhouse) Collections(ln model.LanguageCode) ([]*generated.Collection, error) {
 	var (
 		collections []*generated.Collection
-		key         = productCollectionKey("list")
+		key         = productCollectionKey(ln, "list")
 		l           = ttlcache.LoaderFunc[string, any](
 			func(ttl *ttlcache.Cache[string, any], _ string) *ttlcache.Item[string, any] {
 				var o []relation.ProductCollection
 				rows, err := db.session.
-					Select(productCollectionSelection(ln)...).
+					Select(key.Selection()...).
 					From(key.Table()).
 					Load(&o)
 				if rows < 1 || err != nil {
@@ -155,12 +155,12 @@ func (db *clickhouse) Collections(ln model.LanguageCode) ([]*generated.Collectio
 func (db *clickhouse) ProductCollections(ln model.LanguageCode, id string) ([]*generated.Collection, error) {
 	var (
 		collections []*generated.Collection
-		key         = productCollectionKey("product.id=?", id)
+		key         = productCollectionKey(ln, "product.id=?", id)
 		l           = ttlcache.LoaderFunc[string, any](
 			func(ttl *ttlcache.Cache[string, any], _ string) *ttlcache.Item[string, any] {
 				var o []relation.ProductCollection
 				rows, err := db.session.SelectBySql("SELECT "+
-					strings.Join(productCollectionSelection(ln), ", ")+
+					strings.Join(key.Selection(), ", ")+
 					" FROM `"+key.Table()+"`"+
 					" ARRAY JOIN (SELECT `collections` FROM `product` WHERE `id` = ?) AS cid"+
 					" WHERE `id` = cid", key.Args()...).
@@ -201,12 +201,12 @@ func (db *clickhouse) Product(ln model.LanguageCode, handle *string, id *string)
 	}
 
 	var (
-		key = productKey(clause.String(), vars...)
+		key = productKey(ln, defaultCurrency, clause.String(), vars...)
 		l   = ttlcache.LoaderFunc[string, any](
 			func(ttl *ttlcache.Cache[string, any], _ string) *ttlcache.Item[string, any] {
 				o := relation.Product{}
 				rows, err := db.session.
-					Select(productSelection(ln, defaultCurrency)...).
+					Select(key.Selection()...).
 					From(fmt.Sprintf("%s as t", key.Table())).
 					LeftJoin("product_variant", "product_variant.product_id = t.id").
 					LeftJoin("inventory_item", "inventory_item.id = product_variant.inventory_item_id").
@@ -235,12 +235,12 @@ func (db *clickhouse) Product(ln model.LanguageCode, handle *string, id *string)
 func (db *clickhouse) ProductOptions(ln model.LanguageCode, id string) ([]*generated.ProductOption, error) {
 	var (
 		options []*generated.ProductOption
-		key     = productOptionKey("product_id=?", id)
+		key     = productOptionKey(ln, "product_id=?", id)
 		l       = ttlcache.LoaderFunc[string, any](
 			func(ttl *ttlcache.Cache[string, any], _ string) *ttlcache.Item[string, any] {
 				var o []relation.ProductOption
 				rows, err := db.session.SelectBySql("SELECT "+
-					strings.Join(productOptionSelection(ln), ", ")+
+					strings.Join(key.Selection(), ", ")+
 					" FROM `"+key.Table()+"`"+
 					" ARRAY JOIN (SELECT `options` FROM `product` WHERE `id` = ?) AS oid"+
 					" WHERE `id` = oid", key.Args()).
@@ -266,12 +266,12 @@ func (db *clickhouse) ProductOptions(ln model.LanguageCode, id string) ([]*gener
 func (db *clickhouse) ProductVariants(ctx *middleware.GShopifyContext, id string) ([]*generated.ProductVariant, error) {
 	var (
 		variants []*generated.ProductVariant
-		key      = productVariantKey("t.product_id=?", id)
+		key      = productVariantKey(ctx.Language, defaultCurrency, "t.product_id=?", id)
 		l        = ttlcache.LoaderFunc[string, any](
 			func(ttl *ttlcache.Cache[string, any], _ string) *ttlcache.Item[string, any] {
 				var o []relation.ProductVariant
 				rows, err := db.session.
-					Select(productVariantSelection(ctx.Language, defaultCurrency)...).
+					Select(key.Selection()...).
 					From(fmt.Sprintf("%s as t", key.Table())).
 					LeftJoin("product", "product.id = t.product_id").
 					LeftJoin("inventory_item", "inventory_item.id = t.inventory_item_id").
@@ -302,12 +302,12 @@ func (db *clickhouse) ProductVariants(ctx *middleware.GShopifyContext, id string
 func (db *clickhouse) CollectionProducts(ln model.LanguageCode, id string) ([]*generated.Product, error) {
 	var (
 		products []*generated.Product
-		key      = productKey("has(t.collections, ?) AND t.status = ?", id, model.ProductStatusActive)
+		key      = productKey(ln, defaultCurrency, "has(t.collections, ?) AND t.status = ?", id, model.ProductStatusActive)
 		l        = ttlcache.LoaderFunc[string, any](
 			func(ttl *ttlcache.Cache[string, any], _ string) *ttlcache.Item[string, any] {
 				var o []relation.Product
 				rows, err := db.session.
-					Select(productSelection(ln, defaultCurrency)...).
+					Select(key.Selection()...).
 					From(fmt.Sprintf("%s as t", key.Table())).
 					LeftJoin("product_variant", "product_variant.product_id = t.id").
 					LeftJoin("inventory_item", "inventory_item.id = product_variant.inventory_item_id").
@@ -339,14 +339,14 @@ func (db *clickhouse) CollectionProducts(ln model.LanguageCode, id string) ([]*g
 func (db *clickhouse) ProductVariantOptions(ln model.LanguageCode, id string) ([]*generated.SelectedOption, error) {
 	var (
 		options []*generated.SelectedOption
-		key     = productOptionKey("product_variant.id = ?", id)
+		key     = productOptionKey(ln, "product_variant.id = ?", id)
 		l       = ttlcache.LoaderFunc[string, any](
 			func(ttl *ttlcache.Cache[string, any], _ string) *ttlcache.Item[string, any] {
 				var o []relation.ProductOption
 				rows, err := db.session.SelectBySql("SELECT "+
-					strings.Join(productOptionSelectedSelection(ln), ", ")+
+					strings.Join(key.Selection(), ", ")+
 					" FROM `"+key.Table()+"`"+
-					" ARRAY JOIN (SELECT `options` from `product_variant` where `id` = ?) as opt"+
+					" ARRAY JOIN (SELECT anyLast(options) as options from `product_variant` where `id` = ? GROUP BY id) as opt"+
 					" WHERE id = tupleElement(opt, 1)"+
 					" ORDER BY position", key.Args()).
 					Load(&o)

+ 61 - 81
db/product.go

@@ -11,107 +11,87 @@ import (
 const defaultCurrency = "USD"
 
 var (
-	productKey = func(clause string, args ...any) *cache.SqlKey {
+	productKey = func(ln model.LanguageCode, currency generated.CurrencyCode, clause string, args ...any) *cache.SqlKey {
 		return cache.NewSQLKey(
 			"product",
 			time.Minute,
+			[]string{
+				"t.id as id",
+				"anyLast(handle) as handle", "anyLast(type) as type", "anyLast(scope) as scope", "anyLast(tags) as tags", "anyLast(vendor) as vendor",
+				"anyLast(gift) as gift", "anyLast(collections) as collections", "anyLast(images) as images",
+				"anyLast(product_variant.inventory_management) as inventory_management",
+				"anyLast(product_variant.inventory_policy) as inventory_policy",
+				"anyLast(product_variant.options) as options",
+				"anyLast(inventory_item.requires_shipping) as requires_shipping",
+				"anyLast(inventory_item.tracked) as tracked",
+				"anyLast(inventory_level.available) as available",
+				"if(tracked, if(available > 0, true, if(inventory_policy == 'continue', true, false)), true) as for_sale",
+				"anyLast(created_at) as created_at", "anyLast(updated_at) as updated_at", "anyLast(published_at) as published_at", "anyLast(deleted_at) as deleted_at",
+				ln.SqlFieldSelection("title", "", "anyLast"),
+				ln.SqlFieldSelection("description", "", "anyLast"),
+				fmt.Sprintf("min(product_variant.price['%s']) as price_min", currency),
+				fmt.Sprintf("max(product_variant.price['%s']) as price_max", currency),
+				fmt.Sprintf("min(product_variant.compare_at_price['%s']) as compare_at_price_min", currency),
+				fmt.Sprintf("max(product_variant.compare_at_price['%s']) as compare_at_price_max", currency),
+			},
 			clause, args...)
 	}
-	productSelection = func(ln model.LanguageCode, currency generated.CurrencyCode) []string {
-		return append([]string{
-			"t.id as id",
-			"any(handle) as handle", "any(type) as type", "any(scope) as scope", "any(tags) as tags", "any(vendor) as vendor",
-			"any(gift) as gift", "any(collections) as collections", "any(images) as images",
-			"any(product_variant.inventory_management) as inventory_management",
-			"any(product_variant.inventory_policy) as inventory_policy",
-			"any(product_variant.options) as options",
-			"any(inventory_item.requires_shipping) as requires_shipping",
-			"any(inventory_item.tracked) as tracked",
-			"any(inventory_level.available) as available",
-			"if(tracked, if(available > 0, true, if(inventory_policy == 'continue', true, false)), true) as for_sale",
-			"any(created_at) as created_at", "any(updated_at) as updated_at", "any(published_at) as published_at", "any(deleted_at) as deleted_at",
-		},
-			ln.SqlFieldSelection("title", "", "any"),
-			ln.SqlFieldSelection("description", "", "any"),
-			fmt.Sprintf("min(product_variant.price['%s']) as price_min", currency),
-			fmt.Sprintf("max(product_variant.price['%s']) as price_max", currency),
-			fmt.Sprintf("min(product_variant.compare_at_price['%s']) as compare_at_price_min", currency),
-			fmt.Sprintf("max(product_variant.compare_at_price['%s']) as compare_at_price_max", currency),
-		)
-	}
-	productCollectionSelection = func(ln model.LanguageCode) []string {
-		return append([]string{
-			"id", "handle", "scope", "sort_order", "image", "template_suffix",
-			"created_at", "updated_at", "published_at", "deleted_at",
-		},
-			ln.SqlFieldSelection("title", "", ""),
-			ln.SqlFieldSelection("description", "", ""),
-		)
-	}
-	productCollectionKey = func(clause string, args ...any) *cache.SqlKey {
+	productCollectionKey = func(ln model.LanguageCode, clause string, args ...any) *cache.SqlKey {
 		return cache.NewSQLKey(
 			"product_collection",
 			3*time.Minute,
+			[]string{
+				"id", "handle", "scope", "sort_order", "image", "template_suffix",
+				"created_at", "updated_at", "published_at", "deleted_at",
+				ln.SqlFieldSelection("title", "", ""),
+				ln.SqlFieldSelection("description", "", ""),
+			},
 			clause,
 			args...,
 		)
 	}
-	productOptionSelection = func(ln model.LanguageCode) []string {
-		return append([]string{"id", "position", "created_at", "updated_at", "published_at", "deleted_at"},
-			ln.SqlFieldSelection("name", "", ""),
-			ln.SqlArraySelection("values"),
-		)
-	}
-	productOptionSelectedSelection = func(ln model.LanguageCode) []string {
-		return append([]string{
-			"id",
-			"if(notEmpty(value_ln), value_ln, value_en) as value",
-			"position", "created_at", "updated_at", "published_at", "deleted_at",
-		},
-			ln.SqlFieldSelection("name", "", ""),
-			fmt.Sprintf("arrayElement(values, tupleElement(opt, 2))['%s'] as value_en", model.LanguageCodeEn),
-			fmt.Sprintf("arrayElement(values, tupleElement(opt, 2))['%s'] as value_ln", ln),
-		)
-	}
-	productOptionKey = func(clause string, args ...any) *cache.SqlKey {
+	productOptionKey = func(ln model.LanguageCode, clause string, args ...any) *cache.SqlKey {
 		return cache.NewSQLKey(
 			"product_option",
 			time.Minute,
+			[]string{
+				"id", "position", "created_at", "updated_at", "published_at", "deleted_at",
+				ln.SqlFieldSelection("name", "", ""),
+				ln.SqlArraySelection("values"),
+				"values[tupleElement(opt, 2)] as value",
+			},
 			clause, args...)
 	}
-	productVariantSelection = func(ln model.LanguageCode, currency generated.CurrencyCode) []string {
-		return append([]string{
-			"any(t.id) as id",
-			"any(t.product_id) as product_id",
-			"any(t.inventory_item_id) as inventory_item_id",
-			"any(t.position) as position",
-			"any(t.image) as image",
-			"any(t.inventory_management) as inventory_management",
-			"any(t.inventory_policy) as inventory_policy",
-			"any(t.options) as options",
-			"any(t.grams) as grams",
-			"any(t.weight) as weight",
-			"any(t.weight_unit) as weight_unit",
-			"any(t.created_at) as created_at",
-			"any(t.updated_at) as updated_at",
-			"any(t.published_at) as published_at",
-			"any(t.deleted_at) as deleted_at",
-			"sum(inventory_level.available) as available",
-			"any(inventory_item.sku) as sku",
-			"any(inventory_item.barcode) as barcode",
-			"any(inventory_item.requires_shipping) as requires_shipping",
-			"any(inventory_item.tracked) as tracked",
-			"if(tracked, if(available > 0, true, if(inventory_policy == 'continue', true, false)), true) as for_sale",
-		},
-			ln.SqlFieldSelection("title", "product", "any"),
-			fmt.Sprintf("any(t.price['%s']) as price", currency),
-			fmt.Sprintf("any(t.compare_at_price['%s']) as compare_at_price", currency),
-		)
-	}
-	productVariantKey = func(clause string, args ...any) *cache.SqlKey {
+	productVariantKey = func(ln model.LanguageCode, currency generated.CurrencyCode, clause string, args ...any) *cache.SqlKey {
 		return cache.NewSQLKey(
 			"product_variant",
 			time.Minute,
+			[]string{
+				"anyLast(t.id) as id",
+				"anyLast(t.product_id) as product_id",
+				"anyLast(t.inventory_item_id) as inventory_item_id",
+				"anyLast(t.position) as position",
+				"anyLast(t.image) as image",
+				"anyLast(t.inventory_management) as inventory_management",
+				"anyLast(t.inventory_policy) as inventory_policy",
+				"anyLast(t.options) as options",
+				"anyLast(t.grams) as grams",
+				"anyLast(t.weight) as weight",
+				"anyLast(t.weight_unit) as weight_unit",
+				"anyLast(t.created_at) as created_at",
+				"anyLast(t.updated_at) as updated_at",
+				"anyLast(t.published_at) as published_at",
+				"anyLast(t.deleted_at) as deleted_at",
+				"sum(inventory_level.available) as available",
+				"anyLast(inventory_item.sku) as sku",
+				"anyLast(inventory_item.barcode) as barcode",
+				"anyLast(inventory_item.requires_shipping) as requires_shipping",
+				"anyLast(inventory_item.tracked) as tracked",
+				"if(tracked, if(available > 0, true, if(inventory_policy == 'continue', true, false)), true) as for_sale",
+				ln.SqlFieldSelection("title", "product", "anyLast"),
+				fmt.Sprintf("any(t.price['%s']) as price", currency),
+				fmt.Sprintf("any(t.compare_at_price['%s']) as compare_at_price", currency),
+			},
 			clause, args...)
 	}
 )