func (v *VideoModel) getConditions(include, exclude []string) []exp.Expression {
	queryCols := []string{"title", "tag", "description", "username"}
	f := func(terms []string, include bool) []exp.Expression {
		var incQuery []exp.Expression
		for _, term := range terms {
			var currConds []exp.Expression
			for _, col := range queryCols {
				patt := regexp.MustCompile(fmt.Sprintf(".*%s.*", term))
				if col == "tag" {
					t := goqu.Dialect("postgres").
						Select("videos.id").
						From(
							goqu.T("videos"),
						).Join(
						goqu.T("tags"),
						goqu.On(goqu.Ex{"videos.id": goqu.I("tags.video_id")})).
						Where(goqu.I("tag").Like(patt))
					var exp exp.Expression
					if include {
						exp = goqu.I("videos.id").In(t)
					} else {
						exp = goqu.I("videos.id").NotIn(t)
					}
					currConds = append(currConds, exp)
				} else if col == "username" {
					resp, err := v.grpcClient.GetUserIDsForUsername(context.Background(), &proto.GetUserIDsForUsernameRequest{
						Username: "%" + term + "%"})
					if err != nil || len(resp.UserIDs) == 0 {
						log.Errorf("could not retrieve user ids for username %s", term)
						continue
					}
					for _, id := range resp.UserIDs {
						if include {
							exp := goqu.I("userid").Eq(id)
							currConds = append(currConds, exp)
						} else {
							exp := goqu.I("userid").Neq(id)
							currConds = append(currConds, exp)
						}
					}
				} else {
					if include {
						exp := goqu.I(col).Like(patt)
						currConds = append(currConds, exp)
					} else {
						exp := goqu.I(col).NotLike(patt)
						currConds = append(currConds, exp)
					}
				}
			}
			if include {
				incQuery = append(incQuery, goqu.Or(currConds...))
			} else {
				incQuery = append(incQuery, goqu.And(currConds...))
			}
		}
		return incQuery
	}
	incConds := f(include, true)
	excConds := f(exclude, false)
	allConds := append(incConds, excConds...)
	return []exp.Expression{goqu.And(allConds...)}
}
This function is pretty horrifying, one of the worst things I've written
package models
import (
	"context"
	sql2 "database/sql"
	"fmt"
	"regexp"
	"strings"
	"github.com/go-redis/redis"
	log "github.com/sirupsen/logrus"
	"github.com/horahoradev/horahora/user_service/errors"
	serror "errors"
	"google.golang.org/grpc/status"
	"github.com/doug-martin/goqu/v9"
	_ "github.com/doug-martin/goqu/v9/dialect/postgres"
	"github.com/doug-martin/goqu/v9/exp"
	_ "github.com/horahoradev/horahora/user_service/protocol"
	proto "github.com/horahoradev/horahora/user_service/protocol"
	userproto "github.com/horahoradev/horahora/user_service/protocol"
	videoproto "github.com/horahoradev/horahora/video_service/protocol"
	"github.com/jmoiron/sqlx"
)
const (
	maxRating         = 10.00
	NumResultsPerPage = 50
	cdnURL            = "images.horahora.org"
)
type VideoModel struct {
	db *sqlx.DB
	grpcClient proto.UserServiceClient
	ApprovalThreshold int
	r                 Recommender
}
func NewVideoModel(db *sqlx.DB, client proto.UserServiceClient, redisClient *redis.Client, approvalThreshold int) (*VideoModel, error) {
	rec := NewBayesianTagSum(db)
	return &VideoModel{db: db,
		grpcClient: client,
		r:          &rec,
	}, nil
}
func (v *VideoModel) SaveForeignVideo(ctx context.Context, title, description string, foreignAuthorUsername string, foreignAuthorID string,
	originalSite string, originalVideoLink, originalVideoID, newURI string, tags []string, domesticAuthorID int64) (int64, error) {
	tx, err := v.db.BeginTx(ctx, nil)
	if err != nil {
		return 0, err
	}
	if foreignAuthorID == "" || foreignAuthorUsername == "" {
		return 0, serror.New("foreign author info cannot be blank")
	}
	if originalVideoLink == "" || originalVideoID == "" {
		return 0, serror.New("original video info cannot be blank")
	}
	req := proto.GetForeignUserRequest{
		OriginalWebsite: originalSite,
		ForeignUserID:   foreignAuthorID,
	}
	horahoraUID := domesticAuthorID
	if horahoraUID == 0 {
		resp, err := v.grpcClient.GetUserForForeignUID(ctx, &req)
		grpcErr, ok := status.FromError(err)
		if !ok {
			return 0, fmt.Errorf("could not parse gRPC err")
		}
		switch {
		case grpcErr.Message() == errors.UserDoesNotExistMessage:
			log.Infof("User %s does not exist for video %s, creating...", foreignAuthorUsername, originalVideoID)
			regReq := proto.RegisterRequest{
				Email:          "",
				Username:       foreignAuthorUsername,
				Password:       "",
				ForeignUser:    true,
				ForeignUserID:  foreignAuthorID,
				ForeignWebsite: originalSite,
			}
			regResp, err := v.grpcClient.Register(ctx, &regReq)
			if err != nil {
				return 0, err
			}
			validateReq := proto.ValidateJWTRequest{
				Jwt: regResp.Jwt,
			}
			validateResp, err := v.grpcClient.ValidateJWT(ctx, &validateReq)
			if err != nil {
				return 0, err
			}
			if !validateResp.IsValid {
				return 0, fmt.Errorf("jwt invalid (this should never happen!)")
			}
			horahoraUID = validateResp.Uid
		case err != nil:
			return 0, err
		case err == nil:
			horahoraUID = resp.NewUID
		}
	}
	sql := "INSERT INTO videos (title, description, userID, originalSite, " +
		"originalLink, newLink, originalID, upload_date) " +
		"VALUES ($1, $2, $3, $4, $5, $6, $7, Now())" +
		"returning id"
	var videoID int64
	res := tx.QueryRow(sql, title, description, horahoraUID, originalSite, originalVideoLink, newURI, originalVideoID)
	err = res.Scan(&videoID)
	if err != nil {
		tx.Rollback()
		return 0, err
	}
	tagSQL := "INSERT INTO tags (video_id, tag) VALUES ($1, $2)"
	for _, tag := range tags {
		_, err = tx.Exec(tagSQL, videoID, tag)
		if err != nil {
			tx.Rollback()
			return 0, err
		}
	}
	err = tx.Commit()
	if err != nil {
		return 0, err
	}
	return videoID, nil
}
func (v *VideoModel) ForeignVideoExists(foreignVideoID, website string) (bool, error) {
	sql := "SELECT id FROM videos WHERE originalSite=$1 AND originalID=$2"
	var videoID int64
	res := v.db.QueryRow(sql, website, foreignVideoID)
	err := res.Scan(&videoID)
	switch {
	case err == sql2.ErrNoRows:
		return false, nil
	case err != nil:
		return false, err
	default:  
		return true, nil
	}
}
func (v *VideoModel) IncrementViewsForVideo(videoID int64) error {
	sql := "UPDATE videos SET views = views + 1 WHERE id = $1"
	_, err := v.db.Exec(sql, videoID)
	if err != nil {
		return err
	}
	return nil
}
func (v *VideoModel) AddRatingToVideoID(ratingUID, videoID int64, ratingValue float64) error {
	sql := "INSERT INTO ratings (user_id, video_id, rating) VALUES ($1, $2, $3)" +
		"ON CONFLICT (user_id, video_id) DO update SET rating = $4"
	_, err := v.db.Exec(sql, ratingUID, videoID, ratingValue, ratingValue)
	if err != nil {
		return err
	}
	rating, err := v.GetAverageRatingForVideoID(videoID)
	if err != nil {
		return err
	}
	_, err = v.db.Exec("UPDATE videos SET rating = $1 WHERE id = $2", rating, videoID)
	if err != nil {
		return err
	}
	return nil
}
func (v *VideoModel) GetVideoList(direction videoproto.SortDirection, pageNum int64, fromUserID int64, searchVal string, showUnapproved bool,
	category videoproto.OrderCategory) ([]*videoproto.Video, error) {
	sql, err := v.generateVideoListSQL(direction, pageNum, fromUserID, searchVal, showUnapproved, category)
	if err != nil {
		return nil, err
	}
	var results []*videoproto.Video
	rows, err := v.db.Query(sql)
	if err != nil {
		return nil, err
	}
	for rows.Next() {
		var video videoproto.Video
		var authorID, views int64
		var mpdLoc string
		err = rows.Scan(&video.VideoID, &video.VideoTitle, &authorID, &mpdLoc, &views)
		if err != nil {
			return nil, err
		}
		basicInfo, err := v.getBasicVideoInfo(authorID, video.VideoID)
		if err != nil {
			return nil, err
		}
		video.Rating = basicInfo.rating
		video.AuthorName = basicInfo.authorName
		video.Views = uint64(views)
		video.ThumbnailLoc = strings.Replace(mpdLoc, ".mpd", ".thumb", 1)
		results = append(results, &video)
	}
	return results, nil
}
func (v *VideoModel) GetNumberOfSearchResultsForQuery(fromUserID int64, searchVal string) (int64, error) {
	var sql string
	var args []interface{}
	switch {
	case fromUserID != 0:
		sql = "SELECT COUNT(*) FROM videos WHERE userID = $1 AND transcoded=true"
		args = []interface{}{fromUserID}
	case searchVal != "":
		dialect := goqu.Dialect("postgres")
		ds := dialect.
			Select(goqu.COUNT(goqu.DISTINCT("videos.id"))).
			From(
				goqu.T("videos"),
			)
		conditions := v.getConditions(extractSearchTerms(searchVal))
		ds = ds.LeftJoin(
			goqu.T("tags"),
			goqu.On(goqu.Ex{"videos.id": goqu.I("tags.video_id")})).
			Where(conditions...).
			Where(goqu.C("transcoded").Eq(true))
		var err error
		sql, _, err = ds.ToSQL()
		if err != nil {
			return 0, err
		}
		args = []interface{}{}
	default:
		sql = "SELECT COUNT(*) FROM videos WHERE transcoded=true"
		args = []interface{}{}
	}
	rows, err := v.db.Query(sql, args...)
	if err != nil {
		return 0, err
	}
	var l int64
	for rows.Next() {
		err = rows.Scan(&l)
		if err != nil {
			return 0, err
		}
	}
	return l, nil
}
func (v *VideoModel) generateVideoListSQL(direction videoproto.SortDirection, pageNum, fromUserID int64, searchVal string, showUnapproved bool, orderCategory videoproto.OrderCategory) (string, error) {
	minResultNum := (pageNum - 1) * NumResultsPerPage
	dialect := goqu.Dialect("postgres")
	ds := dialect.
		Select("videos.id", "title", "userid", "newlink", "views").
		From(
			goqu.T("videos"),
		).
		Offset(uint(minResultNum)).
		Limit(NumResultsPerPage)
	switch orderCategory {
	case videoproto.OrderCategory_upload_date:
		switch direction {
		case videoproto.SortDirection_asc:
			ds = ds.Order(goqu.I("upload_date").Asc())
		case videoproto.SortDirection_desc:
			ds = ds.Order(goqu.I("upload_date").Desc())
		}
	case videoproto.OrderCategory_views:
		switch direction {
		case videoproto.SortDirection_asc:
			ds = ds.Order(goqu.I("views").Asc())
		case videoproto.SortDirection_desc:
			ds = ds.Order(goqu.I("views").Desc())
		}
	case videoproto.OrderCategory_rating:
		switch direction {
		case videoproto.SortDirection_asc:
			ds = ds.Order(goqu.I("rating").Asc())
		case videoproto.SortDirection_desc:
			ds = ds.Order(goqu.I("rating").Desc())
		}
	}
	switch {
	case fromUserID != 0:
		ds = ds.
			Where(goqu.C("userid").Eq(fromUserID))
	case searchVal != "":
		conditions := v.getConditions(extractSearchTerms(searchVal))
		ds = ds.LeftJoin(
			goqu.T("tags"),
			goqu.On(goqu.Ex{"videos.id": goqu.I("tags.video_id")})).
			Where(conditions...)
	}
	if !showUnapproved {
		ds = ds.Where(goqu.C("is_approved").Eq(true))
	}
	ds = ds.Where(goqu.C("transcoded").Eq(true))
	ds = ds.GroupBy(goqu.I("videos.id"))
	sql, _, err := ds.ToSQL()
	return sql, err
}