分页查询

This commit is contained in:
2024-11-05 18:42:54 +08:00
parent 13ec042ddf
commit da6913eaba
7 changed files with 178 additions and 162 deletions

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"log"
"reflect"
"strconv"
"strings"
"git.satori.love/gameui/webp/models"
@@ -11,6 +12,7 @@ import (
"github.com/graphql-go/graphql/language/ast"
"github.com/jmoiron/sqlx"
"github.com/mitchellh/mapstructure"
"github.com/thoas/go-funk"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
@@ -97,12 +99,13 @@ func NewSchema(config Config) (graphql.Schema, error) {
Name: "Article",
Description: "文章",
Fields: graphql.Fields{
"id": &graphql.Field{Type: graphql.Int, Description: "文章ID"},
"title": &graphql.Field{Type: graphql.String, Description: "文章标题"},
"tags": &graphql.Field{Type: graphql.String, Description: "文章标签"},
"user": &graphql.Field{Type: user, Description: "文章所属用户"},
"create_time": &graphql.Field{Type: graphql.DateTime, Description: "文章创建时间"},
"update_time": &graphql.Field{Type: graphql.DateTime, Description: "文章更新时间"},
"id": &graphql.Field{Type: graphql.Int, Description: "ID"},
"title": &graphql.Field{Type: graphql.String, Description: "标题"},
"orientation": &graphql.Field{Type: graphql.String, Description: "方向"},
"tags": &graphql.Field{Type: graphql.String, Description: "标签"},
"user": &graphql.Field{Type: user, Description: "所属用户"},
"create_time": &graphql.Field{Type: graphql.DateTime, Description: "创建时间"},
"update_time": &graphql.Field{Type: graphql.DateTime, Description: "更新时间"},
},
})
@@ -167,7 +170,6 @@ func NewSchema(config Config) (graphql.Schema, error) {
if p.Args["text"] != nil {
var texts TextList
for _, text := range p.Source.(Image).Text {
fmt.Println("san", text.Text)
if strings.Contains(text.Text, p.Args["text"].(string)) {
texts = append(texts, text)
}
@@ -193,18 +195,6 @@ func NewSchema(config Config) (graphql.Schema, error) {
for _, field := range fieldAST.SelectionSet.Selections {
fieldAST, ok := field.(*ast.Field)
if ok {
if fieldAST.Name.Value == "user" {
fields = append(fields, "user_id")
continue
}
if fieldAST.Name.Value == "article" {
fields = append(fields, "article_id")
continue
}
if fieldAST.Name.Value == "similars" {
// 跳过自定义字段
continue
}
fields = append(fields, fieldAST.Name.Value)
}
}
@@ -344,7 +334,7 @@ func NewSchema(config Config) (graphql.Schema, error) {
},
}),
Args: graphql.FieldConfigArgument{
"preference": &graphql.ArgumentConfig{Type: graphql.String, Description: "使用浏览记录获取的偏好推荐图像"},
"interest": &graphql.ArgumentConfig{Type: graphql.Int, Description: "获取指定ID用户的兴趣推荐图像"},
"similar": &graphql.ArgumentConfig{Type: graphql.Int, Description: "获取与指定ID图像相似的图像"},
"id": &graphql.ArgumentConfig{Type: graphql.Int, Description: "获取指定ID的图像"},
"width": &graphql.ArgumentConfig{Type: graphql.Int, Description: "筛选图像中指定宽度的"},
@@ -364,72 +354,49 @@ func NewSchema(config Config) (graphql.Schema, error) {
"update_time": &graphql.ArgumentConfig{Type: graphql.DateTime, Description: "筛选图像中更新时间等于指定值的"},
"first": &graphql.ArgumentConfig{Type: graphql.Int, Description: "翻页参数(傳回清單中的前n個元素)"},
"last": &graphql.ArgumentConfig{Type: graphql.Int, Description: "翻页参数(傳回清單中的最後n個元素)"},
"after": &graphql.ArgumentConfig{Type: graphql.String, Description: "翻页参数(傳回清單中指定遊標之後的元素)"},
"before": &graphql.ArgumentConfig{Type: graphql.String, Description: "翻页参数(傳回清單中指定遊標之前的元素)"},
"after": &graphql.ArgumentConfig{Type: graphql.Int, Description: "翻页参数(傳回清單中指定遊標之後的元素)"},
"before": &graphql.ArgumentConfig{Type: graphql.Int, Description: "翻页参数(傳回清單中指定遊標之前的元素)"},
},
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
// 定义参数结构体
var args struct {
First int
Last int
After string
Before string
Text string
Preference string
Similar int
First int
Last int
After int
Before int
Text string
Interest int
Similar int
}
mapstructure.Decode(p.Args, &args)
// 参数到 SQL 格式字符串的映射
var argToSQLFormat = map[string]string{
"id": "id=%d",
"width": "width=%d",
"height": "height=%d",
"content": "content='%s'",
"remark": "remark='%s'",
"description": "description='%s'",
"tags": "tags='%s'",
"rank": "rank='%s'",
"comment_num": "comment_num=%d",
"praise_count": "praise_count=%d",
"collect_count": "collect_count=%d",
"article_id": "article_id=%d",
"user_id": "user_id=%d",
"create_time": "create_time='%s'",
"update_time": "update_time='%s'",
// 限制长度防止全表扫描
var limit = 10
if args.First != 0 {
limit = args.First
} else if args.Last != 0 {
limit = args.Last
}
var total int
var images []Image
var fields = get_fields(p.Info.FieldASTs[0].SelectionSet.Selections)
var query = db.Limit(limit)
// 参数映射
var argFormat = []string{"id", "width", "height", "content", "remark", "description", "tags", "rank", "comment_num", "praise_count", "collect_count", "article_id", "user_id", "create_time", "update_time"}
// 筛选条件
var where []string
var order []string
for arg, format := range argToSQLFormat {
if p.Args[arg] != nil {
where = append(where, fmt.Sprintf(format, p.Args[arg]))
for _, format := range argFormat {
if p.Args[format] != nil {
query = query.Where(fmt.Sprintf(format, " = ?"), p.Args[format])
}
}
var id_list []string
var list []int
var id_list [][]int
// 特殊处理 preference 参数
if args.Preference != "" {
// 去除空格并拆分以逗号分割的ID
id_list = strings.Split(strings.ReplaceAll(args.Preference, " ", ""), ",")
// 使用这一组 id 推荐
fmt.Println("preference:", args.Preference)
}
// 特殊处理 similar 参数
if args.Similar != 0 {
id_list := models.GetSimilarImagesIdList(args.Similar, 200)
ids_str := strings.Trim(strings.Join(strings.Fields(fmt.Sprint(id_list)), ","), "[]")
if ids_str == "" {
return map[string]interface{}{"list": []Image{}, "total": 0}, nil
}
where = append(where, fmt.Sprintf("id IN (%s)", ids_str))
order = append(order, fmt.Sprintf("ORDER BY FIELD(id,%s)", ids_str))
}
// 特殊处理 text 参数
// 筛选:提取文字
if args.Text != "" {
resp, err := models.ZincSearch(map[string]interface{}{
"query": map[string]interface{}{
@@ -447,84 +414,123 @@ func NewSchema(config Config) (graphql.Schema, error) {
"from": 0,
"size": 200,
})
if err != nil {
fmt.Println("ZincSearch 获取图像列表失败", err)
return nil, err
}
id_list = resp.ToIDList(args.First, args.Last, args.After, args.Before)
id_list_str := strings.Trim(strings.Join(strings.Fields(fmt.Sprint(id_list)), ","), "[]")
if id_list_str == "" {
return map[string]interface{}{
"list": []Image{},
"total": 0,
}, nil
var item []int
for _, hit := range resp.Hits.Hits {
num, _ := strconv.Atoi(hit.ID)
item = append(item, num)
}
id_list = append(id_list, item)
if len(id_list) == 0 {
return map[string]interface{}{"list": []Image{}, "total": 0}, nil
}
where = append(where, fmt.Sprintf("id IN (%s)", id_list_str))
}
where_str := strings.Join(where, " AND ")
order_str := strings.Join(order, "")
// 筛选:相似图像
if args.Similar != 0 {
var item []int
for _, id := range models.GetSimilarImagesIdList(args.Similar, 200) {
item = append(item, int(id))
}
id_list = append(id_list, item)
if where_str != "" {
where_str = "WHERE " + where_str
if len(id_list) == 0 {
return map[string]interface{}{"list": []Image{}, "total": 0}, nil
}
}
// 处理翻页参数
var limit, offset int
if args.First == 0 && args.Last == 0 {
limit = 10
offset = 0
// 筛选:兴趣推荐
if args.Interest != 0 {
fmt.Println("Interest:", args.Interest)
}
// 排序
// 截取:取交集
if len(id_list) != 0 {
list = id_list[0]
if len(id_list) > 1 {
for _, slice := range id_list[1:] {
list = funk.Join(list, slice, funk.InnerJoin).([]int)
}
}
if len(list) == 0 {
return map[string]interface{}{"list": []Image{}, "total": 0}, nil
}
}
total = len(list)
// 截取: 分页
if args.After != 0 {
index := -1
for i, id := range list {
if id == args.After {
index = i
break
}
}
if index != -1 {
list = list[index+1:]
}
}
if args.Before != 0 {
index := -1
for i, id := range list {
if id == args.Before {
index = i
break
}
}
if index != -1 {
list = list[:index]
}
}
if args.First != 0 {
limit = args.First
offset = 0
list = list[:args.First]
}
if args.Last != 0 {
limit = args.Last
offset = len(id_list) - limit
list = list[len(list)-args.Last:]
}
// 执行查询
var query strings.Builder
fields := strings.Join(get_fields(p.Info.FieldASTs[0].SelectionSet.Selections), ",")
query.WriteString(fmt.Sprintf("SELECT %s FROM web_images %s %s LIMIT %d OFFSET %d", fields, where_str, order_str, limit, offset))
if args.First == 0 && args.Last == 0 {
list = list[:10]
}
var images ImageList
var q = query.String()
if err := connection.Select(&images, q); err != nil {
// 存在外部筛选条件
if len(id_list) > 0 && len(list) > 0 {
query = query.Where("id IN ?", list)
}
// 输出
if funk.Contains(fields, "user") {
query = query.Preload("User")
fmt.Println("加载 user")
}
if funk.Contains(fields, "article") {
query = query.Preload("Article")
}
if err := query.Find(&images).Error; err != nil {
fmt.Println("获取图像列表失败", err)
return nil, err
}
// 获取用户信息(如果图像列表不为空且请求字段中包含user)
if len(images) > 0 && strings.Contains(fields, "user") {
user_ids_str := images.ToAllUserID().ToString()
var users []User
if err := connection.Select(&users, fmt.Sprintf("SELECT id,user_name,avatar,rank,create_time,update_time FROM web_member WHERE id IN (%s)", user_ids_str)); err != nil {
fmt.Println("获取用户列表失败", err)
return nil, err
}
// 将用户信息与图像信息关联
images.SetUser(users)
}
// 获取文章信息(如果图像列表不为空且请求字段中包含article)
if len(images) > 0 && strings.Contains(fields, "article") {
article_ids_str := images.ToAllArticleID().ToString()
var articles []Article
if err := connection.Select(&articles, fmt.Sprintf("SELECT id,title,tags,create_time,update_time FROM web_article WHERE id IN (%s)", article_ids_str)); err != nil {
fmt.Println("获取文章列表失败", err)
return nil, err
}
// 将文章信息与图像信息关联
images.SetArticle(articles)
}
return map[string]interface{}{
"list": images,
"total": 0,
"total": total,
}, nil
},
},

View File

@@ -55,30 +55,8 @@ func (images *ImageList) ToAllUserID() (uniqueIds IDS) {
return uniqueIds
}
// 为每个图像设置用户信息
func (images *ImageList) SetUser(userList []User) {
for i, image := range *images {
for _, user := range userList {
if image.UserID == user.ID {
(*images)[i].User = user
}
}
}
}
// 为每个图像设置文章信息
func (images *ImageList) SetArticle(articleList []Article) {
for i, image := range *images {
for _, article := range articleList {
if image.ArticleID == article.ID {
(*images)[i].Article = article
}
}
}
}
type Image struct {
ID int `json:"id" db:"id"`
ID int `json:"id" db:"id" gorm:"primaryKey"`
Width int `json:"width" db:"width"`
Height int `json:"height" db:"height"`
Content string `json:"content" db:"content"`
@@ -93,9 +71,13 @@ type Image struct {
UserID int `json:"user_id" db:"user_id"`
CreateTime time.Time `json:"create_time" db:"create_time"`
UpdateTime time.Time `json:"update_time" db:"update_time"`
Text TextList `json:"text" db:"text"`
User User `json:"user" db:"-"`
Article Article `json:"article" db:"-"`
Text TextList `json:"text" db:"text" gorm:"type:json"`
User User `json:"user" gorm:"foreignKey:UserID"`
Article Article `json:"article" gorm:"foreignKey:ArticleID"`
}
func (Image) TableName() string {
return "web_images"
}
type TextList []struct {
@@ -109,7 +91,7 @@ func (a *TextList) Scan(value interface{}) error {
}
type User struct {
ID int `json:"id" db:"id"`
ID int `json:"id" db:"id" gorm:"primaryKey"`
UserName *string `json:"user_name" db:"user_name"`
Avatar *string `json:"avatar" db:"avatar"`
Rank *string `json:"rank" db:"rank"`
@@ -117,12 +99,21 @@ type User struct {
UpdateTime time.Time `json:"update_time" db:"update_time"`
}
func (User) TableName() string {
return "web_member"
}
type Article struct {
ID int `json:"id" db:"id"`
Title string `json:"title" db:"title"`
Tags string `json:"tags" db:"tags"`
CreateTime time.Time `json:"create_time" db:"create_time"`
UpdateTime time.Time `json:"update_time" db:"update_time"`
ID int `json:"id" db:"id" gorm:"primaryKey"`
Title string `json:"title" db:"title"`
Orientation string `json:"orientation" db:"orientation"`
Tags string `json:"tags" db:"tags"`
CreateTime time.Time `json:"create_time" db:"create_time"`
UpdateTime time.Time `json:"update_time" db:"update_time"`
}
func (Article) TableName() string {
return "web_article"
}
type Category struct {