Files
ragflow/internal/service/file.go
Jin Hai 70e9743ef1 RAGFlow go API server (#13240)
# RAGFlow Go Implementation Plan 🚀

This repository tracks the progress of porting RAGFlow to Go. We'll
implement core features and provide performance comparisons between
Python and Go versions.

## Implementation Checklist

- [x] User Management APIs
- [x] Dataset Management Operations
- [x] Retrieval Test
- [x] Chat Management Operations
- [x] Infinity Go SDK

---------

Signed-off-by: Jin Hai <haijin.chn@gmail.com>
Co-authored-by: Yingfeng Zhang <yingfeng.zhang@gmail.com>
2026-03-04 19:17:16 +08:00

221 lines
6.0 KiB
Go

//
// Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package service
import (
"ragflow/internal/dao"
"ragflow/internal/model"
)
// FileService file service
type FileService struct {
fileDAO *dao.FileDAO
file2DocumentDAO *dao.File2DocumentDAO
}
// NewFileService create file service
func NewFileService() *FileService {
return &FileService{
fileDAO: dao.NewFileDAO(),
file2DocumentDAO: dao.NewFile2DocumentDAO(),
}
}
// FileInfo file info with additional fields
type FileInfo struct {
*model.File
Size int64 `json:"size"`
KbsInfo []map[string]interface{} `json:"kbs_info"`
HasChildFolder bool `json:"has_child_folder,omitempty"`
}
// ListFilesResponse list files response
type ListFilesResponse struct {
Total int64 `json:"total"`
Files []map[string]interface{} `json:"files"`
ParentFolder map[string]interface{} `json:"parent_folder"`
}
// GetRootFolder gets or creates root folder for tenant
func (s *FileService) GetRootFolder(tenantID string) (map[string]interface{}, error) {
file, err := s.fileDAO.GetRootFolder(tenantID)
if err != nil {
return nil, err
}
return s.toFileResponse(file), nil
}
// ListFiles lists files by parent folder ID
func (s *FileService) ListFiles(tenantID, pfID string, page, pageSize int, orderby string, desc bool, keywords string) (*ListFilesResponse, error) {
// If pfID is empty, get root folder
if pfID == "" {
rootFolder, err := s.fileDAO.GetRootFolder(tenantID)
if err != nil {
return nil, err
}
pfID = rootFolder.ID
}
// Check if parent folder exists
if _, err := s.fileDAO.GetByID(pfID); err != nil {
return nil, err
}
// Get files by parent folder ID
files, total, err := s.fileDAO.GetByPfID(tenantID, pfID, page, pageSize, orderby, desc, keywords)
if err != nil {
return nil, err
}
// Get parent folder
parentFolder, err := s.fileDAO.GetParentFolder(pfID)
if err != nil {
return nil, err
}
// Process files to add additional info
fileResponses := make([]map[string]interface{}, len(files))
for i, file := range files {
fileInfo := s.toFileInfo(file)
// If folder, calculate size and check for child folders
if file.Type == "folder" {
folderSize, err := s.fileDAO.GetFolderSize(file.ID)
if err == nil {
fileInfo.Size = folderSize
}
hasChild, err := s.fileDAO.HasChildFolder(file.ID)
if err == nil {
fileInfo.HasChildFolder = hasChild
}
fileInfo.KbsInfo = []map[string]interface{}{}
} else {
// Get KB info for non-folder files
kbsInfo, err := s.file2DocumentDAO.GetKBInfoByFileID(file.ID)
if err != nil {
kbsInfo = []map[string]interface{}{}
}
fileInfo.KbsInfo = kbsInfo
}
fileResponses[i] = s.fileInfoToResponse(fileInfo)
}
return &ListFilesResponse{
Total: total,
Files: fileResponses,
ParentFolder: s.toFileResponse(parentFolder),
}, nil
}
// toFileResponse converts file model to response format
func (s *FileService) toFileResponse(file *model.File) map[string]interface{} {
result := map[string]interface{}{
"id": file.ID,
"parent_id": file.ParentID,
"tenant_id": file.TenantID,
"created_by": file.CreatedBy,
"name": file.Name,
"size": file.Size,
"type": file.Type,
"create_time": file.CreateTime,
"update_time": file.UpdateTime,
}
if file.Location != nil {
result["location"] = *file.Location
}
result["source_type"] = file.SourceType
return result
}
// toFileInfo converts file model to FileInfo
func (s *FileService) toFileInfo(file *model.File) *FileInfo {
return &FileInfo{
File: file,
Size: file.Size,
KbsInfo: []map[string]interface{}{},
HasChildFolder: false,
}
}
// fileInfoToResponse converts FileInfo to response map
func (s *FileService) fileInfoToResponse(info *FileInfo) map[string]interface{} {
result := map[string]interface{}{
"id": info.File.ID,
"parent_id": info.File.ParentID,
"tenant_id": info.File.TenantID,
"created_by": info.File.CreatedBy,
"name": info.File.Name,
"size": info.Size,
"type": info.File.Type,
"create_time": info.File.CreateTime,
"update_time": info.File.UpdateTime,
"kbs_info": info.KbsInfo,
}
if info.File.Location != nil {
result["location"] = *info.File.Location
}
result["source_type"] = info.File.SourceType
if info.File.Type == "folder" {
result["has_child_folder"] = info.HasChildFolder
}
return result
}
// GetParentFolder gets parent folder of a file
func (s *FileService) GetParentFolder(fileID string) (map[string]interface{}, error) {
// Check if file exists
if _, err := s.fileDAO.GetByID(fileID); err != nil {
return nil, err
}
// Get parent folder
parentFolder, err := s.fileDAO.GetParentFolder(fileID)
if err != nil {
return nil, err
}
return s.toFileResponse(parentFolder), nil
}
// GetAllParentFolders gets all parent folders in path
func (s *FileService) GetAllParentFolders(fileID string) ([]map[string]interface{}, error) {
// Check if file exists
if _, err := s.fileDAO.GetByID(fileID); err != nil {
return nil, err
}
// Get all parent folders
parentFolders, err := s.fileDAO.GetAllParentFolders(fileID)
if err != nil {
return nil, err
}
// Convert to response format
result := make([]map[string]interface{}, len(parentFolders))
for i, folder := range parentFolders {
result[i] = s.toFileResponse(folder)
}
return result, nil
}