in src/psearch/serving/internal/services/spanner_service.go [85:141]
func (s *SpannerService) GetProductsBatch(ctx context.Context, productIDs []string) (map[string]map[string]interface{}, error) {
if len(productIDs) == 0 {
return make(map[string]map[string]interface{}), nil
}
startTime := time.Now()
// Create a SQL statement with UNNEST to handle large number of product IDs
stmt := spanner.Statement{
SQL: `SELECT product_id, product_data
FROM products
WHERE product_id IN UNNEST(@product_ids)`,
Params: map[string]interface{}{
"product_ids": productIDs,
},
}
resultMap := make(map[string]map[string]interface{})
// Execute the query
iter := s.client.Single().Query(ctx, stmt)
defer iter.Stop()
for {
row, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
return nil, fmt.Errorf("error iterating through query results: %v", err)
}
var productID string
var productDataJSON spanner.NullJSON
if err := row.Columns(&productID, &productDataJSON); err != nil {
return nil, fmt.Errorf("failed to scan columns: %v", err)
}
if productDataJSON.Valid {
// Type assert productDataJSON.Value directly to map[string]interface{}
productData, ok := productDataJSON.Value.(map[string]interface{})
if !ok {
// Log the actual type if the assertion fails
log.Printf("DEBUG: Unexpected type for productDataJSON.Value: %T", productDataJSON.Value)
return nil, fmt.Errorf("failed to type assert product data from NullJSON.Value")
}
resultMap[productID] = productData
}
}
elapsed := time.Since(startTime)
log.Printf("Spanner batch fetch for %d products took %s, retrieved %d",
len(productIDs), elapsed, len(resultMap))
return resultMap, nil
}