Feat: add file path sorting for document manager

- Add file_path sorting support to all database backends (JSON, Redis, PostgreSQL, MongoDB)
- Implement smart column header switching between "ID" and "File Name" based on display mode
- Add automatic sort field switching when toggling between ID and file name display
- Create composite indexes for workspace+file_path in PostgreSQL and MongoDB for better query performance
- Update frontend to maintain sort state when switching display modes
- Add internationalization support for "fileName" in English and Chinese locales

This enhancement improves user experience by providing intuitive file-based sorting
while maintaining performance through optimized database indexes.
This commit is contained in:
yangdx 2025-07-30 18:46:55 +08:00
parent e60c26ea77
commit 0eac1a883a
9 changed files with 40 additions and 11 deletions

View file

@ -510,7 +510,7 @@ class DocumentsRequest(BaseModel):
page_size: int = Field(
default=50, ge=10, le=200, description="Number of documents per page (10-200)"
)
sort_field: Literal["created_at", "updated_at", "id"] = Field(
sort_field: Literal["created_at", "updated_at", "id", "file_path"] = Field(
default="updated_at", description="Field to sort by"
)
sort_direction: Literal["asc", "desc"] = Field(

View file

@ -201,7 +201,7 @@ class JsonDocStatusStorage(DocStatusStorage):
elif page_size > 200:
page_size = 200
if sort_field not in ["created_at", "updated_at", "id"]:
if sort_field not in ["created_at", "updated_at", "id", "file_path"]:
sort_field = "updated_at"
if sort_direction.lower() not in ["asc", "desc"]:

View file

@ -503,6 +503,7 @@ class MongoDocStatusStorage(DocStatusStorage):
{"name": "updated_at", "keys": [("updated_at", -1)]},
{"name": "created_at", "keys": [("created_at", -1)]},
{"name": "id", "keys": [("_id", 1)]},
{"name": "file_path", "keys": [("file_path", 1)]},
]
# Check which indexes already exist
@ -553,7 +554,7 @@ class MongoDocStatusStorage(DocStatusStorage):
elif page_size > 200:
page_size = 200
if sort_field not in ["created_at", "updated_at", "_id"]:
if sort_field not in ["created_at", "updated_at", "_id", "file_path"]:
sort_field = "updated_at"
if sort_direction.lower() not in ["asc", "desc"]:

View file

@ -948,6 +948,11 @@ class PostgreSQLDB:
"sql": "CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_lightrag_doc_status_workspace_id ON LIGHTRAG_DOC_STATUS (workspace, id)",
"description": "Index for workspace + id sorting",
},
{
"name": "idx_lightrag_doc_status_workspace_file_path",
"sql": "CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_lightrag_doc_status_workspace_file_path ON LIGHTRAG_DOC_STATUS (workspace, file_path)",
"description": "Index for workspace + file_path sorting",
},
]
for index in indexes:
@ -2066,7 +2071,7 @@ class PGDocStatusStorage(DocStatusStorage):
elif page_size > 200:
page_size = 200
if sort_field not in ["created_at", "updated_at", "id"]:
if sort_field not in ["created_at", "updated_at", "id", "file_path"]:
sort_field = "updated_at"
if sort_direction.lower() not in ["asc", "desc"]:

View file

@ -947,7 +947,7 @@ class RedisDocStatusStorage(DocStatusStorage):
elif page_size > 200:
page_size = 200
if sort_field not in ["created_at", "updated_at", "id"]:
if sort_field not in ["created_at", "updated_at", "id", "file_path"]:
sort_field = "updated_at"
if sort_direction.lower() not in ["asc", "desc"]:

View file

@ -189,7 +189,7 @@ export type DocumentsRequest = {
status_filter?: DocStatus | null
page: number
page_size: number
sort_field: 'created_at' | 'updated_at' | 'id'
sort_field: 'created_at' | 'updated_at' | 'id' | 'file_path'
sort_direction: 'asc' | 'desc'
}

View file

@ -145,7 +145,7 @@ const pulseStyle = `
`;
// Type definitions for sort field and direction
type SortField = 'created_at' | 'updated_at' | 'id';
type SortField = 'created_at' | 'updated_at' | 'id' | 'file_path';
type SortDirection = 'asc' | 'desc';
export default function DocumentManager() {
@ -234,9 +234,16 @@ export default function DocumentManager() {
// Handle sort column click
const handleSort = (field: SortField) => {
const newDirection = (sortField === field && sortDirection === 'desc') ? 'asc' : 'desc';
let actualField = field;
setSortField(field);
// When clicking the first column, determine the actual sort field based on showFileName
if (field === 'id') {
actualField = showFileName ? 'file_path' : 'id';
}
const newDirection = (sortField === actualField && sortDirection === 'desc') ? 'asc' : 'desc';
setSortField(actualField);
setSortDirection(newDirection);
// Reset page to 1 when sorting changes
@ -600,6 +607,17 @@ export default function DocumentManager() {
}, [fetchDocuments])
// Handle showFileName change - switch sort field if currently sorting by first column
useEffect(() => {
// Only switch if currently sorting by the first column (id or file_path)
if (sortField === 'id' || sortField === 'file_path') {
const newSortField = showFileName ? 'file_path' : 'id';
if (sortField !== newSortField) {
setSortField(newSortField);
}
}
}, [showFileName, sortField]);
// Central effect to handle all data fetching
useEffect(() => {
if (currentTab === 'documents') {
@ -796,8 +814,11 @@ export default function DocumentManager() {
className="cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-800 select-none"
>
<div className="flex items-center">
{t('documentPanel.documentManager.columns.id')}
{sortField === 'id' && (
{showFileName
? t('documentPanel.documentManager.columns.fileName')
: t('documentPanel.documentManager.columns.id')
}
{((sortField === 'id' && !showFileName) || (sortField === 'file_path' && showFileName)) && (
<span className="ml-1">
{sortDirection === 'asc' ? <ArrowUpIcon size={14} /> : <ArrowDownIcon size={14} />}
</span>

View file

@ -125,6 +125,7 @@
"emptyDescription": "There are no uploaded documents yet.",
"columns": {
"id": "ID",
"fileName": "File Name",
"summary": "Summary",
"status": "Status",
"length": "Length",

View file

@ -125,6 +125,7 @@
"emptyDescription": "还没有上传任何文档",
"columns": {
"id": "ID",
"fileName": "文件名",
"summary": "摘要",
"status": "状态",
"length": "长度",