feat(rag): 添加相对路径搜索功能并优化文件显示

- 在RagFileRepositoryImpl中新增relativePath字段和路径模式构建方法
- 实现buildRelativePathPattern方法用于构建相对路径搜索模式
- 修改page方法添加相对路径模糊查询支持
- 在RagFileReq DTO中添加relativePath参数字段
- 优化KnowledgeBaseDetail页面中的文件名显示逻辑
- 添加normalizePath函数处理文件路径规范化显示
This commit is contained in:
2026-01-30 21:50:53 +08:00
parent ca7ff56610
commit cbad129ce4
3 changed files with 39 additions and 30 deletions

View File

@@ -20,6 +20,8 @@ import java.util.List;
*/
@Repository
public class RagFileRepositoryImpl extends CrudRepository<RagFileMapper, RagFile> implements RagFileRepository {
private static final String RELATIVE_PATH_KEY = "\"relativePath\":\"";
private static final String PATH_SEPARATOR = "/";
@Override
public void removeByKnowledgeBaseId(String knowledgeBaseId) {
lambdaUpdate().eq(RagFile::getKnowledgeBaseId, knowledgeBaseId).remove();
@@ -42,9 +44,25 @@ public class RagFileRepositoryImpl extends CrudRepository<RagFileMapper, RagFile
@Override
public IPage<RagFile> page(IPage<RagFile> page, RagFileReq request) {
String relativePathPattern = buildRelativePathPattern(request.getRelativePath());
return lambdaQuery()
.eq(RagFile::getKnowledgeBaseId, request.getKnowledgeBaseId())
.like(StringUtils.hasText(request.getFileName()), RagFile::getFileName, request.getFileName())
.like(StringUtils.hasText(relativePathPattern), RagFile::getMetadata, relativePathPattern)
.page(page);
}
private String buildRelativePathPattern(String relativePath) {
if (!StringUtils.hasText(relativePath)) {
return "";
}
String normalized = relativePath.replace("\\", PATH_SEPARATOR).trim();
while (normalized.startsWith(PATH_SEPARATOR)) {
normalized = normalized.substring(1);
}
if (!StringUtils.hasText(normalized)) {
return "";
}
return RELATIVE_PATH_KEY + normalized;
}
}

View File

@@ -14,5 +14,6 @@ import lombok.Setter;
@Getter
public class RagFileReq extends PagingQuery {
private String fileName;
private String relativePath;
private String knowledgeBaseId;
}

View File

@@ -124,10 +124,14 @@ const KnowledgeBaseDetailPage: React.FC = () => {
const pageSize = 200;
let page = 0;
let combined: KBFile[] = [];
const currentPrefix = normalizePrefix(filePrefix);
const keyword = fileKeyword.trim();
while (true) {
const { data } = await queryKnowledgeBaseFilesUsingGet(id, {
page,
size: pageSize,
...(currentPrefix ? { relativePath: currentPrefix } : {}),
...(keyword ? { fileName: keyword } : {}),
});
const content = Array.isArray(data?.content) ? data.content : [];
combined = combined.concat(content.map(mapFileData));
@@ -146,14 +150,19 @@ const KnowledgeBaseDetailPage: React.FC = () => {
} finally {
setFilesLoading(false);
}
}, [id, message]);
}, [id, filePrefix, fileKeyword, message]);
useEffect(() => {
if (id) {
fetchKnowledgeBaseDetails(id);
}
}, [id, fetchKnowledgeBaseDetails]);
useEffect(() => {
if (id) {
fetchFiles();
}
}, [id, fetchKnowledgeBaseDetails, fetchFiles]);
}, [id, fetchFiles]);
// File table logic
const handleDeleteFile = async (file: KBFileRow) => {
@@ -257,8 +266,7 @@ const KnowledgeBaseDetailPage: React.FC = () => {
const normalizedPrefix = useMemo(() => normalizePrefix(filePrefix), [filePrefix]);
const { rows: fileRows, total: fileTotal } = useMemo(() => {
const keyword = fileKeyword.trim().toLowerCase();
const folderMap = new Map<string, { name: string; fileCount: number; hasMatch: boolean }>();
const folderMap = new Map<string, { name: string; fileCount: number }>();
const fileItems: KBFileRow[] = [];
allFiles.forEach((file) => {
@@ -271,31 +279,22 @@ const KnowledgeBaseDetailPage: React.FC = () => {
return;
}
const leafName = segments[0];
const fileMatches =
!keyword ||
leafName.toLowerCase().includes(keyword) ||
fullPath.toLowerCase().includes(keyword);
if (segments.length > 1) {
const folderName = leafName;
const entry = folderMap.get(folderName) || {
name: folderName,
fileCount: 0,
hasMatch: false,
};
entry.fileCount += 1;
if (fileMatches) {
entry.hasMatch = true;
}
folderMap.set(folderName, entry);
return;
}
if (!fileMatches) {
return;
}
const displayName = file.fileName || leafName;
const normalizedFileName = normalizePath(file.fileName);
const displayName = normalizedFileName.includes(PATH_SEPARATOR)
? leafName
: file.fileName || leafName;
fileItems.push({
...file,
name: displayName,
@@ -304,17 +303,8 @@ const KnowledgeBaseDetailPage: React.FC = () => {
});
});
const folderItems: KBFileRow[] = Array.from(folderMap.values())
.filter((entry) => {
if (!keyword) {
return true;
}
return (
entry.hasMatch ||
entry.name.toLowerCase().includes(keyword)
);
})
.map((entry) =>
const folderItems: KBFileRow[] = Array.from(folderMap.values()).map(
(entry) =>
({
id: `directory-${normalizedPrefix}${entry.name}`,
fileName: entry.name,
@@ -333,7 +323,7 @@ const KnowledgeBaseDetailPage: React.FC = () => {
fullPath: `${normalizedPrefix}${entry.name}/`,
fileCount: entry.fileCount,
}) as KBFileRow
);
);
const sortByName = (a: KBFileRow, b: KBFileRow) =>
(a.displayName || a.name || "").localeCompare(
@@ -346,7 +336,7 @@ const KnowledgeBaseDetailPage: React.FC = () => {
const combined = [...folderItems, ...fileItems];
return { rows: combined, total: combined.length };
}, [allFiles, fileKeyword, knowledgeBase?.id, normalizedPrefix]);
}, [allFiles, knowledgeBase?.id, normalizedPrefix]);
const filePageCurrent = filePagination.current;
const filePageSize = filePagination.pageSize;