fix(elasticsearch): save new document rows

This commit is contained in:
t8y2 2026-06-23 20:33:51 +08:00
parent 7ae024ffd4
commit 0c8bf24977
3 changed files with 71 additions and 6 deletions

View File

@ -88,6 +88,13 @@ type LocalFilterSummary = {
values: string[];
hiddenValueCount: number;
};
type DocumentGridChanges = {
dirtyRows: Map<number, Map<number, MongoInputValue>>;
deletedRows: Set<number>;
newRows: MongoInputValue[][];
columns: string[];
rows: MongoInputValue[][];
};
const documentFilterBuilderOpen = ref(false);
const documentFilterRules = ref<DocumentFilterRule[]>([]);
const appliedDocumentFilter = ref<Record<string, unknown> | null>(null);
@ -227,7 +234,15 @@ function clearDocumentFilters(clearLocalFilter?: (columnIndex?: number) => void)
applyFilter();
}
async function gridSave(changes: { dirtyRows: Map<number, Map<number, MongoInputValue>>; deletedRows: Set<number>; columns: string[]; rows: MongoInputValue[][] }) {
function documentIdFromGridValue(value: MongoInputValue | undefined): string | null {
if (value === null || value === undefined) return null;
const parsed = parseMongoDocumentInputValue(value);
if (parsed === null || parsed === undefined) return null;
const id = typeof parsed === "object" ? JSON.stringify(parsed) : String(parsed);
return id.trim() ? id : null;
}
async function gridSave(changes: DocumentGridChanges) {
const cols = changes.columns;
const idColIdx = cols.indexOf("_id");
if (idColIdx < 0) throw new Error("No _id column");
@ -267,6 +282,20 @@ async function gridSave(changes: { dirtyRows: Map<number, Map<number, MongoInput
await api.mongoDeleteDocument(props.connectionId, props.database, props.collection, String(id));
}
for (const newRow of changes.newRows) {
const doc = buildMongoInsertDocument(newRow, cols);
if (isEs) {
const id = documentIdFromGridValue(newRow[idColIdx]);
if (id) {
await api.mongoUpdateDocument(props.connectionId, props.database, props.collection, id, JSON.stringify(doc));
} else {
await api.mongoInsertDocument(props.connectionId, props.database, props.collection, JSON.stringify(doc));
}
continue;
}
await api.mongoInsertDocument(props.connectionId, props.database, props.collection, JSON.stringify(doc));
}
await load();
}
@ -283,7 +312,11 @@ function mongoIdPreview(val: unknown): string {
return formatMongoValue(val);
}
async function previewDocumentChanges(changes: { dirtyRows: Map<number, Map<number, MongoInputValue>>; deletedRows: Set<number>; newRows: MongoInputValue[][]; columns: string[]; rows: MongoInputValue[][] }): Promise<string[]> {
function elasticsearchPathIdPreview(id: string): string {
return encodeURIComponent(id);
}
async function previewDocumentChanges(changes: DocumentGridChanges): Promise<string[]> {
const { dirtyRows, deletedRows, newRows, columns, rows } = changes;
const idColIdx = columns.indexOf("_id");
const stmts: string[] = [];
@ -296,7 +329,7 @@ async function previewDocumentChanges(changes: { dirtyRows: Map<number, Map<numb
if (id == null) continue;
if (isEs) {
const updateDoc = buildMongoUpdateDocument(dirtyCols, columns);
stmts.push(`POST /${coll}/_update/${JSON.stringify(String(id))}\n${JSON.stringify({ doc: updateDoc.$set ?? updateDoc }, null, 2)}`);
stmts.push(`POST /${coll}/_update/${elasticsearchPathIdPreview(String(id))}\n${JSON.stringify({ doc: updateDoc.$set ?? updateDoc }, null, 2)}`);
} else {
const updateDoc = buildMongoUpdateDocument(dirtyCols, columns);
stmts.push(`db.${coll}.updateOne({_id: ${mongoIdPreview(id)}}, ${formatMongoShellLiteral(updateDoc)})`);
@ -308,7 +341,7 @@ async function previewDocumentChanges(changes: { dirtyRows: Map<number, Map<numb
const id = row?.[idColIdx];
if (id == null) continue;
if (isEs) {
stmts.push(`DELETE /${coll}/_doc/${JSON.stringify(String(id))}`);
stmts.push(`DELETE /${coll}/_doc/${elasticsearchPathIdPreview(String(id))}`);
} else {
stmts.push(`db.${coll}.deleteOne({_id: ${mongoIdPreview(id)}})`);
}
@ -317,7 +350,12 @@ async function previewDocumentChanges(changes: { dirtyRows: Map<number, Map<numb
for (const newRow of newRows) {
const doc = buildMongoInsertDocument(newRow, columns);
if (isEs) {
stmts.push(`POST /${coll}/_doc\n${JSON.stringify(doc, null, 2)}`);
const id = idColIdx >= 0 ? documentIdFromGridValue(newRow[idColIdx]) : null;
if (id) {
stmts.push(`PUT /${coll}/_doc/${elasticsearchPathIdPreview(id)}\n${JSON.stringify(doc, null, 2)}`);
} else {
stmts.push(`POST /${coll}/_doc\n${JSON.stringify(doc, null, 2)}`);
}
} else {
stmts.push(`db.${coll}.insertOne(${formatMongoShellLiteral(doc)})`);
}

View File

@ -2631,6 +2631,7 @@ const {
dirtyRows,
newRows,
deletedRows,
pendingChangesVersion,
pendingChangeCount,
hasPendingChanges,
transactionActive,
@ -2712,7 +2713,7 @@ function closeSqlPreview() {
}
// Watch for edits auto-refresh preview when panel is open
watch([pendingChangeCount, dirtyRows, newRows, deletedRows], () => {
watch([pendingChangeCount, pendingChangesVersion], () => {
schedulePreviewRefresh();
});

View File

@ -142,6 +142,7 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
const dirtyRows = ref<Map<number, Map<number, CellValue>>>(new Map());
const newRows = ref<CellValue[][]>([]);
const deletedRows = ref<Set<number>>(new Set());
const pendingChangesVersion = ref(0);
let restoredEditingCell = false;
let restoredTransactionActive = false;
let suppressNextBlurCommit = false;
@ -218,6 +219,10 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
transactionActive.value = true;
}
function touchPendingChanges() {
pendingChangesVersion.value++;
}
function exitTransaction() {
transactionActive.value = false;
}
@ -415,6 +420,8 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
if (newRows.value[item.newIndex]) {
newRows.value[item.newIndex][col] = newVal;
}
newRows.value = [...newRows.value];
touchPendingChanges();
editingCell.value = null;
isCommitting = false;
return;
@ -445,6 +452,7 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
if (rowChanges?.size === 0) dirtyRows.value.delete(item.sourceIndex);
}
dirtyRows.value = new Map(dirtyRows.value);
touchPendingChanges();
editingCell.value = null;
isCommitting = false;
}
@ -466,6 +474,7 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
const oldVal = newRows.value[item.newIndex]?.[col];
newRows.value[item.newIndex][col] = value === null ? null : coerceCellValue(value, oldVal, col);
newRows.value = [...newRows.value];
touchPendingChanges();
return;
}
@ -486,6 +495,7 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
if (rowChanges?.size === 0) dirtyRows.value.delete(item.sourceIndex);
}
dirtyRows.value = new Map(dirtyRows.value);
touchPendingChanges();
}
function cancelEdit() {
@ -511,6 +521,8 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
function addRow() {
rowStatusFilter.value = rowStatusFilterAfterAddingRow(rowStatusFilter.value);
newRows.value.push(result.value.columns.map(() => null));
newRows.value = [...newRows.value];
touchPendingChanges();
if (useTransaction.value && !transactionActive.value) {
enterTransaction();
}
@ -545,6 +557,8 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
const clonedData = clonedRowData(item);
rowStatusFilter.value = rowStatusFilterAfterAddingRow(rowStatusFilter.value);
newRows.value.push(clonedData);
newRows.value = [...newRows.value];
touchPendingChanges();
if (useTransaction.value && !transactionActive.value) {
enterTransaction();
}
@ -564,6 +578,8 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
const clonedData = clonedRowData(item);
newRows.value.push(clonedData);
}
newRows.value = [...newRows.value];
touchPendingChanges();
if (useTransaction.value && !transactionActive.value) {
enterTransaction();
}
@ -574,11 +590,15 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
if (!item) return;
if (item.isNew && item.newIndex !== undefined) {
newRows.value.splice(item.newIndex, 1);
newRows.value = [...newRows.value];
} else if (item.sourceIndex !== undefined) {
if (!canEditExistingRows.value) return;
dirtyRows.value.delete(item.sourceIndex);
deletedRows.value.add(item.sourceIndex);
dirtyRows.value = new Map(dirtyRows.value);
deletedRows.value = new Set(deletedRows.value);
}
touchPendingChanges();
if (editingCell.value?.rowId === rowId) editingCell.value = null;
if (useTransaction.value && !transactionActive.value) {
enterTransaction();
@ -616,6 +636,8 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
const item = getRowItem(rowId);
if (item?.sourceIndex !== undefined) {
deletedRows.value.delete(item.sourceIndex);
deletedRows.value = new Set(deletedRows.value);
touchPendingChanges();
}
}
@ -725,6 +747,7 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
dirtyRows.value.clear();
newRows.value = [];
deletedRows.value.clear();
touchPendingChanges();
exitTransaction();
isSaving.value = false;
if (shouldReloadAfterSave) {
@ -808,6 +831,7 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
dirtyRows.value.clear();
newRows.value = [];
deletedRows.value.clear();
touchPendingChanges();
exitTransaction();
isSaving.value = false;
if (shouldReloadAfterSave) {
@ -819,6 +843,7 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
dirtyRows.value.clear();
newRows.value = [];
deletedRows.value.clear();
touchPendingChanges();
editingCell.value = null;
exitTransaction();
}
@ -939,6 +964,7 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
dirtyRows,
newRows,
deletedRows,
pendingChangesVersion,
dirtyRowCount,
newRowCount,
deletedRowCount,