From 53afc323499aa8cc63e463c0c6a4376168a5dc13 Mon Sep 17 00:00:00 2001 From: Wang Qi Date: Mon, 27 Jul 2026 10:24:13 +0800 Subject: [PATCH] Fix get datasets owner retrieve the whole dataset (#17370) --- api/apps/restful_apis/dataset_api.py | 14 +++++++++ api/apps/services/dataset_api_service.py | 7 +++++ api/db/services/knowledgebase_service.py | 15 +++++++++ internal/dao/kb.go | 15 +++++++++ internal/entity/dataset.go | 7 +++++ internal/handler/dataset.go | 10 ++++++ internal/service/dataset/crud.go | 34 +++++++++++++++++++++ web/src/hooks/use-knowledge-request.ts | 21 +++++++++++++ web/src/interfaces/database/dataset.ts | 8 +++++ web/src/pages/datasets/use-select-owners.ts | 7 ++--- web/src/services/knowledge-service.ts | 2 ++ web/src/utils/api.ts | 1 + 12 files changed, 137 insertions(+), 4 deletions(-) diff --git a/api/apps/restful_apis/dataset_api.py b/api/apps/restful_apis/dataset_api.py index c931e9e607..7c8764742e 100644 --- a/api/apps/restful_apis/dataset_api.py +++ b/api/apps/restful_apis/dataset_api.py @@ -363,6 +363,20 @@ def list_datasets(tenant_id): items: type: object """ + if request.args.get("type") == "filter": + try: + success, result = dataset_api_service.list_dataset_filters(tenant_id) + if success: + return get_result(data=result) + else: + return get_error_data_result(message=result) + except OperationalError as e: + logging.exception(e) + return get_error_data_result(message="Database operation failed") + except Exception as e: + logging.exception(e) + return get_error_data_result(message="Internal server error") + args, err = validate_and_parse_request_args(request, ListDatasetReq) if err is not None: return get_error_argument_result(err) diff --git a/api/apps/services/dataset_api_service.py b/api/apps/services/dataset_api_service.py index 1687cf5729..002de87a98 100644 --- a/api/apps/services/dataset_api_service.py +++ b/api/apps/services/dataset_api_service.py @@ -458,6 +458,13 @@ def list_datasets(tenant_id: str, args: dict): return True, {"data": response_data_list, "total": total} +def list_dataset_filters(tenant_id: str): + tenants = TenantService.get_joined_tenants_by_user_id(tenant_id) + tenant_ids = [m["tenant_id"] for m in tenants] + owners = KnowledgebaseService.get_owner_filter(tenant_ids, tenant_id) + return True, {"filter": {"owner": owners}, "total": sum(owner["count"] for owner in owners)} + + async def get_knowledge_graph(dataset_id: str, tenant_id: str): """ Get knowledge graph for a dataset. diff --git a/api/db/services/knowledgebase_service.py b/api/db/services/knowledgebase_service.py index 5e21a02ade..e28fcbafbc 100644 --- a/api/db/services/knowledgebase_service.py +++ b/api/db/services/knowledgebase_service.py @@ -501,6 +501,21 @@ class KnowledgebaseService(CommonService): return list(kbs.dicts()), total + @classmethod + @DB.connection_context() + def get_owner_filter(cls, joined_tenant_ids, user_id): + owners = ( + cls.model.select( + cls.model.tenant_id.alias("id"), + User.nickname.alias("label"), + fn.COUNT(cls.model.id).alias("count"), + ) + .join(User, on=(cls.model.tenant_id == User.id)) + .where(cls._visibility_and_status_filter(joined_tenant_ids, user_id)) + .group_by(cls.model.tenant_id, User.nickname) + ) + return list(owners.dicts()) + @classmethod @DB.connection_context() def accessible(cls, kb_id, user_id): diff --git a/internal/dao/kb.go b/internal/dao/kb.go index 1dabf86997..0f0c403db5 100644 --- a/internal/dao/kb.go +++ b/internal/dao/kb.go @@ -227,6 +227,21 @@ func (dao *KnowledgebaseDAO) GetByTenantIDs(ctx context.Context, db *gorm.DB, te return kbs, total, nil } +// GetOwnerFilter returns owner counts for datasets visible to a user. +func (dao *KnowledgebaseDAO) GetOwnerFilter(tenantIDs []string, userID string) ([]*entity.DatasetOwnerFilter, error) { + owners := make([]*entity.DatasetOwnerFilter, 0) + + err := DB.Model(&entity.Knowledgebase{}). + Select("knowledgebase.tenant_id as id, user.nickname as label, COUNT(knowledgebase.id) as count"). + Joins("LEFT JOIN user ON knowledgebase.tenant_id = user.id"). + Where("((knowledgebase.tenant_id IN ? AND knowledgebase.permission = ?) OR knowledgebase.tenant_id = ?) AND knowledgebase.status = ?", + tenantIDs, string(entity.TenantPermissionTeam), userID, string(entity.StatusValid)). + Group("knowledgebase.tenant_id, user.nickname"). + Scan(&owners).Error + + return owners, err +} + // GetAllByTenantIDs retrieves all permitted knowledge bases by tenant IDs // This matches the Python get_all_kb_by_tenant_ids method func (dao *KnowledgebaseDAO) GetAllByTenantIDs(ctx context.Context, db *gorm.DB, tenantIDs []string, userID string) ([]*entity.Knowledgebase, error) { diff --git a/internal/entity/dataset.go b/internal/entity/dataset.go index 22d1800511..0e74f16ff3 100644 --- a/internal/entity/dataset.go +++ b/internal/entity/dataset.go @@ -239,3 +239,10 @@ type KnowledgebaseListItem struct { TenantAvatar *string `json:"tenant_avatar,omitempty"` UpdateTime *int64 `json:"update_time,omitempty"` } + +// DatasetOwnerFilter represents an owner option in dataset filter responses. +type DatasetOwnerFilter struct { + ID string `json:"id"` + Label string `json:"label"` + Count int64 `json:"count"` +} diff --git a/internal/handler/dataset.go b/internal/handler/dataset.go index 9f2ecdc717..672e255cc0 100644 --- a/internal/handler/dataset.go +++ b/internal/handler/dataset.go @@ -77,6 +77,16 @@ func (h *DatasetsHandler) ListDatasets(c *gin.Context) { return } + if c.Query("type") == "filter" { + data, code, err := h.datasetsService.ListDatasetFilters(user.ID) + if err != nil { + common.ErrorWithCode(c, code, err.Error()) + return + } + common.SuccessNoMessage(c, data) + return + } + page := 1 if pageStr := c.Query("page"); pageStr != "" { if p, err := strconv.Atoi(pageStr); err == nil && p > 0 { diff --git a/internal/service/dataset/crud.go b/internal/service/dataset/crud.go index 44d1fc353e..fd7dcd1a78 100644 --- a/internal/service/dataset/crud.go +++ b/internal/service/dataset/crud.go @@ -393,6 +393,40 @@ func (d *DatasetService) ListDatasets(ctx context.Context, id, name string, page return data, total, common.CodeSuccess, nil } +func (d *DatasetService) ListDatasetFilters(userID string) (map[string]interface{}, common.ErrorCode, error) { + joinedTenants, err := d.tenantDAO.GetJoinedTenantsByUserID(userID) + if err != nil { + return nil, common.CodeServerError, errors.New("database operation failed") + } + + tenantIDs := make([]string, 0, len(joinedTenants)) + for _, joinedTenant := range joinedTenants { + if joinedTenant == nil || joinedTenant.TenantID == "" { + continue + } + tenantIDs = append(tenantIDs, joinedTenant.TenantID) + } + + owners, err := d.kbDAO.GetOwnerFilter(tenantIDs, userID) + if err != nil { + return nil, common.CodeServerError, errors.New("database operation failed") + } + + var total int64 + for _, owner := range owners { + if owner != nil { + total += owner.Count + } + } + + return map[string]interface{}{ + "filter": map[string]interface{}{ + "owner": owners, + }, + "total": total, + }, common.CodeSuccess, nil +} + // ptrStringValue safely dereferences a *string. func ptrStringValue(s *string) string { if s == nil { diff --git a/web/src/hooks/use-knowledge-request.ts b/web/src/hooks/use-knowledge-request.ts index bd03e00b6b..6d2009c635 100644 --- a/web/src/hooks/use-knowledge-request.ts +++ b/web/src/hooks/use-knowledge-request.ts @@ -8,6 +8,7 @@ import { IArtifactPage, IArtifactTopic, IDataset, + IDatasetFilter, IDatasetListResult, IKnowledgeGraph, INextTestingResult, @@ -33,6 +34,7 @@ import kbService, { getWikiCommit, listArtifactTopics, listArtifacts, + datasetFilter, listDataset, listTag, listWikiCommits, @@ -65,6 +67,7 @@ import { useSetPaginationParams } from './route-hook'; export const enum KnowledgeApiAction { FetchKnowledgeListByPage = 'fetchKnowledgeListByPage', + FetchDatasetFilter = 'fetchDatasetFilter', CreateKnowledge = 'createKnowledge', DeleteKnowledge = 'deleteKnowledge', SaveKnowledge = 'saveKnowledge', @@ -209,6 +212,24 @@ export const useFetchNextKnowledgeListByPage = () => { }; }; +export const useGetDatasetFilter = (): { filter: IDatasetFilter } => { + const { data } = useQuery({ + queryKey: [KnowledgeApiAction.FetchDatasetFilter], + queryFn: async () => { + const { data } = await datasetFilter(); + if (data.code === 0) { + return data.data; + } + }, + }); + + return { + filter: data?.filter || { + owner: [], + }, + }; +}; + export const useCreateKnowledge = () => { const queryClient = useQueryClient(); const { diff --git a/web/src/interfaces/database/dataset.ts b/web/src/interfaces/database/dataset.ts index 98c303a50b..7a729c5eab 100644 --- a/web/src/interfaces/database/dataset.ts +++ b/web/src/interfaces/database/dataset.ts @@ -50,6 +50,14 @@ export interface IDataset { connectors: IConnector[]; } +export type IDatasetFilter = { + owner: Array<{ + id: string; + label: string; + count: number; + }>; +}; + interface Parserconfig { auto_keywords: number; auto_questions: number; diff --git a/web/src/pages/datasets/use-select-owners.ts b/web/src/pages/datasets/use-select-owners.ts index 51fcf5a404..925acd63a5 100644 --- a/web/src/pages/datasets/use-select-owners.ts +++ b/web/src/pages/datasets/use-select-owners.ts @@ -1,14 +1,13 @@ import { FilterCollection } from '@/components/list-filter-bar/interface'; -import { useFetchAllKnowledgeList } from '@/hooks/use-knowledge-request'; -import { buildOwnersFilter } from '@/utils/list-filter-util'; +import { useGetDatasetFilter } from '@/hooks/use-knowledge-request'; import { useTranslation } from 'react-i18next'; export function useSelectOwners() { - const { list } = useFetchAllKnowledgeList(); + const { filter } = useGetDatasetFilter(); const { t } = useTranslation(); const filters: FilterCollection[] = [ - buildOwnersFilter(list, undefined, t('common.owner')), + { field: 'owner', list: filter.owner, label: t('common.owner') }, ]; return filters; diff --git a/web/src/services/knowledge-service.ts b/web/src/services/knowledge-service.ts index 3b21f8790b..cbf2e257f0 100644 --- a/web/src/services/knowledge-service.ts +++ b/web/src/services/knowledge-service.ts @@ -267,6 +267,8 @@ export function deleteKnowledgeGraph(knowledgeId: string) { export const listDataset = (params?: IFetchKnowledgeListRequestParams) => request.get(api.kbList, { params }); +export const datasetFilter = () => request.get(api.datasetFilter); + export const updateKb = (datasetId: string, data: Record) => request.put(api.updateKb(datasetId), { data }); diff --git a/web/src/utils/api.ts b/web/src/utils/api.ts index 4e637a3613..7cfcab6217 100644 --- a/web/src/utils/api.ts +++ b/web/src/utils/api.ts @@ -143,6 +143,7 @@ export default { checkEmbedding: (datasetId: string) => `${restAPIv1}/datasets/${datasetId}/embedding/check`, kbList: `${restAPIv1}/datasets`, + datasetFilter: `${restAPIv1}/datasets?type=filter`, createKb: `${restAPIv1}/datasets`, updateKb: (datasetId: string) => `${restAPIv1}/datasets/${datasetId}`, rmKb: `${restAPIv1}/datasets`,