You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: querybuilder.go
    path of file: ./repos/highlight/backend/clickhouse
    
    the code of the file until where you have to start completion: package clickhouse

import (
	"errors"
	"fmt"
	"math"
	"strconv"
	"strings"
	"time"

	"github.com/highlight-run/highlight/backend/model"
	modelInputs "github.com/highlight-run/highlight/backend/private-graph/graph/model"
	"github.com/huandu/go-sqlbuilder"
)

type Operator string

const (
	Is             Operator = "is"
	Contains       Operator = "contains"
	Exists         Operator = "exists"
	Between        Operator = "between"
	BetweenTime    Operator = "between_time"
	BetweenDate    Operator = "between_date"
	Matches        Operator = "matches"
	IsNot          Operator = "is_not"
	NotContains    Operator = "not_contains"
	NotExists      Operator = "not_exists"
	NotBetween     Operator = "not_between"
	NotBetweenTime Operator = "not_between_time"
	NotBetweenDate Operator = "not_between_date"
	NotMatches     Operator = "not_matches"
)

var negationMap map[Operator]Operator = map[Operator]Operator{
	IsNot:          Is,
	NotContains:    Contains,
	NotExists:      Exists,
	NotBetween:     Between,
	NotBetweenTime: BetweenTime,
	NotBetweenDate: BetweenDate,
	NotMatches:     Matches,
}

type Rule struct {
	Field string
	Op    Operator
	Val   []string
}

func deserializeRules(
    
func parseColumnRule(admin *model.Admin, rule Rule, projectId int, sb *sqlbuilder.SelectBuilder) (string, error) {
	negatedOp, isNegative := negationMap[rule.Op]
	if isNegative {
		child, err := parseColumnRule(admin, Rule{
			Field: rule.Field,
			Op:    negatedOp,
			Val:   rule.Val,
		}, projectId, sb)
		if err != nil {
			return "", err
		}
		return fmt.Sprintf("NOT %s", child), nil
	}

	_, name, found := strings.Cut(rule.Field, "_")
	if !found {
		return "", fmt.Errorf("separator not found for field %s", rule.Field)
	}

	customFieldType, found := customFieldTypes[name]
	if !found {
		customFieldType = text
	}

	mappedName, found := fieldMap[name]
	if !found {
		return "", fmt.Errorf("unknown column %s", name)
	}

	if rule.Op == Exists {
		return sb.IsNotNull(mappedName), nil
	}

	conditions := []string{}
	for _, v := range rule.Val {
		switch rule.Op {
		case Is:
			switch customFieldType {
			case text:
				conditions = append(conditions, fmt.Sprintf(`"%s" ILIKE %s`, mappedName, sb.Var(v)))
			case boolean:
				val, err := strconv.ParseBool(v)
				if err != nil {
					return "", err
				}
				conditions = append(conditions, sb.Equal(mappedName, val))
			case long:
				val, err := strconv.ParseInt(v, 10, 64)
				if err != nil {
					return "", err
				}
				conditions = append(conditions, sb.Equal(mappedName, val))
			case viewedByMe:
				// If no current admin, return false for the "viewed by me" filter
				if admin == nil {
					conditions = append(conditions, "FALSE")
				} else {
					switch v {
					case "true":
						conditions = append(conditions, fmt.Sprintf(`has("%s", %d)`, mappedName, admin.ID))
					case "false":
						conditions = append(conditions, fmt.Sprintf(`NOT has("%s", %d)`, mappedName, admin.ID))
					default:
						return "", fmt.Errorf("unsupported value for viewed_by_me: %s", v)
					}
				}
			default:
				return "", fmt.Errorf("unsupported custom field type %s", customFieldType)
			}
		case Contains:
			conditions = append(conditions, fmt.Sprintf(`"%s" ILIKE %s`, mappedName, sb.Var("%"+v+"%")))
		case Between:
			before, after, found := strings.Cut(v, "_")
			if !found {
				return "", fmt.Errorf("separator not found for between query %s", v)
			}
			start, err := strconv.ParseInt(before, 10, 64)
			if err != nil {
				return "", err
			}
			end, err := strconv.ParseInt(after, 10, 64)
			if err != nil {
				return "", err
			}
			conditions = append(conditions, sb.Between(mappedName, start, end))
		case BetweenTime:
			before, after, found := strings.Cut(v, "_")
			if !found {
				return "", fmt.Errorf("separator not found for between query %s", v)
			}
			start, err := strconv.ParseFloat(before, 64)
			if err != nil {
				return "", err
			}
			start *= 60000
			end, err := strconv.ParseFloat(after, 64)
			if err != nil {
				return "", err
			}
			end *= 60000
			// If the slider is at the maximum, allow any length length greater than the min
			if after == "60" {
				end = math.Inf(1)
			}
			conditions = append(conditions, sb.Between(mappedName, start, end))
		case BetweenDate:
			before, after, found := strings.Cut(v, "_")
			if !found {
				return "", fmt.Errorf("separator not found for between query %s", v)
			}
			startTime, err := time.Parse(timeFormat, before)
			if err != nil {
				return "", err
			}
			endTime, err := time.Parse(timeFormat, after)
			if err != nil {
				return "", err
			}
			conditions = append(conditions, sb.Between(mappedName, startTime, endTime))
		case Matches:
			conditions = append(conditions, fmt.Sprintf(`"%s" REGEXP %s`, sb.Var(mappedName), sb.Var(v)))
		default:
			return "", fmt.Errorf("unsupported operator %s", rule.Op)
		}
	}

	return sb.Or(conditions...), nil
}