215 lines
5.6 KiB
Go
215 lines
5.6 KiB
Go
package search
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/XingfenD/yoresee_doc/internal/config"
|
|
"github.com/XingfenD/yoresee_doc/internal/model"
|
|
"github.com/XingfenD/yoresee_doc/pkg/key"
|
|
"github.com/XingfenD/yoresee_doc/pkg/storage"
|
|
)
|
|
|
|
const defaultSearchIndexPrefix = "yoresee_doc"
|
|
|
|
type DocumentSearchRequest struct {
|
|
Keyword string
|
|
UserID *int64
|
|
KnowledgeID *int64
|
|
ListOwnDoc bool
|
|
DocType *string
|
|
Tags []string
|
|
CreateTimeRangeStart *string
|
|
CreateTimeRangeEnd *string
|
|
UpdateTimeRangeStart *string
|
|
UpdateTimeRangeEnd *string
|
|
Size int
|
|
}
|
|
|
|
func SearchDocumentIDs(ctx context.Context, req DocumentSearchRequest) ([]int64, error) {
|
|
if storage.ES == nil {
|
|
return nil, fmt.Errorf("elasticsearch client is nil")
|
|
}
|
|
return storage.ES.SearchIDs(ctx, DocumentIndexName(), BuildDocumentSearchBody(req))
|
|
}
|
|
|
|
func UpsertDocument(ctx context.Context, doc *model.Document) error {
|
|
if doc == nil {
|
|
return fmt.Errorf("document is nil")
|
|
}
|
|
if storage.ES == nil {
|
|
return fmt.Errorf("elasticsearch client is nil")
|
|
}
|
|
return storage.ES.UpsertDocument(ctx, DocumentIndexName(), strconv.FormatInt(doc.ID, 10), BuildDocumentIndexBody(doc))
|
|
}
|
|
|
|
func DocumentIndexName() string {
|
|
prefix := defaultSearchIndexPrefix
|
|
if config.GlobalConfig != nil {
|
|
configPrefix := strings.TrimSpace(config.GlobalConfig.Elasticsearch.IndexPrefix)
|
|
if configPrefix != "" {
|
|
prefix = configPrefix
|
|
}
|
|
}
|
|
return key.KeyESDocumentIndex(prefix)
|
|
}
|
|
|
|
func BuildDocumentSearchBody(req DocumentSearchRequest) map[string]interface{} {
|
|
size := req.Size
|
|
if size <= 0 {
|
|
size = 5000
|
|
}
|
|
if size > 10000 {
|
|
size = 10000
|
|
}
|
|
|
|
must := []map[string]interface{}{}
|
|
if strings.TrimSpace(req.Keyword) != "" {
|
|
must = append(must, map[string]interface{}{
|
|
"multi_match": map[string]interface{}{
|
|
"query": strings.TrimSpace(req.Keyword),
|
|
"fields": []string{"title^3", "summary^2", "content"},
|
|
"type": "best_fields",
|
|
"operator": "and",
|
|
"fuzziness": "AUTO",
|
|
},
|
|
})
|
|
}
|
|
|
|
filter := []map[string]interface{}{}
|
|
mustNot := []map[string]interface{}{}
|
|
if req.UserID != nil {
|
|
filter = append(filter, map[string]interface{}{
|
|
"term": map[string]interface{}{"user_id": *req.UserID},
|
|
})
|
|
}
|
|
if req.ListOwnDoc {
|
|
mustNot = append(mustNot, map[string]interface{}{
|
|
"exists": map[string]interface{}{"field": "knowledge_id"},
|
|
})
|
|
}
|
|
if req.KnowledgeID != nil {
|
|
filter = append(filter, map[string]interface{}{
|
|
"term": map[string]interface{}{"knowledge_id": *req.KnowledgeID},
|
|
})
|
|
}
|
|
if req.DocType != nil && strings.TrimSpace(*req.DocType) != "" {
|
|
filter = append(filter, map[string]interface{}{
|
|
"term": map[string]interface{}{"type": strings.TrimSpace(*req.DocType)},
|
|
})
|
|
}
|
|
for _, tag := range req.Tags {
|
|
tag = strings.TrimSpace(tag)
|
|
if tag == "" {
|
|
continue
|
|
}
|
|
filter = append(filter, map[string]interface{}{
|
|
"term": map[string]interface{}{"tags.keyword": tag},
|
|
})
|
|
}
|
|
|
|
rangeCreated := map[string]interface{}{}
|
|
if req.CreateTimeRangeStart != nil && strings.TrimSpace(*req.CreateTimeRangeStart) != "" {
|
|
rangeCreated["gte"] = strings.TrimSpace(*req.CreateTimeRangeStart)
|
|
}
|
|
if req.CreateTimeRangeEnd != nil && strings.TrimSpace(*req.CreateTimeRangeEnd) != "" {
|
|
rangeCreated["lte"] = strings.TrimSpace(*req.CreateTimeRangeEnd)
|
|
}
|
|
if len(rangeCreated) > 0 {
|
|
filter = append(filter, map[string]interface{}{
|
|
"range": map[string]interface{}{"created_at": rangeCreated},
|
|
})
|
|
}
|
|
|
|
rangeUpdated := map[string]interface{}{}
|
|
if req.UpdateTimeRangeStart != nil && strings.TrimSpace(*req.UpdateTimeRangeStart) != "" {
|
|
rangeUpdated["gte"] = strings.TrimSpace(*req.UpdateTimeRangeStart)
|
|
}
|
|
if req.UpdateTimeRangeEnd != nil && strings.TrimSpace(*req.UpdateTimeRangeEnd) != "" {
|
|
rangeUpdated["lte"] = strings.TrimSpace(*req.UpdateTimeRangeEnd)
|
|
}
|
|
if len(rangeUpdated) > 0 {
|
|
filter = append(filter, map[string]interface{}{
|
|
"range": map[string]interface{}{"updated_at": rangeUpdated},
|
|
})
|
|
}
|
|
|
|
return map[string]interface{}{
|
|
"size": size,
|
|
"_source": []string{
|
|
"id",
|
|
},
|
|
"query": map[string]interface{}{
|
|
"bool": map[string]interface{}{
|
|
"must": must,
|
|
"filter": filter,
|
|
"must_not": mustNot,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func BuildDocumentIndexBody(doc *model.Document) map[string]interface{} {
|
|
payload := map[string]interface{}{
|
|
"id": doc.ID,
|
|
"external_id": doc.ExternalID,
|
|
"title": doc.Title,
|
|
"summary": doc.Summary,
|
|
"content": doc.Content,
|
|
"type": doc.Type,
|
|
"user_id": doc.UserID,
|
|
"tags": doc.Tags,
|
|
"created_at": doc.CreatedAt.Format(time.RFC3339),
|
|
"updated_at": doc.UpdatedAt.Format(time.RFC3339),
|
|
}
|
|
if doc.KnowledgeID != nil {
|
|
payload["knowledge_id"] = *doc.KnowledgeID
|
|
}
|
|
return payload
|
|
}
|
|
|
|
func BuildDocumentIndexMapping() map[string]interface{} {
|
|
return map[string]interface{}{
|
|
"mappings": map[string]interface{}{
|
|
"properties": map[string]interface{}{
|
|
"id": map[string]interface{}{
|
|
"type": "long",
|
|
},
|
|
"external_id": map[string]interface{}{
|
|
"type": "keyword",
|
|
},
|
|
"title": map[string]interface{}{
|
|
"type": "text",
|
|
},
|
|
"summary": map[string]interface{}{
|
|
"type": "text",
|
|
},
|
|
"content": map[string]interface{}{
|
|
"type": "text",
|
|
},
|
|
"type": map[string]interface{}{
|
|
"type": "keyword",
|
|
},
|
|
"user_id": map[string]interface{}{
|
|
"type": "long",
|
|
},
|
|
"knowledge_id": map[string]interface{}{
|
|
"type": "long",
|
|
},
|
|
"tags": map[string]interface{}{
|
|
"type": "keyword",
|
|
},
|
|
"created_at": map[string]interface{}{
|
|
"type": "date",
|
|
},
|
|
"updated_at": map[string]interface{}{
|
|
"type": "date",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|