feat: 支持删除文档类型标签
This commit is contained in:
@@ -39,6 +39,7 @@ export default {
|
||||
tagEditSuccess: 'Tag updated',
|
||||
tagDeleteTitle: 'Delete tag',
|
||||
tagDeleteDesc: 'Delete tag "{name}"? All FAQ entries under this tag will also be deleted.',
|
||||
tagDeleteDescDoc: 'Delete tag "{name}"? All documents under this tag will also be deleted.',
|
||||
tagDeleteSuccess: 'Tag deleted',
|
||||
tagEditAction: 'Rename',
|
||||
tagDeleteAction: 'Delete',
|
||||
|
||||
@@ -38,6 +38,7 @@ export default {
|
||||
tagEditSuccess: "태그 업데이트 성공",
|
||||
tagDeleteTitle: "태그 삭제",
|
||||
tagDeleteDesc: '"{name}" 태그를 삭제하시겠습니까? 해당 태그의 모든 FAQ 항목이 함께 삭제됩니다',
|
||||
tagDeleteDescDoc: '"{name}" 태그를 삭제하시겠습니까? 해당 태그의 모든 문서가 함께 삭제됩니다',
|
||||
tagDeleteSuccess: "태그가 삭제되었습니다",
|
||||
tagEditAction: "이름 변경",
|
||||
tagDeleteAction: "삭제",
|
||||
|
||||
@@ -38,6 +38,7 @@ export default {
|
||||
tagEditSuccess: 'Тег обновлён',
|
||||
tagDeleteTitle: 'Удаление тега',
|
||||
tagDeleteDesc: 'Удалить тег «{name}»? Все записи FAQ под этим тегом также будут удалены.',
|
||||
tagDeleteDescDoc: 'Удалить тег «{name}»? Все документы под этим тегом также будут удалены.',
|
||||
tagDeleteSuccess: 'Тег удалён',
|
||||
tagEditAction: 'Переименовать',
|
||||
tagDeleteAction: 'Удалить',
|
||||
|
||||
@@ -39,6 +39,7 @@ export default {
|
||||
tagEditSuccess: "标签更新成功",
|
||||
tagDeleteTitle: "删除标签",
|
||||
tagDeleteDesc: '确定删除标签"{name}"?该标签下的所有 FAQ 条目将被一并删除',
|
||||
tagDeleteDescDoc: '确定删除标签"{name}"?该标签下的所有文档将被一并删除',
|
||||
tagDeleteSuccess: "标签已删除",
|
||||
tagEditAction: "重命名",
|
||||
tagDeleteAction: "删除",
|
||||
|
||||
@@ -330,8 +330,9 @@ const confirmDeleteTag = (tag: any) => {
|
||||
if (editingTagId.value) {
|
||||
cancelEditTag();
|
||||
}
|
||||
const deleteDescKey = isFAQ.value ? 'knowledgeBase.tagDeleteDesc' : 'knowledgeBase.tagDeleteDescDoc';
|
||||
const confirm = window.confirm(
|
||||
t('knowledgeBase.tagDeleteDesc', { name: tag.name }) as string,
|
||||
t(deleteDescKey, { name: tag.name }) as string,
|
||||
);
|
||||
if (!confirm) return;
|
||||
deleteKnowledgeBaseTag(kbId.value, tag.seq_id, { force: true })
|
||||
@@ -343,7 +344,10 @@ const confirmDeleteTag = (tag: any) => {
|
||||
handleTagFilterChange('');
|
||||
}
|
||||
loadTags(kbId.value);
|
||||
loadKnowledgeFiles(kbId.value);
|
||||
// 由于后端是异步删除文档,延迟刷新以确保看到最新数据
|
||||
setTimeout(() => {
|
||||
loadKnowledgeFiles(kbId.value);
|
||||
}, 500);
|
||||
})
|
||||
.catch((error: any) => {
|
||||
MessagePlugin.error(error?.message || t('common.operationFailed'));
|
||||
|
||||
@@ -401,3 +401,16 @@ func (r *knowledgeRepository) SearchKnowledge(
|
||||
}
|
||||
return knowledges, hasMore, nil
|
||||
}
|
||||
|
||||
// ListIDsByTagID returns all knowledge IDs that have the specified tag ID
|
||||
func (r *knowledgeRepository) ListIDsByTagID(
|
||||
ctx context.Context,
|
||||
tenantID uint64,
|
||||
kbID, tagID string,
|
||||
) ([]string, error) {
|
||||
var ids []string
|
||||
err := r.db.WithContext(ctx).Model(&types.Knowledge{}).
|
||||
Where("tenant_id = ? AND knowledge_base_id = ? AND tag_id = ?", tenantID, kbID, tagID).
|
||||
Pluck("id", &ids).Error
|
||||
return ids, err
|
||||
}
|
||||
|
||||
@@ -7300,3 +7300,34 @@ func (s *knowledgeService) SearchKnowledge(ctx context.Context, keyword string,
|
||||
}
|
||||
return s.repo.SearchKnowledge(ctx, tenantID, keyword, offset, limit, fileTypes)
|
||||
}
|
||||
|
||||
// ProcessKnowledgeListDelete handles Asynq knowledge list delete tasks
|
||||
func (s *knowledgeService) ProcessKnowledgeListDelete(ctx context.Context, t *asynq.Task) error {
|
||||
var payload types.KnowledgeListDeletePayload
|
||||
if err := json.Unmarshal(t.Payload(), &payload); err != nil {
|
||||
logger.Errorf(ctx, "Failed to unmarshal knowledge list delete payload: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Infof(ctx, "Processing knowledge list delete task for %d knowledge items", len(payload.KnowledgeIDs))
|
||||
|
||||
// Get tenant info
|
||||
tenant, err := s.tenantRepo.GetTenantByID(ctx, payload.TenantID)
|
||||
if err != nil {
|
||||
logger.Errorf(ctx, "Failed to get tenant %d: %v", payload.TenantID, err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Set context values
|
||||
ctx = context.WithValue(ctx, types.TenantIDContextKey, payload.TenantID)
|
||||
ctx = context.WithValue(ctx, types.TenantInfoContextKey, tenant)
|
||||
|
||||
// Delete knowledge list
|
||||
if err := s.DeleteKnowledgeList(ctx, payload.KnowledgeIDs); err != nil {
|
||||
logger.Errorf(ctx, "Failed to delete knowledge list: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Infof(ctx, "Successfully deleted %d knowledge items", len(payload.KnowledgeIDs))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
type knowledgeTagService struct {
|
||||
kbService interfaces.KnowledgeBaseService
|
||||
repo interfaces.KnowledgeTagRepository
|
||||
knowledgeRepo interfaces.KnowledgeRepository
|
||||
chunkRepo interfaces.ChunkRepository
|
||||
retrieveEngine interfaces.RetrieveEngineRegistry
|
||||
modelService interfaces.ModelService
|
||||
@@ -31,6 +32,7 @@ type knowledgeTagService struct {
|
||||
func NewKnowledgeTagService(
|
||||
kbService interfaces.KnowledgeBaseService,
|
||||
repo interfaces.KnowledgeTagRepository,
|
||||
knowledgeRepo interfaces.KnowledgeRepository,
|
||||
chunkRepo interfaces.ChunkRepository,
|
||||
retrieveEngine interfaces.RetrieveEngineRegistry,
|
||||
modelService interfaces.ModelService,
|
||||
@@ -39,6 +41,7 @@ func NewKnowledgeTagService(
|
||||
return &knowledgeTagService{
|
||||
kbService: kbService,
|
||||
repo: repo,
|
||||
knowledgeRepo: knowledgeRepo,
|
||||
chunkRepo: chunkRepo,
|
||||
retrieveEngine: retrieveEngine,
|
||||
modelService: modelService,
|
||||
@@ -181,6 +184,7 @@ func (s *knowledgeTagService) UpdateTag(
|
||||
}
|
||||
|
||||
// DeleteTag deletes a tag. When force=true, also deletes all chunks under this tag.
|
||||
// For document-type knowledge bases, also deletes all knowledge files under this tag.
|
||||
// When contentOnly=true, only deletes the content under the tag but keeps the tag itself.
|
||||
func (s *knowledgeTagService) DeleteTag(ctx context.Context, id string, force bool, contentOnly bool, excludeIDs []string) error {
|
||||
if id == "" {
|
||||
@@ -224,9 +228,49 @@ func (s *knowledgeTagService) DeleteTag(ctx context.Context, id string, force bo
|
||||
return nil
|
||||
}
|
||||
|
||||
// Helper function to enqueue knowledge list delete task for document-type knowledge bases
|
||||
enqueueKnowledgeDeleteTask := func() error {
|
||||
if kb.Type != types.KnowledgeBaseTypeDocument {
|
||||
return nil
|
||||
}
|
||||
// Get all knowledge IDs under this tag
|
||||
knowledgeIDs, err := s.knowledgeRepo.ListIDsByTagID(ctx, tenantID, kb.ID, tag.ID)
|
||||
if err != nil {
|
||||
logger.Errorf(ctx, "Failed to list knowledge IDs by tag ID %s: %v", tag.ID, err)
|
||||
return werrors.NewInternalServerError("获取标签下的文档失败")
|
||||
}
|
||||
if len(knowledgeIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
// Enqueue async task to delete knowledge files
|
||||
payload := types.KnowledgeListDeletePayload{
|
||||
TenantID: tenantID,
|
||||
KnowledgeIDs: knowledgeIDs,
|
||||
}
|
||||
payloadBytes, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
logger.Errorf(ctx, "Failed to marshal knowledge list delete payload: %v", err)
|
||||
return werrors.NewInternalServerError("删除标签下的文档失败")
|
||||
}
|
||||
task := asynq.NewTask(types.TypeKnowledgeListDelete, payloadBytes, asynq.Queue("low"), asynq.MaxRetry(3))
|
||||
info, err := s.task.Enqueue(task)
|
||||
if err != nil {
|
||||
logger.Errorf(ctx, "Failed to enqueue knowledge list delete task: %v", err)
|
||||
return werrors.NewInternalServerError("删除标签下的文档失败")
|
||||
}
|
||||
logger.Infof(ctx, "Enqueued knowledge list delete task %s for %d knowledge files under tag %s", info.ID, len(knowledgeIDs), tag.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// contentOnly mode: only delete content, keep the tag
|
||||
if contentOnly {
|
||||
if cCount > 0 {
|
||||
// For document-type KB, delete knowledge files first (which will also delete chunks)
|
||||
if kb.Type == types.KnowledgeBaseTypeDocument && kCount > 0 {
|
||||
if err := enqueueKnowledgeDeleteTask(); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if cCount > 0 {
|
||||
// For FAQ-type KB, only delete chunks
|
||||
if err := deleteChunksAndEnqueueIndexDelete(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -237,12 +281,22 @@ func (s *knowledgeTagService) DeleteTag(ctx context.Context, id string, force bo
|
||||
if !force && (kCount > 0 || cCount > 0) {
|
||||
return werrors.NewBadRequestError("标签仍有知识或FAQ条目引用,无法删除")
|
||||
}
|
||||
// When force=true, delete all chunks under this tag first
|
||||
if force && cCount > 0 {
|
||||
if err := deleteChunksAndEnqueueIndexDelete(); err != nil {
|
||||
return err
|
||||
|
||||
// When force=true, delete all content under this tag first
|
||||
if force {
|
||||
// For document-type KB, delete knowledge files first (which will also delete chunks)
|
||||
if kb.Type == types.KnowledgeBaseTypeDocument && kCount > 0 {
|
||||
if err := enqueueKnowledgeDeleteTask(); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if cCount > 0 {
|
||||
// For FAQ-type KB, only delete chunks
|
||||
if err := deleteChunksAndEnqueueIndexDelete(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there are excludeIDs, we cannot delete the tag itself as it still has content
|
||||
if len(excludeIDs) > 0 {
|
||||
return nil
|
||||
|
||||
@@ -88,6 +88,9 @@ func RunAsynqServer(params AsynqTaskParams) *asynq.ServeMux {
|
||||
// Register KB clone handler
|
||||
mux.HandleFunc(types.TypeKBClone, params.KnowledgeService.ProcessKBClone)
|
||||
|
||||
// Register knowledge list delete handler
|
||||
mux.HandleFunc(types.TypeKnowledgeListDelete, params.KnowledgeService.ProcessKnowledgeListDelete)
|
||||
|
||||
// Register index delete handler
|
||||
mux.HandleFunc(types.TypeIndexDelete, params.TagService.ProcessIndexDelete)
|
||||
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
package types
|
||||
|
||||
const (
|
||||
TypeChunkExtract = "chunk:extract"
|
||||
TypeDocumentProcess = "document:process" // 文档处理任务
|
||||
TypeFAQImport = "faq:import" // FAQ导入任务(包含dry run模式)
|
||||
TypeQuestionGeneration = "question:generation" // 问题生成任务
|
||||
TypeSummaryGeneration = "summary:generation" // 摘要生成任务
|
||||
TypeKBClone = "kb:clone" // 知识库复制任务
|
||||
TypeIndexDelete = "index:delete" // 索引删除任务
|
||||
TypeKBDelete = "kb:delete" // 知识库删除任务
|
||||
TypeDataTableSummary = "datatable:summary" // 表格摘要任务
|
||||
TypeChunkExtract = "chunk:extract"
|
||||
TypeDocumentProcess = "document:process" // 文档处理任务
|
||||
TypeFAQImport = "faq:import" // FAQ导入任务(包含dry run模式)
|
||||
TypeQuestionGeneration = "question:generation" // 问题生成任务
|
||||
TypeSummaryGeneration = "summary:generation" // 摘要生成任务
|
||||
TypeKBClone = "kb:clone" // 知识库复制任务
|
||||
TypeIndexDelete = "index:delete" // 索引删除任务
|
||||
TypeKBDelete = "kb:delete" // 知识库删除任务
|
||||
TypeKnowledgeListDelete = "knowledge:list_delete" // 批量删除知识任务
|
||||
TypeDataTableSummary = "datatable:summary" // 表格摘要任务
|
||||
)
|
||||
|
||||
// ExtractChunkPayload represents the extract chunk task payload
|
||||
@@ -86,6 +87,12 @@ type KBDeletePayload struct {
|
||||
EffectiveEngines []RetrieverEngineParams `json:"effective_engines"`
|
||||
}
|
||||
|
||||
// KnowledgeListDeletePayload represents the batch knowledge delete task payload
|
||||
type KnowledgeListDeletePayload struct {
|
||||
TenantID uint64 `json:"tenant_id"`
|
||||
KnowledgeIDs []string `json:"knowledge_ids"`
|
||||
}
|
||||
|
||||
// KBCloneTaskStatus represents the status of a knowledge base clone task
|
||||
type KBCloneTaskStatus string
|
||||
|
||||
|
||||
@@ -62,6 +62,8 @@ type KnowledgeService interface {
|
||||
) (*types.PageResult, error)
|
||||
// DeleteKnowledge deletes knowledge by ID.
|
||||
DeleteKnowledge(ctx context.Context, id string) error
|
||||
// DeleteKnowledgeList deletes multiple knowledge entries by IDs.
|
||||
DeleteKnowledgeList(ctx context.Context, ids []string) error
|
||||
// GetKnowledgeFile retrieves the file associated with the knowledge.
|
||||
GetKnowledgeFile(ctx context.Context, id string) (io.ReadCloser, string, error)
|
||||
// UpdateKnowledge updates knowledge information.
|
||||
@@ -127,6 +129,8 @@ type KnowledgeService interface {
|
||||
ProcessSummaryGeneration(ctx context.Context, t *asynq.Task) error
|
||||
// ProcessKBClone handles Asynq knowledge base clone tasks
|
||||
ProcessKBClone(ctx context.Context, t *asynq.Task) error
|
||||
// ProcessKnowledgeListDelete handles Asynq knowledge list delete tasks
|
||||
ProcessKnowledgeListDelete(ctx context.Context, t *asynq.Task) error
|
||||
// GetKBCloneProgress retrieves the progress of a knowledge base clone task
|
||||
GetKBCloneProgress(ctx context.Context, taskID string) (*types.KBCloneProgress, error)
|
||||
// SaveKBCloneProgress saves the progress of a knowledge base clone task
|
||||
@@ -178,4 +182,6 @@ type KnowledgeRepository interface {
|
||||
// SearchKnowledge searches knowledge items by keyword across the tenant.
|
||||
// fileTypes: optional list of file extensions to filter by (e.g., ["csv", "xlsx"])
|
||||
SearchKnowledge(ctx context.Context, tenantID uint64, keyword string, offset, limit int, fileTypes []string) ([]*types.Knowledge, bool, error)
|
||||
// ListIDsByTagID returns all knowledge IDs that have the specified tag ID.
|
||||
ListIDsByTagID(ctx context.Context, tenantID uint64, kbID, tagID string) ([]string, error)
|
||||
}
|
||||
|
||||
@@ -3,15 +3,16 @@
|
||||
-- a "未分类" tag, but this may affect entries that were intentionally tagged.
|
||||
|
||||
-- WARNING: This rollback is destructive and should only be used if absolutely necessary.
|
||||
-- It will set tag_id to empty string for all FAQ entries that reference "未分类" tags.
|
||||
-- It will set tag_id to empty string for all chunks, knowledges, and embeddings that reference "未分类" tags.
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
kb_record RECORD;
|
||||
untagged_tag_id VARCHAR(36);
|
||||
updated_chunks INT;
|
||||
updated_knowledges INT;
|
||||
BEGIN
|
||||
RAISE NOTICE '[Migration 000008 Rollback] WARNING: This rollback will clear tag_id for all FAQ entries referencing "未分类" tags';
|
||||
RAISE NOTICE '[Migration 000008 Rollback] WARNING: This rollback will clear tag_id for all entries referencing "未分类" tags';
|
||||
|
||||
-- Find all "未分类" tags
|
||||
FOR kb_record IN
|
||||
@@ -21,16 +22,25 @@ BEGIN
|
||||
LOOP
|
||||
untagged_tag_id := kb_record.id;
|
||||
|
||||
-- Clear tag_id for chunks referencing this tag
|
||||
-- Clear tag_id for chunks referencing this tag (both faq and document types)
|
||||
UPDATE chunks
|
||||
SET tag_id = '', updated_at = NOW()
|
||||
WHERE tag_id = untagged_tag_id
|
||||
AND chunk_type = 'faq';
|
||||
AND chunk_type IN ('faq', 'document');
|
||||
|
||||
GET DIAGNOSTICS updated_chunks = ROW_COUNT;
|
||||
RAISE NOTICE '[Migration 000008 Rollback] Cleared tag_id for % chunks referencing tag %',
|
||||
updated_chunks, untagged_tag_id;
|
||||
|
||||
-- Clear tag_id for knowledges referencing this tag
|
||||
UPDATE knowledges
|
||||
SET tag_id = '', updated_at = NOW()
|
||||
WHERE tag_id = untagged_tag_id;
|
||||
|
||||
GET DIAGNOSTICS updated_knowledges = ROW_COUNT;
|
||||
RAISE NOTICE '[Migration 000008 Rollback] Cleared tag_id for % knowledges referencing tag %',
|
||||
updated_knowledges, untagged_tag_id;
|
||||
|
||||
-- Clear tag_id in embeddings if column exists
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
|
||||
@@ -1,19 +1,30 @@
|
||||
-- Migration: Create "未分类" tag for each knowledge base that has untagged FAQ entries
|
||||
-- and update those entries to reference the new tag
|
||||
-- Migration: Create "未分类" tag for each knowledge base that has untagged entries
|
||||
-- and update chunks, knowledges, and embeddings to reference the new tag
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
kb_record RECORD;
|
||||
new_tag_id VARCHAR(36);
|
||||
updated_chunks INT;
|
||||
updated_knowledges INT;
|
||||
updated_embeddings INT;
|
||||
BEGIN
|
||||
-- Find all knowledge bases that have FAQ chunks with empty or NULL tag_id
|
||||
-- Find all knowledge bases that have untagged chunks or knowledges
|
||||
FOR kb_record IN
|
||||
SELECT DISTINCT c.tenant_id, c.knowledge_base_id
|
||||
FROM chunks c
|
||||
WHERE c.chunk_type = 'faq'
|
||||
AND (c.tag_id = '' OR c.tag_id IS NULL)
|
||||
SELECT DISTINCT tenant_id, knowledge_base_id
|
||||
FROM (
|
||||
-- Untagged chunks (FAQ or document)
|
||||
SELECT c.tenant_id, c.knowledge_base_id
|
||||
FROM chunks c
|
||||
WHERE c.chunk_type IN ('faq', 'document')
|
||||
AND (c.tag_id = '' OR c.tag_id IS NULL)
|
||||
UNION
|
||||
-- Untagged knowledges (documents)
|
||||
SELECT k.tenant_id, k.knowledge_base_id
|
||||
FROM knowledges k
|
||||
WHERE k.deleted_at IS NULL
|
||||
AND (k.tag_id = '' OR k.tag_id IS NULL)
|
||||
) AS untagged
|
||||
LOOP
|
||||
-- Check if "未分类" tag already exists for this knowledge base
|
||||
SELECT id INTO new_tag_id
|
||||
@@ -35,18 +46,30 @@ BEGIN
|
||||
new_tag_id, kb_record.tenant_id, kb_record.knowledge_base_id;
|
||||
END IF;
|
||||
|
||||
-- Update chunks with empty tag_id to use the new tag
|
||||
-- Update chunks with empty tag_id (both faq and document types)
|
||||
UPDATE chunks
|
||||
SET tag_id = new_tag_id, updated_at = NOW()
|
||||
WHERE tenant_id = kb_record.tenant_id
|
||||
AND knowledge_base_id = kb_record.knowledge_base_id
|
||||
AND chunk_type = 'faq'
|
||||
AND chunk_type IN ('faq', 'document')
|
||||
AND (tag_id = '' OR tag_id IS NULL);
|
||||
|
||||
GET DIAGNOSTICS updated_chunks = ROW_COUNT;
|
||||
RAISE NOTICE '[Migration 000008] Updated % chunks for tenant_id: %, kb_id: %',
|
||||
updated_chunks, kb_record.tenant_id, kb_record.knowledge_base_id;
|
||||
|
||||
-- Update knowledges with empty tag_id (documents)
|
||||
UPDATE knowledges
|
||||
SET tag_id = new_tag_id, updated_at = NOW()
|
||||
WHERE tenant_id = kb_record.tenant_id
|
||||
AND knowledge_base_id = kb_record.knowledge_base_id
|
||||
AND deleted_at IS NULL
|
||||
AND (tag_id = '' OR tag_id IS NULL);
|
||||
|
||||
GET DIAGNOSTICS updated_knowledges = ROW_COUNT;
|
||||
RAISE NOTICE '[Migration 000008] Updated % knowledges for tenant_id: %, kb_id: %',
|
||||
updated_knowledges, kb_record.tenant_id, kb_record.knowledge_base_id;
|
||||
|
||||
-- Update embeddings with empty tag_id (if embeddings table exists and has tag_id column)
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
@@ -60,7 +83,7 @@ BEGIN
|
||||
SELECT id FROM chunks
|
||||
WHERE tenant_id = kb_record.tenant_id
|
||||
AND knowledge_base_id = kb_record.knowledge_base_id
|
||||
AND chunk_type = 'faq'
|
||||
AND chunk_type IN ('faq', 'document')
|
||||
);
|
||||
|
||||
GET DIAGNOSTICS updated_embeddings = ROW_COUNT;
|
||||
@@ -69,5 +92,5 @@ BEGIN
|
||||
END IF;
|
||||
END LOOP;
|
||||
|
||||
RAISE NOTICE '[Migration 000008] Completed migration of untagged FAQ entries';
|
||||
RAISE NOTICE '[Migration 000008] Completed migration of untagged entries';
|
||||
END $$;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
-- Remove last_faq_import_result column from knowledge table
|
||||
|
||||
ALTER TABLE knowledges DROP COLUMN last_faq_import_result;
|
||||
ALTER TABLE knowledges DROP COLUMN IF EXISTS last_faq_import_result;
|
||||
@@ -1,4 +1,4 @@
|
||||
-- Add last_faq_import_result column to knowledge table
|
||||
-- This field stores the latest FAQ import result for FAQ type knowledge
|
||||
|
||||
ALTER TABLE knowledges ADD COLUMN last_faq_import_result JSON DEFAULT NULL;
|
||||
ALTER TABLE knowledges ADD COLUMN IF NOT EXISTS last_faq_import_result JSON DEFAULT NULL;
|
||||
Reference in New Issue
Block a user