Deep Scanning
Relevant source files
The following files were used as context for generating this wiki page:
Deep Scanning is a specialized feature of the data-path library that enables recursive traversal and matching of properties at any level of depth within a nested object structure. By utilizing the ** wildcard, developers can target specific fields across a data tree without needing to specify the absolute path for every occurrence.
This functionality is primarily exposed through the .deep() method, which transforms a standard Path into a TemplatePath. This allows for bulk operations such as gathering all values matching a pattern or applying immutable updates to multiple nodes simultaneously.
Sources: README.md:162-164, src/types.ts:251-260
Architecture and Components
The deep scanning system is built upon the interaction between path implementations and template-specific resolution logic. It leverages the multi-level wildcard ** to signify recursive descent.
The .deep() Method
The .deep() method is defined in both PathImpl and TemplatePathImpl. It accepts an optional lambda expression that defines the tail of the path to be matched after the recursive search.
- Path Extension: If an expression is provided, the library uses a Proxy-based mechanism to record segments of the tail and appends them after the
DEEP_WILDCARDsegment. - ReturnType: It returns a
TemplatePath, which extends the standardPathinterface with bulk-handling capabilities.
Sources: src/impl/path-impl.ts:187-205, src/impl/template-path-impl.ts:47-66
Multi-Level Wildcard Representation
In the internal segment array and string representation, deep scanning is represented by the DEEP_WILDCARD constant.
| Wildcard | String Representation | Internal Segment | Purpose |
|---|---|---|---|
| Deep Wildcard | ** | ** | Recursive traversal across any number of levels. |
| Single Wildcard | * | * | Matches exactly one level (array index or object key). |
Sources: src/impl/template-path-impl.ts:63, src/utils.ts:51-53
Recursive Resolution Logic
The core of Deep Scanning lies in the expand(data) method. This method resolves a template path into an array of concrete Path objects based on the actual structure of the provided data.
The Walk Algorithm
The expand method uses a recursive walk function to navigate the data tree. When the algorithm encounters a DEEP_WILDCARD (**), it performs two concurrent actions:
- Current Match: It attempts to match the remaining segments of the template path against the current data node.
- Descendant Exploration: It recursively calls itself for every key (in an object) or index (in an array) of the current node, effectively branching the search into all descendants.
The following diagram illustrates how the walk function branches when encountering a deep wildcard:
The logic ensures that if a property exists at multiple levels of a tree, all occurrences are captured as separate concrete paths.
Sources: src/impl/template-path-impl.ts:81-114
Data Access and Manipulation
Once a deep scanning template is created, it can be used for bulk data operations:
- .get(data): Resolves the template into concrete paths, then maps over them to return an array of all values found.
- .set(data, value): Resolves the template into concrete paths and iteratively applies immutable updates to the data, returning a single updated root object.
Sources: src/impl/template-path-impl.ts:124-132, src/impl/template-path-impl.ts:145-152
API Usage Examples
Deep scanning is typically used to find identifiers or specific metadata across complex trees.
Targeting Properties at Any Depth
interface Node { id: string; children?: Node[] }
const root = path<Node>();
// Creates a path that targets every 'id' field in the tree
const allIds = root.deep(n => n.id);
console.log(allIds.$); // "**.id"
Sources: src/tests/templates.spec.ts:63-67
Combining Single and Deep Wildcards
Templates can mix * (one level) and ** (any level) for complex queries.
// Match any 'label' inside the 'tree' property of every item in an array
const tmpl = path((p: Data) => p.items)
.each(i => i.tree)
.deep(n => n.label);
Sources: src/tests/templates.spec.ts:128-134
Relational Algebra for Deep Paths
The data-path library supports relational checks for deep paths, allowing developers to determine if a concrete path falls under a deep scanning pattern.
- patternMatches: A utility function that checks if a concrete path's segments match a template pattern. It returns
trueif every segment matches, treating wildcards as valid substitutes. - matchesPrefix: Used for
.startsWith()and.includes(), this function specifically handles theDEEP_WILDCARDby allowing "skipping" segments in the concrete path to find a match for the remainder of the pattern.
Conclusion
Deep Scanning provides a powerful mechanism for bulk data interaction in nested structures. By abstracting the exact location of data behind the ** wildcard, it allows for flexible, type-safe operations that automatically adapt to the shape of the data at runtime through the expand mechanism.