(
query: QueryDescriptor,
workspace: FoamWorkspace,
graph: FoamGraph,
options: { trusted: boolean; readSource?: SourceReader }
)
| 659 | // --- executeQuery --- |
| 660 | |
| 661 | export function executeQuery( |
| 662 | query: QueryDescriptor, |
| 663 | workspace: FoamWorkspace, |
| 664 | graph: FoamGraph, |
| 665 | options: { trusted: boolean; readSource?: SourceReader } |
| 666 | ): QueryExecutionResult { |
| 667 | const { predicate, warnings } = parseFilter( |
| 668 | query.filter, |
| 669 | workspace, |
| 670 | graph, |
| 671 | options.trusted |
| 672 | ); |
| 673 | const fields = (query.select ?? DEFAULT_SELECT) |
| 674 | .map(normalizeSelectEntry) |
| 675 | .map(e => e.field); |
| 676 | |
| 677 | // Sort + offset + limit before reading source — otherwise a query with |
| 678 | // `select: [body]` over a broad filter would synchronously read every |
| 679 | // matched note. Sort keys can't be source-derived so this is safe. |
| 680 | const cheapFields = fields.filter(f => !requiresSource(f)); |
| 681 | const matched = workspace.list().filter(predicate); |
| 682 | |
| 683 | let slice = matched.map(r => |
| 684 | projectResource(r, graph, cheapFields, undefined) |
| 685 | ); |
| 686 | |
| 687 | if (query.sort) { |
| 688 | const { field, direction } = parseSortDescriptor(query.sort); |
| 689 | slice = [...slice].sort((a, b) => { |
| 690 | const cmp = compareValues(a[field], b[field]); |
| 691 | return direction === 'desc' ? -cmp : cmp; |
| 692 | }); |
| 693 | } |
| 694 | |
| 695 | const offset = query.offset ?? 0; |
| 696 | if (offset > 0) { |
| 697 | slice = slice.slice(offset); |
| 698 | } |
| 699 | if (query.limit !== undefined) { |
| 700 | slice = slice.slice(0, query.limit); |
| 701 | } |
| 702 | |
| 703 | const sourceFields = fields.filter(requiresSource); |
| 704 | if (sourceFields.length === 0) { |
| 705 | return { results: slice, warnings }; |
| 706 | } |
| 707 | |
| 708 | // Map keeps the row→Resource lookup at O(1) per surviving row. |
| 709 | const byPath = new Map<string, Resource>(); |
| 710 | for (const r of matched) byPath.set(r.uri.path, r); |
| 711 | |
| 712 | const results = slice.map(row => { |
| 713 | const resource = byPath.get(row.uri.path); |
| 714 | if (!resource) return row; |
| 715 | const sourceProjection = projectResource( |
| 716 | resource, |
| 717 | graph, |
| 718 | sourceFields, |
no test coverage detected