mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-08-15 05:04:27 +08:00
Implement UpdateDataset and UpdateMetadata in GO (#13928)
### What problem does this PR solve? Implement UpdateDataset and UpdateMetadata in GO Add cli: UPDATE CHUNK <chunk_id> OF DATASET <dataset_name> SET <update_fields> REMOVE TAGS 'tag1', 'tag2' from DATASET 'dataset_name'; SET METADATA OF DOCUMENT <doc_id> TO <meta> ### Type of change - [ ] Refactoring
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"ragflow/internal/common"
|
||||
|
||||
@@ -246,3 +247,122 @@ func (h *ChunkHandler) List(c *gin.Context) {
|
||||
"message": "success",
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateChunk updates a chunk
|
||||
// @Summary Update Chunk
|
||||
// @Description Update chunk fields
|
||||
// @Tags chunks
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param dataset_id path string true "Dataset ID"
|
||||
// @Param document_id path string true "Document ID"
|
||||
// @Param chunk_id path string true "Chunk ID"
|
||||
// @Param request body service.UpdateChunkRequest true "update chunk"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /v1/datasets/{dataset_id}/documents/{document_id}/chunks/{chunk_id} [put]
|
||||
func (h *ChunkHandler) UpdateChunk(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
jsonError(c, errorCode, errorMessage)
|
||||
return
|
||||
}
|
||||
|
||||
// Get path parameters
|
||||
datasetID := c.Param("dataset_id")
|
||||
documentID := c.Param("document_id")
|
||||
chunkID := c.Param("chunk_id")
|
||||
|
||||
if datasetID == "" || documentID == "" || chunkID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"message": "dataset_id, document_id, and chunk_id are required",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Validate allowed update fields
|
||||
var rawBody map[string]interface{}
|
||||
if err := json.NewDecoder(c.Request.Body).Decode(&rawBody); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"message": "invalid JSON body: " + err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Allowed fields for update
|
||||
allowedFields := map[string]bool{
|
||||
"content": true,
|
||||
"important_keywords": true,
|
||||
"questions": true,
|
||||
"available": true,
|
||||
"positions": true,
|
||||
"tag_kwd": true,
|
||||
"tag_feas": true,
|
||||
}
|
||||
for field := range rawBody {
|
||||
if !allowedFields[field] {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"message": "Update field '" + field + "' is not supported. Updatable fields: content, important_keywords, questions, available, positions, tag_kwd, tag_feas",
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Build UpdateChunkRequest from rawBody
|
||||
var req service.UpdateChunkRequest
|
||||
if content, ok := rawBody["content"].(string); ok {
|
||||
req.Content = &content
|
||||
}
|
||||
if importantKwd, ok := rawBody["important_keywords"].([]interface{}); ok {
|
||||
req.ImportantKwd = make([]string, len(importantKwd))
|
||||
for i, v := range importantKwd {
|
||||
if s, ok := v.(string); ok {
|
||||
req.ImportantKwd[i] = s
|
||||
}
|
||||
}
|
||||
}
|
||||
if questions, ok := rawBody["questions"].([]interface{}); ok {
|
||||
req.Questions = make([]string, len(questions))
|
||||
for i, v := range questions {
|
||||
if s, ok := v.(string); ok {
|
||||
req.Questions[i] = s
|
||||
}
|
||||
}
|
||||
}
|
||||
if available, ok := rawBody["available"].(bool); ok {
|
||||
req.Available = &available
|
||||
}
|
||||
if positions, ok := rawBody["positions"].([]interface{}); ok {
|
||||
req.Positions = positions
|
||||
}
|
||||
if tagKwd, ok := rawBody["tag_kwd"].([]interface{}); ok {
|
||||
req.TagKwd = make([]string, len(tagKwd))
|
||||
for i, v := range tagKwd {
|
||||
if s, ok := v.(string); ok {
|
||||
req.TagKwd[i] = s
|
||||
}
|
||||
}
|
||||
}
|
||||
req.TagFeas = rawBody["tag_feas"]
|
||||
|
||||
// Set path parameters
|
||||
req.DatasetID = datasetID
|
||||
req.DocumentID = documentID
|
||||
req.ChunkID = chunkID
|
||||
|
||||
err := h.chunkService.UpdateChunk(&req, user.ID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"code": 500,
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "chunk updated successfully",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"ragflow/internal/common"
|
||||
"strconv"
|
||||
@@ -372,3 +374,103 @@ func (h *DocumentHandler) MetadataSummary(c *gin.Context) {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// SetMetaRequest represents the request for setting document metadata
|
||||
type SetMetaRequest struct {
|
||||
DocID string `json:"doc_id" binding:"required"`
|
||||
Meta string `json:"meta" binding:"required"`
|
||||
}
|
||||
|
||||
// SetMeta handles the set metadata request for a document
|
||||
// @Summary Set Document Metadata
|
||||
// @Description Set metadata for a specific document
|
||||
// @Tags documents
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security ApiKeyAuth
|
||||
// @Param request body SetMetaRequest true "metadata info"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /v1/document/set_meta [post]
|
||||
func (h *DocumentHandler) SetMeta(c *gin.Context) {
|
||||
_, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
jsonError(c, errorCode, errorMessage)
|
||||
return
|
||||
}
|
||||
|
||||
var req SetMetaRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 1,
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if req.DocID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 1,
|
||||
"message": "doc_id is required",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Parse meta JSON string
|
||||
var meta map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(req.Meta), &meta); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 1,
|
||||
"message": "Json syntax error: " + err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if meta == nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 1,
|
||||
"message": "meta is required",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Validate meta values - must be str, int, float, or list of those
|
||||
for k, v := range meta {
|
||||
switch val := v.(type) {
|
||||
case string, int, float64:
|
||||
// Valid
|
||||
case []interface{}:
|
||||
for _, item := range val {
|
||||
if _, ok := item.(string); !ok {
|
||||
if _, ok := item.(float64); !ok {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 1,
|
||||
"message": fmt.Sprintf("Unsupported type in list for key %s: %T", k, item),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 1,
|
||||
"message": fmt.Sprintf("Unsupported type for key %s: %T", k, v),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err := h.documentService.SetDocumentMetadata(req.DocID, meta)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"code": 1,
|
||||
"message": "Failed to set metadata: " + err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": true,
|
||||
})
|
||||
}
|
||||
@@ -444,6 +444,34 @@ func (h *KnowledgebaseHandler) RemoveTags(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Get KB to find tenant_id and build index name
|
||||
kb, err := h.kbService.GetByID(kbID)
|
||||
if err != nil {
|
||||
jsonError(c, common.CodeDataError, "knowledge base not found")
|
||||
return
|
||||
}
|
||||
|
||||
// Build index name prefix: ragflow_<tenant_id>
|
||||
indexName := "ragflow_" + kb.TenantID
|
||||
|
||||
// For each tag, call UpdateChunk to remove it from documents
|
||||
for _, tag := range req.Tags {
|
||||
condition := map[string]interface{}{
|
||||
"tag_kwd": tag,
|
||||
"kb_id": kbID,
|
||||
}
|
||||
newValue := map[string]interface{}{
|
||||
"remove": map[string]interface{}{
|
||||
"tag_kwd": tag,
|
||||
},
|
||||
}
|
||||
err := h.kbService.RemoveTag(condition, newValue, indexName, kbID)
|
||||
if err != nil {
|
||||
jsonError(c, common.CodeServerError, "Failed to remove tag: "+err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
jsonResponse(c, common.CodeSuccess, true, "success")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user