mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-08-01 13:33:48 +08:00
fix: write wrong type of metadata (#17605)
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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{}:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user