Skip to content

Commit 5d00aa9

Browse files
committed
feat: add index update timestamps to document schema and service
1 parent c500ec7 commit 5d00aa9

7 files changed

Lines changed: 148 additions & 93 deletions

File tree

aperag/api/components/schemas/document.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,18 @@ document:
3939
- COMPLETE
4040
- FAILED
4141
- SKIPPED
42+
vector_index_updated:
43+
type: string
44+
format: date-time
45+
description: Vector index last updated time
46+
fulltext_index_updated:
47+
type: string
48+
format: date-time
49+
description: Fulltext index last updated time
50+
graph_index_updated:
51+
type: string
52+
format: date-time
53+
description: Graph index last updated time
4254
config:
4355
type: string
4456
size:

aperag/schema/view_models.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# generated by datamodel-codegen:
22
# filename: openapi.merged.yaml
3-
# timestamp: 2025-06-23T09:37:51+00:00
3+
# timestamp: 2025-06-23T10:33:05+00:00
44

55
from __future__ import annotations
66

@@ -547,6 +547,15 @@ class Document(BaseModel):
547547
graph_index_status: Optional[
548548
Literal['PENDING', 'RUNNING', 'COMPLETE', 'FAILED', 'SKIPPED']
549549
] = None
550+
vector_index_updated: Optional[datetime] = Field(
551+
None, description='Vector index last updated time'
552+
)
553+
fulltext_index_updated: Optional[datetime] = Field(
554+
None, description='Fulltext index last updated time'
555+
)
556+
graph_index_updated: Optional[datetime] = Field(
557+
None, description='Graph index last updated time'
558+
)
550559
config: Optional[str] = None
551560
size: Optional[float] = None
552561
created: Optional[datetime] = None

aperag/service/document_service.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,44 @@ def map_state_to_old_enum(actual_state: str):
100100
else:
101101
return "PENDING"
102102

103+
# Get individual index update times from DocumentIndex table
104+
from sqlalchemy import select
105+
from aperag.db.models import DocumentIndex, DocumentIndexType
106+
107+
vector_updated = None
108+
fulltext_updated = None
109+
graph_updated = None
110+
111+
# Query for each index type's update time
112+
for index_type, var_name in [
113+
(DocumentIndexType.VECTOR, 'vector_updated'),
114+
(DocumentIndexType.FULLTEXT, 'fulltext_updated'),
115+
(DocumentIndexType.GRAPH, 'graph_updated')
116+
]:
117+
stmt = select(DocumentIndex).where(
118+
DocumentIndex.document_id == document.id,
119+
DocumentIndex.index_type == index_type
120+
)
121+
result = await session.execute(stmt)
122+
index_record = result.scalar_one_or_none()
123+
if index_record:
124+
if var_name == 'vector_updated':
125+
vector_updated = index_record.gmt_updated
126+
elif var_name == 'fulltext_updated':
127+
fulltext_updated = index_record.gmt_updated
128+
elif var_name == 'graph_updated':
129+
graph_updated = index_record.gmt_updated
130+
103131
return Document(
104132
id=document.id,
105133
name=document.name,
106134
status=document.status,
107135
vector_index_status=map_state_to_old_enum(indexes.get("vector", {}).get("actual_state", "absent")),
108136
fulltext_index_status=map_state_to_old_enum(indexes.get("fulltext", {}).get("actual_state", "absent")),
109137
graph_index_status=map_state_to_old_enum(indexes.get("graph", {}).get("actual_state", "absent")),
138+
vector_index_updated=vector_updated,
139+
fulltext_index_updated=fulltext_updated,
140+
graph_index_updated=graph_updated,
110141
size=document.size,
111142
created=document.gmt_created,
112143
updated=document.gmt_updated,

frontend/openapitools.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"spaces": 2,
44
"generator-cli": {
55
"version": "7.12.0",
6-
"useDocker": true,
6+
"useDocker": false,
77
"generators": {
88
"v3.0": {
99
"generatorName": "typescript-axios",

frontend/src/api/models/document.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,24 @@ export interface Document {
5656
* @memberof Document
5757
*/
5858
'graph_index_status'?: DocumentGraphIndexStatusEnum;
59+
/**
60+
* Vector index last updated time
61+
* @type {string}
62+
* @memberof Document
63+
*/
64+
'vector_index_updated'?: string;
65+
/**
66+
* Fulltext index last updated time
67+
* @type {string}
68+
* @memberof Document
69+
*/
70+
'fulltext_index_updated'?: string;
71+
/**
72+
* Graph index last updated time
73+
* @type {string}
74+
* @memberof Document
75+
*/
76+
'graph_index_updated'?: string;
5977
/**
6078
*
6179
* @type {string}

frontend/src/api/openapi.merged.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2793,6 +2793,18 @@ components:
27932793
- COMPLETE
27942794
- FAILED
27952795
- SKIPPED
2796+
vector_index_updated:
2797+
type: string
2798+
format: date-time
2799+
description: Vector index last updated time
2800+
fulltext_index_updated:
2801+
type: string
2802+
format: date-time
2803+
description: Fulltext index last updated time
2804+
graph_index_updated:
2805+
type: string
2806+
format: date-time
2807+
description: Graph index last updated time
27962808
config:
27972809
type: string
27982810
size:
@@ -2845,6 +2857,7 @@ components:
28452857
- fulltext
28462858
- graph
28472859
description: Types of indexes to rebuild
2860+
minItems: 1
28482861
required:
28492862
- index_types
28502863
vectorSearchParams:

frontend/src/pages/collections/$collectionId/documents.tsx

Lines changed: 63 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
Typography,
3939
Upload,
4040
UploadProps,
41+
Tooltip,
4142
} from 'antd';
4243
import byteSize from 'byte-size';
4344
import alpha from 'color-alpha';
@@ -91,41 +92,35 @@ export default () => {
9192
[collectionId],
9293
);
9394

94-
const rebuildIndexes = useCallback(
95-
async (documentId: string, indexTypes: string[]) => {
96-
if (!collectionId || !documentId || indexTypes.length === 0) return;
97-
98-
try {
99-
await api.collectionsCollectionIdDocumentsDocumentIdRebuildIndexesPost({
100-
collectionId,
101-
documentId,
102-
rebuildIndexesRequest: {
103-
index_types: indexTypes as any,
104-
},
105-
});
106-
toast.success(formatMessage({ id: 'document.index.rebuild.success' }));
107-
getDocuments();
108-
} catch (error) {
109-
toast.error(formatMessage({ id: 'document.index.rebuild.failed' }));
110-
}
111-
},
112-
[collectionId, formatMessage],
113-
);
114-
115-
const handleRebuildIndex = useCallback((record: ApeDocument) => {
116-
setRebuildSelectedDocument(record);
117-
setRebuildSelectedTypes([]);
95+
const handleRebuildIndex = (document: ApeDocument) => {
96+
setRebuildSelectedDocument(document);
97+
setRebuildSelectedTypes(['vector', 'fulltext', 'graph']);
11898
setRebuildModalVisible(true);
119-
}, []);
99+
};
100+
101+
const handleRebuildConfirm = async () => {
102+
if (!rebuildSelectedDocument || rebuildSelectedTypes.length === 0) return;
120103

121-
const handleRebuildConfirm = useCallback(() => {
122-
if (rebuildSelectedDocument && rebuildSelectedTypes.length > 0) {
123-
rebuildIndexes(rebuildSelectedDocument.id!, rebuildSelectedTypes);
104+
try {
105+
setLoading(true);
106+
await api.collectionsCollectionIdDocumentsDocumentIdRebuildIndexesPost({
107+
collectionId: collectionId!,
108+
documentId: rebuildSelectedDocument.id!,
109+
rebuildIndexesRequest: {
110+
index_types: rebuildSelectedTypes as ('vector' | 'fulltext' | 'graph')[],
111+
},
112+
});
113+
toast.success(formatMessage({ id: 'document.index.rebuild.success' }));
124114
setRebuildModalVisible(false);
125115
setRebuildSelectedDocument(null);
126116
setRebuildSelectedTypes([]);
117+
getDocuments();
118+
} catch (error) {
119+
toast.error(formatMessage({ id: 'document.index.rebuild.failed' }));
120+
} finally {
121+
setLoading(false);
127122
}
128-
}, [rebuildSelectedDocument, rebuildSelectedTypes, rebuildIndexes]);
123+
};
129124

130125
const indexTypeOptions = [
131126
{ label: formatMessage({ id: 'document.index.type.vector' }), value: 'vector' },
@@ -134,62 +129,25 @@ export default () => {
134129
];
135130

136131
const renderIndexStatus = (
137-
vectorStatus?: DocumentVectorIndexStatusEnum,
138-
fulltextStatus?: DocumentFulltextIndexStatusEnum,
139-
graphStatus?: DocumentGraphIndexStatusEnum,
132+
status?: DocumentVectorIndexStatusEnum | DocumentFulltextIndexStatusEnum | DocumentGraphIndexStatusEnum,
133+
updatedTime?: string
140134
) => {
141-
const indexTypes = [
142-
{ nameKey: 'document.index.type.vector', status: vectorStatus },
143-
{ nameKey: 'document.index.type.fulltext', status: fulltextStatus },
144-
{ nameKey: 'document.index.type.graph', status: graphStatus },
145-
];
146-
return (
147-
<Space direction="vertical" size="small">
148-
{indexTypes.map(({ nameKey, status }, index) => (
149-
<div
150-
key={index}
151-
style={{
152-
fontSize: '12px',
153-
lineHeight: '18px',
154-
display: 'flex',
155-
alignItems: 'center',
156-
whiteSpace: 'nowrap'
157-
}}
158-
>
159-
<span
160-
style={{
161-
color: '#666',
162-
width: '100px',
163-
textAlign: 'right',
164-
display: 'inline-block'
165-
}}
166-
>
167-
{formatMessage({ id: nameKey })}
168-
</span>
169-
<span
170-
style={{
171-
color: '#666',
172-
width: '12px',
173-
textAlign: 'center',
174-
display: 'inline-block'
175-
}}
176-
>
177-
178-
</span>
179-
<div style={{ width: '80px' }}>
180-
<Badge
181-
status={UI_INDEX_STATUS[status as keyof typeof UI_INDEX_STATUS]}
182-
text={
183-
<span style={{ display: 'inline-block', width: '70px' }}>
184-
{formatMessage({ id: `document.index.status.${status}` })}
185-
</span>
186-
}
187-
/>
188-
</div>
189-
</div>
190-
))}
191-
</Space>
135+
const statusBadge = (
136+
<Badge
137+
status={UI_INDEX_STATUS[status as keyof typeof UI_INDEX_STATUS]}
138+
text={formatMessage({ id: `document.index.status.${status}` })}
139+
/>
192140
);
141+
142+
if (updatedTime) {
143+
return (
144+
<Tooltip title={`${formatMessage({ id: 'text.updatedAt' })}: ${moment(updatedTime).format(DATETIME_FORMAT)}`}>
145+
{statusBadge}
146+
</Tooltip>
147+
);
148+
}
149+
150+
return statusBadge;
193151
};
194152

195153
const columns: TableProps<ApeDocument>['columns'] = [
@@ -224,16 +182,30 @@ export default () => {
224182
},
225183
},
226184
{
227-
title: formatMessage({ id: 'document.status' }),
228-
dataIndex: 'status',
229-
width: 190,
185+
title: formatMessage({ id: 'document.index.type.vector' }),
186+
dataIndex: 'vector_index_status',
187+
width: 120,
230188
align: 'center',
231189
render: (value, record) => {
232-
return renderIndexStatus(
233-
record.vector_index_status,
234-
record.fulltext_index_status,
235-
record.graph_index_status,
236-
);
190+
return renderIndexStatus(record.vector_index_status, record.vector_index_updated);
191+
},
192+
},
193+
{
194+
title: formatMessage({ id: 'document.index.type.fulltext' }),
195+
dataIndex: 'fulltext_index_status',
196+
width: 120,
197+
align: 'center',
198+
render: (value, record) => {
199+
return renderIndexStatus(record.fulltext_index_status, record.fulltext_index_updated);
200+
},
201+
},
202+
{
203+
title: formatMessage({ id: 'document.index.type.graph' }),
204+
dataIndex: 'graph_index_status',
205+
width: 120,
206+
align: 'center',
207+
render: (value, record) => {
208+
return renderIndexStatus(record.graph_index_status, record.graph_index_updated);
237209
},
238210
},
239211
{

0 commit comments

Comments
 (0)