mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2025-12-06 22:36:23 +00:00
128 lines
4.3 KiB
Go
128 lines
4.3 KiB
Go
package cache
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/bitechdev/ResolveSpec/pkg/common"
|
|
)
|
|
|
|
// QueryCacheKey represents the components used to build a cache key for query total count
|
|
type QueryCacheKey struct {
|
|
TableName string `json:"table_name"`
|
|
Filters []common.FilterOption `json:"filters"`
|
|
Sort []common.SortOption `json:"sort"`
|
|
CustomSQLWhere string `json:"custom_sql_where,omitempty"`
|
|
CustomSQLOr string `json:"custom_sql_or,omitempty"`
|
|
Expand []ExpandOptionKey `json:"expand,omitempty"`
|
|
Distinct bool `json:"distinct,omitempty"`
|
|
CursorForward string `json:"cursor_forward,omitempty"`
|
|
CursorBackward string `json:"cursor_backward,omitempty"`
|
|
}
|
|
|
|
// ExpandOptionKey represents expand options for cache key
|
|
type ExpandOptionKey struct {
|
|
Relation string `json:"relation"`
|
|
Where string `json:"where,omitempty"`
|
|
}
|
|
|
|
// BuildQueryCacheKey builds a cache key from query parameters for total count caching
|
|
// This is used to cache the total count of records matching a query
|
|
func BuildQueryCacheKey(tableName string, filters []common.FilterOption, sort []common.SortOption, customWhere, customOr string) string {
|
|
key := QueryCacheKey{
|
|
TableName: tableName,
|
|
Filters: filters,
|
|
Sort: sort,
|
|
CustomSQLWhere: customWhere,
|
|
CustomSQLOr: customOr,
|
|
}
|
|
|
|
// Serialize to JSON for consistent hashing
|
|
jsonData, err := json.Marshal(key)
|
|
if err != nil {
|
|
// Fallback to simple string concatenation if JSON fails
|
|
return hashString(fmt.Sprintf("%s_%v_%v_%s_%s", tableName, filters, sort, customWhere, customOr))
|
|
}
|
|
|
|
return hashString(string(jsonData))
|
|
}
|
|
|
|
// BuildExtendedQueryCacheKey builds a cache key for extended query options (restheadspec)
|
|
// Includes expand, distinct, and cursor pagination options
|
|
func BuildExtendedQueryCacheKey(tableName string, filters []common.FilterOption, sort []common.SortOption,
|
|
customWhere, customOr string, expandOpts []interface{}, distinct bool, cursorFwd, cursorBwd string) string {
|
|
|
|
key := QueryCacheKey{
|
|
TableName: tableName,
|
|
Filters: filters,
|
|
Sort: sort,
|
|
CustomSQLWhere: customWhere,
|
|
CustomSQLOr: customOr,
|
|
Distinct: distinct,
|
|
CursorForward: cursorFwd,
|
|
CursorBackward: cursorBwd,
|
|
}
|
|
|
|
// Convert expand options to cache key format
|
|
if len(expandOpts) > 0 {
|
|
key.Expand = make([]ExpandOptionKey, 0, len(expandOpts))
|
|
for _, exp := range expandOpts {
|
|
// Type assert to get the expand option fields we care about for caching
|
|
if expMap, ok := exp.(map[string]interface{}); ok {
|
|
expKey := ExpandOptionKey{}
|
|
if rel, ok := expMap["relation"].(string); ok {
|
|
expKey.Relation = rel
|
|
}
|
|
if where, ok := expMap["where"].(string); ok {
|
|
expKey.Where = where
|
|
}
|
|
key.Expand = append(key.Expand, expKey)
|
|
}
|
|
}
|
|
// Sort expand options for consistent hashing (already sorted by relation name above)
|
|
}
|
|
|
|
// Serialize to JSON for consistent hashing
|
|
jsonData, err := json.Marshal(key)
|
|
if err != nil {
|
|
// Fallback to simple string concatenation if JSON fails
|
|
return hashString(fmt.Sprintf("%s_%v_%v_%s_%s_%v_%v_%s_%s",
|
|
tableName, filters, sort, customWhere, customOr, expandOpts, distinct, cursorFwd, cursorBwd))
|
|
}
|
|
|
|
return hashString(string(jsonData))
|
|
}
|
|
|
|
// hashString computes SHA256 hash of a string
|
|
func hashString(s string) string {
|
|
h := sha256.New()
|
|
h.Write([]byte(s))
|
|
return hex.EncodeToString(h.Sum(nil))
|
|
}
|
|
|
|
// GetQueryTotalCacheKey returns a formatted cache key for storing/retrieving total count
|
|
func GetQueryTotalCacheKey(hash string) string {
|
|
return fmt.Sprintf("query_total:%s", hash)
|
|
}
|
|
|
|
// CachedTotal represents a cached total count
|
|
type CachedTotal struct {
|
|
Total int `json:"total"`
|
|
}
|
|
|
|
// InvalidateCacheForTable removes all cached totals for a specific table
|
|
// This should be called when data in the table changes (insert/update/delete)
|
|
func InvalidateCacheForTable(ctx context.Context, tableName string) error {
|
|
cache := GetDefaultCache()
|
|
|
|
// Build a pattern to match all query totals for this table
|
|
// Note: This requires pattern matching support in the provider
|
|
pattern := fmt.Sprintf("query_total:*%s*", strings.ToLower(tableName))
|
|
|
|
return cache.DeleteByPattern(ctx, pattern)
|
|
}
|