fix: write wrong type of metadata (#17605)

This commit is contained in:
Haruko386
2026-07-31 16:55:34 +08:00
committed by GitHub
parent 8a66aa7aae
commit 0108cdac67
5 changed files with 109 additions and 5 deletions

View File

@@ -22,6 +22,7 @@ import (
"encoding/json"
"fmt"
"io"
"os"
"strings"
"github.com/elastic/go-elasticsearch/v8/esapi"
@@ -30,6 +31,7 @@ import (
"ragflow/internal/common"
"ragflow/internal/dao"
"ragflow/internal/engine/types"
"ragflow/internal/utility"
"go.uber.org/zap"
)
@@ -47,9 +49,18 @@ func (e *elasticsearchEngine) CreateMetadataStore(ctx context.Context, tenantID
return nil
}
// Index will be created with mapping from index template (ragflow_doc_meta_mapping)
mappingPath, err := utility.FindConfFileInProject("doc_meta_es_mapping.json")
if err != nil {
return err
}
data, err := os.ReadFile(*mappingPath)
if err != nil {
return fmt.Errorf("failed to read mapping file %q: %w", *mappingPath, err)
}
req := esapi.IndicesCreateRequest{
Index: indexName,
Body: bytes.NewReader(data),
}
res, err := req.Do(ctx, e.client)
if err != nil {

View File

@@ -577,9 +577,6 @@ func (h *DocumentHandler) ListDocuments(c *gin.Context) {
return
}
// Mirror Python: an explicit id/name filter that matches nothing is an
// ownership error rather than an empty page. Each filter's existence is
// checked independently of the other.
if docID := c.Query("id"); docID != "" {
idOpts := opts
idOpts.DocIDs = []string{docID}

View File

@@ -54,6 +54,7 @@ func (s *DocumentService) SetDocumentMetadata(ctx context.Context, docID string,
return fmt.Errorf("failed to ensure metadata store: %w", err)
}
meta = splitCombinedDocumentMetadataValues(meta)
if err = s.docEngine.UpdateMetadata(ctx, docID, doc.KbID, meta, tenantID); err != nil {
return fmt.Errorf("failed to update metadata: %w", err)
}
@@ -711,6 +712,55 @@ func cloneDocumentMetadata(meta map[string]interface{}) map[string]interface{} {
return cloned
}
func splitCombinedDocumentMetadataValues(meta map[string]any) map[string]any {
if len(meta) == 0 {
return meta
}
out := make(map[string]any, len(meta))
for key, value := range meta {
switch typed := value.(type) {
case []interface{}:
out[key] = splitCombinedDocumentMetadataList(typed)
case []string:
items := make([]any, 0, len(typed))
for _, item := range typed {
items = append(items, item)
}
out[key] = splitCombinedDocumentMetadataList(items)
default:
out[key] = value
}
}
return out
}
var combinedDocumentMetadataValueSplitter = regexp.MustCompile(`[、,;|]+`)
func splitCombinedDocumentMetadataList(items []any) []any {
out := make([]interface{}, 0, len(items))
for _, item := range items {
text, ok := item.(string)
if !ok {
out = append(out, item)
continue
}
parts := combinedDocumentMetadataValueSplitter.Split(strings.TrimSpace(text), -1)
added := false
for _, part := range parts {
part = strings.TrimSpace(part)
if part == "" {
continue
}
out = append(out, part)
added = true
}
if !added {
out = append(out, item)
}
}
return dedupeDocumentMetadataList(out)
}
func cloneDocumentMetadataValue(v interface{}) interface{} {
switch typed := v.(type) {
case []interface{}:

View File

@@ -51,6 +51,35 @@ func TestNormalizeMetadataListValue(t *testing.T) {
}
}
func TestSplitCombinedDocumentMetadataValues(t *testing.T) {
meta := map[string]interface{}{
"character": []interface{}{"关羽、孙权", "张辽|赵云", "曹操"},
"author": "alice,bob",
"year": 2026,
}
got := splitCombinedDocumentMetadataValues(meta)
characters, ok := got["character"].([]interface{})
if !ok {
t.Fatalf("character has unexpected type: %T", got["character"])
}
want := []interface{}{"关羽", "孙权", "张辽", "赵云", "曹操"}
if len(characters) != len(want) {
t.Fatalf("character length = %d, want %d: %#v", len(characters), len(want), characters)
}
for i := range want {
if characters[i] != want[i] {
t.Fatalf("character[%d] = %#v, want %#v", i, characters[i], want[i])
}
}
if got["author"] != "alice,bob" {
t.Fatalf("scalar author should be preserved, got %#v", got["author"])
}
if got["year"] != 2026 {
t.Fatalf("year should be preserved, got %#v", got["year"])
}
}
func TestFirstScalarMetadataValue(t *testing.T) {
if v, ok := firstScalarMetadataValue([]interface{}{"a", "b"}); !ok || v != "a" {
t.Errorf("should return first non-nil scalar: %v (ok=%v)", v, ok)

View File

@@ -2029,7 +2029,11 @@ func TestSetDocumentMetadataMergesMetadataRow(t *testing.T) {
svc.metadataSvc = service.NewMetadataServiceForTest(dao.NewKnowledgebaseDAO(), engine)
ctx := t.Context()
if err := svc.SetDocumentMetadata(ctx, "doc-1", map[string]interface{}{"category": "tech", "year": 2026}); err != nil {
if err := svc.SetDocumentMetadata(ctx, "doc-1", map[string]interface{}{
"category": "tech",
"year": 2026,
"character": []interface{}{"关羽、孙权", "张辽|赵云"},
}); err != nil {
t.Fatalf("SetDocumentMetadata failed: %v", err)
}
if got := engine.records["doc-1"]["author"]; got != "alice" {
@@ -2041,6 +2045,19 @@ func TestSetDocumentMetadataMergesMetadataRow(t *testing.T) {
if got := engine.records["doc-1"]["year"]; got != 2026 {
t.Fatalf("year = %#v, want 2026", got)
}
characters, ok := engine.records["doc-1"]["character"].([]interface{})
if !ok {
t.Fatalf("character has unexpected type: %T", engine.records["doc-1"]["character"])
}
wantCharacters := []interface{}{"关羽", "孙权", "张辽", "赵云"}
if len(characters) != len(wantCharacters) {
t.Fatalf("character length = %d, want %d: %#v", len(characters), len(wantCharacters), characters)
}
for i := range wantCharacters {
if characters[i] != wantCharacters[i] {
t.Fatalf("character[%d] = %#v, want %#v", i, characters[i], wantCharacters[i])
}
}
if got := engine.docKBs["doc-1"]; got != "kb-1" {
t.Fatalf("kb_id = %q, want kb-1", got)
}