Port dataset-level structure merge for timeline/graph/mindmap (#18201)

Ports Python dataset-level structure aggregation (timeline, graph, mindmap) to Go. Mindmap emits entity/relation rows and merges like graph. Adds dataset_merge guard, engine gate, resolveDatasetStructureKind, kind-required structure graph GET/DELETE API, per-index task-id fields.
This commit is contained in:
Zhichang Yu
2026-08-13 18:37:47 +08:00
committed by GitHub
parent a6f2eac496
commit 0784bef5b0
16 changed files with 958 additions and 307 deletions

View File

@@ -14,6 +14,7 @@
package handler
import (
"errors"
"net/http"
"strconv"
@@ -276,35 +277,56 @@ func (h *DatasetArtifactHandler) GetArtifactGraph(c *gin.Context) {
common.SuccessWithData(c, graph, "success")
}
// ListStructures handles GET /artifacts/structure — list compiled structures of a dataset.
// ListStructures handles GET /artifacts/structure?kind=<kind> — the dataset-scope
// structure graph for a resolved kind (mirrors Python get_dataset_structure).
// kind is REQUIRED: missing or invalid → 400 ARGUMENT_ERROR.
func (h *DatasetArtifactHandler) ListStructures(c *gin.Context) {
_, tenantID, _ := h.datasetOwner(c, c.Param("dataset_id"))
if tenantID == "" {
return
}
datasetID := c.Param("dataset_id")
structureKind := c.Query("structure_kind")
structureIndexType := c.Query("structure_index_type")
items, total, err := h.svc.ListStructures(c.Request.Context(), tenantID, datasetID, structureKind, structureIndexType)
if err != nil {
common.ErrorWithCode(c, common.CodeDataError, err.Error())
kind := c.Query("kind")
if kind == "" {
common.ErrorWithCode(c, common.CodeArgumentError, "kind is required")
return
}
common.SuccessWithData(c, gin.H{"total": total, "structures": items}, "success")
in := service.DatasetStructureGraphInput{TenantID: tenantID, DatasetID: datasetID, Kind: kind}
resp, err := h.svc.GetDatasetStructure(c.Request.Context(), in)
if err != nil {
if errors.Is(err, service.ErrInvalidStructureKind) {
common.ErrorWithCode(c, common.CodeArgumentError, err.Error())
} else {
common.ErrorWithCode(c, common.CodeServerError, err.Error())
}
return
}
common.SuccessWithData(c, resp, "success")
}
// DeleteStructures handles DELETE /artifacts/structure — delete compiled structures of a dataset.
// DeleteStructures handles DELETE /artifacts/structure?kind=<kind>&wipe=<bool> —
// cancel the kind's task (wipe=false) or delete its dataset rows (wipe=true).
// kind is REQUIRED.
func (h *DatasetArtifactHandler) DeleteStructures(c *gin.Context) {
_, tenantID, _ := h.datasetOwner(c, c.Param("dataset_id"))
if tenantID == "" {
return
}
datasetID := c.Param("dataset_id")
structureKind := c.Query("structure_kind")
structureIndexType := c.Query("structure_index_type")
n, err := h.svc.DeleteStructures(c.Request.Context(), tenantID, datasetID, structureKind, structureIndexType)
kind := c.Query("kind")
if kind == "" {
common.ErrorWithCode(c, common.CodeArgumentError, "kind is required")
return
}
wipe := c.Query("wipe") == "true"
in := service.DatasetStructureGraphInput{TenantID: tenantID, DatasetID: datasetID, Kind: kind, Wipe: wipe}
n, err := h.svc.DeleteDatasetStructure(c.Request.Context(), in)
if err != nil {
common.ErrorWithCode(c, common.CodeDataError, err.Error())
if errors.Is(err, service.ErrInvalidStructureKind) {
common.ErrorWithCode(c, common.CodeArgumentError, err.Error())
} else {
common.ErrorWithCode(c, common.CodeServerError, err.Error())
}
return
}
common.SuccessWithData(c, gin.H{"deleted": n}, "success")