fix(elasticsearch): pass routing for document writes

This commit is contained in:
t8y2 committed 2026-07-01 00:43:14 +08:00
1 parent 910fde75e1
commit 299a9843f3
16 files changed
+912 -351

No files matched your search

@@ -244,6 +244,22 @@ function documentIdFromGridValue(value: MongoInputValue | undefined): string | n
return id.trim() ? id : null;
}
function documentRoutingValue(value: unknown): string | undefined {
if (value === null || value === undefined) return undefined;
const routing = typeof value === "string" ? value : String(value);
const trimmed = routing.trim();
return trimmed ? trimmed : undefined;
}
function documentRoutingFromDocument(doc: JsonRecord | undefined): string | undefined {
return documentRoutingValue(doc?._routing);
}
function documentRoutingFromGridRow(row: MongoInputValue[] | undefined, columns: string[]): string | undefined {
const routingColIdx = columns.indexOf("_routing");
return routingColIdx >= 0 ? documentRoutingValue(row?.[routingColIdx]) : undefined;
}
async function gridSave(changes: DocumentGridChanges) {
const cols = changes.columns;
const idColIdx = cols.indexOf("_id");
@@ -258,30 +274,32 @@ async function gridSave(changes: DocumentGridChanges) {
if (isEs) {
const doc = documents.value[rowIdx];
if (!doc) continue;
const routing = documentRoutingFromDocument(doc);
const updated = { ...doc };
for (const [colIdx, newVal] of dirtyCols) {
const col = cols[colIdx];
if (col === "_id") continue;
if (col === "_id" || col === "_routing") continue;
if (newVal === null) {
delete updated[col];
} else {
updated[col] = parseMongoDocumentInputValue(newVal);
}
}
await api.mongoUpdateDocument(props.connectionId, props.database, props.collection, String(id), JSON.stringify(updated));
await api.documentUpdateDocument(props.connectionId, props.database, props.collection, String(id), JSON.stringify(updated), routing);
continue;
}
const updateDoc = buildMongoUpdateDocument(dirtyCols, cols);
if (Object.keys(updateDoc).length === 0) continue;
await api.mongoUpdateDocument(props.connectionId, props.database, props.collection, String(id), JSON.stringify(updateDoc));
await api.documentUpdateDocument(props.connectionId, props.database, props.collection, String(id), JSON.stringify(updateDoc));
}
for (const rowIdx of changes.deletedRows) {
const row = changes.rows[rowIdx];
const id = row?.[idColIdx];
if (id == null) continue;
await api.mongoDeleteDocument(props.connectionId, props.database, props.collection, String(id));
const routing = isEs ? documentRoutingFromDocument(documents.value[rowIdx]) : undefined;
await api.documentDeleteDocument(props.connectionId, props.database, props.collection, String(id), routing);
}
for (const newRow of changes.newRows) {
@@ -289,13 +307,13 @@ async function gridSave(changes: DocumentGridChanges) {
if (isEs) {
const id = documentIdFromGridValue(newRow[idColIdx]);
if (id) {
await api.mongoUpdateDocument(props.connectionId, props.database, props.collection, id, JSON.stringify(doc));
await api.documentUpdateDocument(props.connectionId, props.database, props.collection, id, JSON.stringify(doc), documentRoutingFromGridRow(newRow, cols));
} else {
await api.mongoInsertDocument(props.connectionId, props.database, props.collection, JSON.stringify(doc));
await api.documentInsertDocument(props.connectionId, props.database, props.collection, JSON.stringify(doc));
}
continue;
}
await api.mongoInsertDocument(props.connectionId, props.database, props.collection, JSON.stringify(doc));
await api.documentInsertDocument(props.connectionId, props.database, props.collection, JSON.stringify(doc));
}
await load();
@@ -318,6 +336,20 @@ function elasticsearchPathIdPreview(id: string): string {
return encodeURIComponent(id);
}
function elasticsearchRoutingPreview(routing: string | undefined): string {
return routing ? `?routing=${encodeURIComponent(routing)}` : "";
}
function buildElasticsearchPartialUpdateDocument(changes: Map<number, MongoInputValue>, columns: string[]): Record<string, unknown> {
const filtered = new Map<number, MongoInputValue>();
for (const [colIdx, newVal] of changes) {
const col = columns[colIdx];
if (col === "_id" || col === "_routing") continue;
filtered.set(colIdx, newVal);
}
return buildMongoUpdateDocument(filtered, columns);
}
async function previewDocumentChanges(changes: DocumentGridChanges): Promise<string[]> {
const { dirtyRows, deletedRows, newRows, columns, rows } = changes;
const idColIdx = columns.indexOf("_id");
@@ -330,8 +362,9 @@ async function previewDocumentChanges(changes: DocumentGridChanges): Promise<str
const id = row?.[idColIdx];
if (id == null) continue;
if (isEs) {
const updateDoc = buildMongoUpdateDocument(dirtyCols, columns);
stmts.push(`POST /${coll}/_update/${elasticsearchPathIdPreview(String(id))}\n${JSON.stringify({ doc: updateDoc.$set ?? updateDoc }, null, 2)}`);
const updateDoc = buildElasticsearchPartialUpdateDocument(dirtyCols, columns);
const routing = documentRoutingFromGridRow(row, columns);
stmts.push(`POST /${coll}/_update/${elasticsearchPathIdPreview(String(id))}${elasticsearchRoutingPreview(routing)}\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)})`);
@@ -343,7 +376,8 @@ async function previewDocumentChanges(changes: DocumentGridChanges): Promise<str
const id = row?.[idColIdx];
if (id == null) continue;
if (isEs) {
stmts.push(`DELETE /${coll}/_doc/${elasticsearchPathIdPreview(String(id))}`);
const routing = documentRoutingFromGridRow(row, columns);
stmts.push(`DELETE /${coll}/_doc/${elasticsearchPathIdPreview(String(id))}${elasticsearchRoutingPreview(routing)}`);
} else {
stmts.push(`db.${coll}.deleteOne({_id: ${mongoIdPreview(id)}})`);
}
@@ -370,6 +404,7 @@ const customSaveHandler = computed<CustomSaveHandler>(() => ({
save: gridSave,
preview: previewDocumentChanges,
supportsInsert: true,
readonlyColumns: documentStoreProvider.value.kind === "elasticsearch" ? ["_routing"] : undefined,
}));
function stopDocumentLoadingTimer() {
@@ -514,7 +549,10 @@ function startNew() {
function startEdit() {
const doc = selectedDoc.value;
if (!doc) return;
editFields.value = Object.entries(doc).map(([name, value]) => createEditNode(name, value, name === "_id", name === "_id"));
editFields.value = Object.entries(doc).map(([name, value]) => {
const readonlyMetadata = name === "_id" || (documentStoreProvider.value.kind === "elasticsearch" && name === "_routing");
return createEditNode(name, value, readonlyMetadata, readonlyMetadata);
});
isEditing.value = true;
isNew.value = false;
}
@@ -603,7 +641,7 @@ function buildObjectFromNodes(nodes: EditNode[], path: string): JsonRecord {
for (const field of nodes) {
const name = field.keyName.trim();
if (!name || (!path && name === "_id")) continue;
if (!name || (!path && (name === "_id" || (documentStoreProvider.value.kind === "elasticsearch" && name === "_routing")))) continue;
if (seen.has(name)) throw new Error(t("mongo.duplicateField", { field: name }));
seen.add(name);
doc[name] = buildValueFromNode(field, path ? `${path}.${name}` : name);
@@ -629,7 +667,7 @@ async function saveDoc() {
try {
const doc = buildDocumentFromFields();
if (isNew.value) {
await api.mongoInsertDocument(props.connectionId, props.database, props.collection, JSON.stringify(doc));
await api.documentInsertDocument(props.connectionId, props.database, props.collection, JSON.stringify(doc));
} else if (selectedIdx.value !== null) {
const current = documents.value[selectedIdx.value];
const id = current?._id;
@@ -637,7 +675,7 @@ async function saveDoc() {
error.value = "No _id field";
return;
}
await api.mongoUpdateDocument(props.connectionId, props.database, props.collection, String(id), JSON.stringify(doc));
await api.documentUpdateDocument(props.connectionId, props.database, props.collection, String(id), JSON.stringify(doc), documentRoutingFromDocument(current));
}
isEditing.value = false;
isNew.value = false;
@@ -657,7 +695,7 @@ async function applyDeleteDoc(idx: number) {
if (!id) return;
error.value = "";
try {
await api.mongoDeleteDocument(props.connectionId, props.database, props.collection, String(id));
await api.documentDeleteDocument(props.connectionId, props.database, props.collection, String(id), documentRoutingFromDocument(doc));
if (selectedIdx.value === idx) {
selectedIdx.value = null;
editJson.value = "";
+5
View File
@@ -366,7 +366,9 @@ export const mqRawRequest = forward("mqRawRequest");
export const mqSendMessage = forward("mqSendMessage");
// MongoDB
export const documentListDatabases = forward("documentListDatabases");
export const mongoListDatabases = forward("mongoListDatabases");
export const documentListCollections = forward("documentListCollections");
export const mongoListCollections = forward("mongoListCollections");
export const mongoCreateDatabase = forward("mongoCreateDatabase");
export const mongoDropDatabase = forward("mongoDropDatabase");
@@ -376,10 +378,13 @@ export const mongoFindDocuments = forward("mongoFindDocuments");
export const mongoServerVersion = forward("mongoServerVersion");
export const mongoAggregateDocuments = forward("mongoAggregateDocuments");
export const mongoCreateIndex = forward("mongoCreateIndex");
export const documentInsertDocument = forward("documentInsertDocument");
export const mongoInsertDocument = forward("mongoInsertDocument");
export const mongoInsertDocuments = forward("mongoInsertDocuments");
export const documentUpdateDocument = forward("documentUpdateDocument");
export const mongoUpdateDocument = forward("mongoUpdateDocument");
export const mongoUpdateDocuments = forward("mongoUpdateDocuments");
export const documentDeleteDocument = forward("documentDeleteDocument");
export const mongoDeleteDocument = forward("mongoDeleteDocument");
export const mongoDeleteDocuments = forward("mongoDeleteDocuments");
+30 -10
View File
@@ -1711,12 +1711,20 @@ export async function nacosRawRequest(connectionId: string, req: NacosRawRequest
// MongoDB
// ---------------------------------------------------------------------------
export async function documentListDatabases(connectionId: string): Promise<string[]> {
return post("/api/document-store/list-databases", { connectionId });
}
export async function mongoListDatabases(connectionId: string): Promise<string[]> {
return post("/api/mongo/list-databases", { connectionId });
return documentListDatabases(connectionId);
}
export async function documentListCollections(connectionId: string, database: string): Promise<CollectionInfo[]> {
return post("/api/document-store/list-collections", { connectionId, database });
}
export async function mongoListCollections(connectionId: string, database: string): Promise<CollectionInfo[]> {
return post("/api/mongo/list-collections", { connectionId, database });
return documentListCollections(connectionId, database);
}
export async function mongoCreateDatabase(connectionId: string, database: string): Promise<void> {
@@ -1732,16 +1740,16 @@ export async function mongoDropCollection(connectionId: string, database: string
}
export async function elasticsearchListIndices(connectionId: string): Promise<string[]> {
const collections = await mongoListCollections(connectionId, "default");
const collections = await documentListCollections(connectionId, "default");
return collections.map((c) => c.name);
}
export async function vectorListCollections(connectionId: string, database?: string): Promise<CollectionInfo[]> {
return mongoListCollections(connectionId, database || "default");
return documentListCollections(connectionId, database || "default");
}
export async function mongoFindDocuments(connectionId: string, database: string, collection: string, skip: number, limit: number, filter?: string, projection?: string, sort?: string, executionId?: string): Promise<MongoDocumentResult> {
return post("/api/mongo/find-documents", { connectionId, database, collection, skip, limit, filter, projection, sort, executionId });
return documentFindDocuments(connectionId, database, collection, skip, limit, filter, projection, sort, executionId);
}
export async function documentFindDocuments(connectionId: string, database: string, collection: string, skip: number, limit: number, filter?: string, projection?: string, sort?: string, executionId?: string): Promise<MongoDocumentResult> {
@@ -1761,23 +1769,35 @@ export async function mongoCreateIndex(connectionId: string, database: string, c
}
export async function mongoInsertDocument(connectionId: string, database: string, collection: string, docJson: string): Promise<string> {
return post("/api/mongo/insert-document", { connectionId, database, collection, docJson });
return documentInsertDocument(connectionId, database, collection, docJson);
}
export async function documentInsertDocument(connectionId: string, database: string, collection: string, docJson: string): Promise<string> {
return post("/api/document-store/insert-document", { connectionId, database, collection, docJson });
}
export async function mongoInsertDocuments(connectionId: string, database: string, collection: string, docsJson: string): Promise<{ affected_rows: number }> {
return post("/api/mongo/insert-documents", { connectionId, database, collection, docsJson });
}
export async function mongoUpdateDocument(connectionId: string, database: string, collection: string, id: string, docJson: string): Promise<number> {
return post("/api/mongo/update-document", { connectionId, database, collection, id, docJson });
export async function mongoUpdateDocument(connectionId: string, database: string, collection: string, id: string, docJson: string, routing?: string): Promise<number> {
return documentUpdateDocument(connectionId, database, collection, id, docJson, routing);
}
export async function documentUpdateDocument(connectionId: string, database: string, collection: string, id: string, docJson: string, routing?: string): Promise<number> {
return post("/api/document-store/update-document", { connectionId, database, collection, id, docJson, routing });
}
export async function mongoUpdateDocuments(connectionId: string, database: string, collection: string, filterJson: string, updateJson: string, many: boolean): Promise<{ affected_rows: number }> {
return post("/api/mongo/update-documents", { connectionId, database, collection, filterJson, updateJson, many });
}
export async function mongoDeleteDocument(connectionId: string, database: string, collection: string, id: string): Promise<number> {
return post("/api/mongo/delete-document", { connectionId, database, collection, id });
export async function mongoDeleteDocument(connectionId: string, database: string, collection: string, id: string, routing?: string): Promise<number> {
return documentDeleteDocument(connectionId, database, collection, id, routing);
}
export async function documentDeleteDocument(connectionId: string, database: string, collection: string, id: string, routing?: string): Promise<number> {
return post("/api/document-store/delete-document", { connectionId, database, collection, id, routing });
}
export async function mongoDeleteDocuments(connectionId: string, database: string, collection: string, filterJson: string, many: boolean): Promise<{ affected_rows: number }> {
+30 -10
View File
@@ -1441,12 +1441,20 @@ export interface MongoDocumentResult {
total: number;
}
export async function documentListDatabases(connectionId: string): Promise<string[]> {
return invoke("document_list_databases", { connectionId });
}
export async function mongoListDatabases(connectionId: string): Promise<string[]> {
return invoke("mongo_list_databases", { connectionId });
return documentListDatabases(connectionId);
}
export async function documentListCollections(connectionId: string, database: string): Promise<CollectionInfo[]> {
return invoke("document_list_collections", { connectionId, database });
}
export async function mongoListCollections(connectionId: string, database: string): Promise<CollectionInfo[]> {
return invoke("mongo_list_collections", { connectionId, database });
return documentListCollections(connectionId, database);
}
export async function mongoCreateDatabase(connectionId: string, database: string): Promise<void> {
@@ -1462,16 +1470,16 @@ export async function mongoDropCollection(connectionId: string, database: string
}
export async function elasticsearchListIndices(connectionId: string): Promise<string[]> {
const collections = await mongoListCollections(connectionId, "default");
const collections = await documentListCollections(connectionId, "default");
return collections.map((c) => c.name);
}
export async function vectorListCollections(connectionId: string, database?: string): Promise<CollectionInfo[]> {
return mongoListCollections(connectionId, database || "default");
return documentListCollections(connectionId, database || "default");
}
export async function mongoFindDocuments(connectionId: string, database: string, collection: string, skip: number, limit: number, filter?: string, projection?: string, sort?: string, executionId?: string): Promise<MongoDocumentResult> {
return invoke("mongo_find_documents", { connectionId, database, collection, skip, limit, filter, projection, sort, executionId });
return documentFindDocuments(connectionId, database, collection, skip, limit, filter, projection, sort, executionId);
}
export async function documentFindDocuments(connectionId: string, database: string, collection: string, skip: number, limit: number, filter?: string, projection?: string, sort?: string, executionId?: string): Promise<MongoDocumentResult> {
@@ -1491,7 +1499,11 @@ export async function mongoCreateIndex(connectionId: string, database: string, c
}
export async function mongoInsertDocument(connectionId: string, database: string, collection: string, docJson: string): Promise<string> {
return invoke("mongo_insert_document", { connectionId, database, collection, docJson });
return documentInsertDocument(connectionId, database, collection, docJson);
}
export async function documentInsertDocument(connectionId: string, database: string, collection: string, docJson: string): Promise<string> {
return invoke("document_insert_document", { connectionId, database, collection, docJson });
}
export async function mongoInsertDocuments(connectionId: string, database: string, collection: string, docsJson: string): Promise<{ affected_rows: number }> {
@@ -1499,8 +1511,12 @@ export async function mongoInsertDocuments(connectionId: string, database: strin
return { affected_rows: affectedRows };
}
export async function mongoUpdateDocument(connectionId: string, database: string, collection: string, id: string, docJson: string): Promise<number> {
return invoke("mongo_update_document", { connectionId, database, collection, id, docJson });
export async function mongoUpdateDocument(connectionId: string, database: string, collection: string, id: string, docJson: string, routing?: string): Promise<number> {
return documentUpdateDocument(connectionId, database, collection, id, docJson, routing);
}
export async function documentUpdateDocument(connectionId: string, database: string, collection: string, id: string, docJson: string, routing?: string): Promise<number> {
return invoke("document_update_document", { connectionId, database, collection, id, docJson, routing });
}
export async function mongoUpdateDocuments(connectionId: string, database: string, collection: string, filterJson: string, updateJson: string, many: boolean): Promise<{ affected_rows: number }> {
@@ -1515,8 +1531,12 @@ export async function mongoUpdateDocuments(connectionId: string, database: strin
return { affected_rows: affectedRows };
}
export async function mongoDeleteDocument(connectionId: string, database: string, collection: string, id: string): Promise<number> {
return invoke("mongo_delete_document", { connectionId, database, collection, id });
export async function mongoDeleteDocument(connectionId: string, database: string, collection: string, id: string, routing?: string): Promise<number> {
return documentDeleteDocument(connectionId, database, collection, id, routing);
}
export async function documentDeleteDocument(connectionId: string, database: string, collection: string, id: string, routing?: string): Promise<number> {
return invoke("document_delete_document", { connectionId, database, collection, id, routing });
}
export async function mongoDeleteDocuments(connectionId: string, database: string, collection: string, filterJson: string, many: boolean): Promise<{ affected_rows: number }> {