feat(elasticsearch): add searchable field picker
This commit is contained in:
1 parent
e1d240e502
commit
9173cb754d
4 files changed
+471
-24
No files matched your search
@@ -142,10 +142,79 @@ type DocumentFieldPathAccumulatorNode = {
|
||||
childByKey: Map<string, DocumentFieldPathAccumulatorNode>;
|
||||
};
|
||||
|
||||
type ElasticsearchFieldPathAccumulatorNode = {
|
||||
key: string;
|
||||
path: string;
|
||||
selectable: boolean;
|
||||
children: ElasticsearchFieldPathAccumulatorNode[];
|
||||
childByKey: Map<string, ElasticsearchFieldPathAccumulatorNode>;
|
||||
};
|
||||
|
||||
export function documentFieldPathOptionsFromDocuments(documents: readonly Record<string, unknown>[]): string[] {
|
||||
return flattenDocumentFieldPathTree(documentFieldPathTreeFromDocuments(documents)).map((node) => node.path);
|
||||
}
|
||||
|
||||
export function elasticsearchFieldPathTreeFromFieldNames(fieldNames: readonly string[], fieldTypes: ReadonlyMap<string, string> = new Map()): DocumentFieldPathNode[] {
|
||||
const rootNodes: ElasticsearchFieldPathAccumulatorNode[] = [];
|
||||
const rootByKey = new Map<string, ElasticsearchFieldPathAccumulatorNode>();
|
||||
|
||||
for (const fieldName of fieldNames) {
|
||||
appendElasticsearchFieldPath(rootNodes, rootByKey, fieldName, fieldTypes.get(fieldName));
|
||||
}
|
||||
return finalizeElasticsearchFieldPathNodes(rootNodes);
|
||||
}
|
||||
|
||||
function appendElasticsearchFieldPath(nodes: ElasticsearchFieldPathAccumulatorNode[], byKey: Map<string, ElasticsearchFieldPathAccumulatorNode>, fieldName: string, fieldType?: string): void {
|
||||
const segments = fieldName.split(".");
|
||||
if (segments.some((segment) => !segment)) return;
|
||||
let siblingNodes = nodes;
|
||||
let siblingByKey = byKey;
|
||||
let currentPath = "";
|
||||
|
||||
segments.forEach((segment, segmentIndex) => {
|
||||
currentPath = currentPath ? `${currentPath}.${segment}` : segment;
|
||||
const node = ensureElasticsearchFieldPathNode(siblingNodes, siblingByKey, segment, currentPath);
|
||||
if (segmentIndex === segments.length - 1) node.selectable = !isElasticsearchContainerFieldType(fieldType);
|
||||
siblingNodes = node.children;
|
||||
siblingByKey = node.childByKey;
|
||||
});
|
||||
}
|
||||
|
||||
function isElasticsearchContainerFieldType(fieldType?: string): boolean {
|
||||
const normalizedType = fieldType?.trim().toLowerCase();
|
||||
return normalizedType === "object" || normalizedType === "nested";
|
||||
}
|
||||
|
||||
function ensureElasticsearchFieldPathNode(nodes: ElasticsearchFieldPathAccumulatorNode[], byKey: Map<string, ElasticsearchFieldPathAccumulatorNode>, key: string, path: string): ElasticsearchFieldPathAccumulatorNode {
|
||||
const existing = byKey.get(key);
|
||||
if (existing) return existing;
|
||||
const node: ElasticsearchFieldPathAccumulatorNode = {
|
||||
key,
|
||||
path,
|
||||
selectable: false,
|
||||
children: [],
|
||||
childByKey: new Map(),
|
||||
};
|
||||
byKey.set(key, node);
|
||||
nodes.push(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
function finalizeElasticsearchFieldPathNodes(nodes: readonly ElasticsearchFieldPathAccumulatorNode[], parentDisplaySegments: readonly string[] = []): DocumentFieldPathNode[] {
|
||||
return nodes.map((node) => {
|
||||
const displaySegments = [...parentDisplaySegments, node.key];
|
||||
return {
|
||||
key: node.key,
|
||||
path: node.path,
|
||||
label: node.key,
|
||||
displayPath: displaySegments.join(" > "),
|
||||
kind: "scalar",
|
||||
selectable: node.selectable,
|
||||
children: finalizeElasticsearchFieldPathNodes(node.children, displaySegments),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function documentFieldPathTreeFromDocuments(documents: readonly Record<string, unknown>[]): DocumentFieldPathNode[] {
|
||||
if (documents.length === 0) return [];
|
||||
const rootNodes: DocumentFieldPathAccumulatorNode[] = [];
|
||||
@@ -178,6 +247,10 @@ export function searchDocumentFieldPathTree(nodes: readonly DocumentFieldPathNod
|
||||
});
|
||||
}
|
||||
|
||||
export function searchElasticsearchFieldPathTree(nodes: readonly DocumentFieldPathNode[], query: string): DocumentFieldPathNode[] {
|
||||
return searchDocumentFieldPathTree(nodes, query).filter((node) => node.selectable);
|
||||
}
|
||||
|
||||
export function arrayObjectAncestorPathForDocumentField(nodes: readonly DocumentFieldPathNode[], path: string): string | null {
|
||||
for (const node of nodes) {
|
||||
if (node.path === path) return null;
|
||||
|
||||
Reference in new issue
Block a user