0
# Search Operations
1
2
Powerful search capabilities for finding paths and values matching glob patterns in nested dictionaries. These operations support filtering, yielding, and flexible result formats for different use cases.
3
4
## Capabilities
5
6
### Dictionary Search
7
8
Search dictionaries for paths matching glob patterns, returning structured results that preserve the original data hierarchy.
9
10
```python { .api }
11
def search(obj, glob, yielded=False, separator="/", afilter=None, dirs=True):
12
"""
13
Search for paths/values matching glob pattern.
14
15
Parameters:
16
- obj (MutableMapping): Target dictionary to search
17
- glob (Glob): Path pattern as string or sequence
18
- yielded (bool): If True, return generator of (path, value) tuples instead of dict
19
- separator (str): Path separator character (default "/")
20
- afilter (Filter): Optional function to filter results by value
21
- dirs (bool): Include non-leaf (directory-like) nodes in results (default True)
22
23
Returns:
24
dict or generator: Dictionary preserving structure, or generator of (path_str, value) tuples if yielded=True
25
"""
26
```
27
28
#### Usage Examples
29
30
```python
31
import dpath
32
33
data = {
34
"users": {
35
"john": {"age": 30, "city": "NYC", "hobbies": ["reading", "gaming"]},
36
"jane": {"age": 25, "city": "LA", "hobbies": ["hiking", "cooking"]},
37
"bob": {"age": 35, "city": "Chicago"}
38
},
39
"settings": {"theme": "dark", "lang": "en"}
40
}
41
42
# Search with wildcard - returns structured dict
43
cities = dpath.search(data, "users/*/city")
44
# Returns: {'users': {'john': {'city': 'NYC'}, 'jane': {'city': 'LA'}, 'bob': {'city': 'Chicago'}}}
45
46
# Search with yielded=True - returns generator of (path, value) pairs
47
for path, value in dpath.search(data, "users/*/city", yielded=True):
48
print(f"{path}: {value}")
49
# Output:
50
# users/john/city: NYC
51
# users/jane/city: LA
52
# users/bob/city: Chicago
53
54
# Recursive search with **
55
all_values = dpath.search(data, "**/age")
56
# Returns all age values regardless of depth
57
58
# Search excluding directories (dirs=False) - only leaf values
59
leaves_only = dpath.search(data, "users/**", dirs=False)
60
# Excludes intermediate dict objects, only includes final values
61
62
# Filter search results
63
def young_filter(value):
64
return isinstance(value, dict) and value.get("age", 0) < 30
65
66
young_users = dpath.search(data, "users/*", afilter=young_filter)
67
# Returns only jane's record
68
69
# Complex pattern matching
70
hobbies = dpath.search(data, "users/*/hobbies/*") # All individual hobbies
71
first_hobbies = dpath.search(data, "users/*/hobbies/0") # First hobby of each user
72
```
73
74
### Value Extraction
75
76
Extract just the values that match glob patterns, returning a simple list rather than preserving the dictionary structure.
77
78
```python { .api }
79
def values(obj, glob, separator="/", afilter=None, dirs=True):
80
"""
81
Get list of all values matching glob pattern.
82
83
Parameters:
84
- obj (MutableMapping): Target dictionary to search
85
- glob (Glob): Path pattern as string or sequence
86
- separator (str): Path separator character (default "/")
87
- afilter (Filter): Optional function to filter results by value
88
- dirs (bool): Include non-leaf (directory-like) nodes in results (default True)
89
90
Returns:
91
list: List of matching values in order found
92
"""
93
```
94
95
#### Usage Examples
96
97
```python
98
import dpath
99
100
data = {
101
"products": {
102
"electronics": {
103
"laptop": {"price": 1200, "stock": 5},
104
"phone": {"price": 800, "stock": 12}
105
},
106
"books": {
107
"python_guide": {"price": 45, "stock": 20},
108
"data_science": {"price": 60, "stock": 8}
109
}
110
}
111
}
112
113
# Extract all prices as a simple list
114
prices = dpath.values(data, "products/*/*/price")
115
# Returns: [1200, 800, 45, 60]
116
117
# Extract with filtering
118
def expensive_filter(price):
119
return isinstance(price, (int, float)) and price > 50
120
121
expensive_prices = dpath.values(data, "products/*/*/price", afilter=expensive_filter)
122
# Returns: [1200, 800, 60]
123
124
# Extract all product info (directories included by default)
125
all_products = dpath.values(data, "products/*/*")
126
# Returns list of product dictionaries
127
128
# Extract only leaf values (dirs=False)
129
leaf_values = dpath.values(data, "products/**", dirs=False)
130
# Returns: [1200, 5, 800, 12, 45, 20, 60, 8] (all leaf values)
131
132
# Extract with custom separator
133
values_custom_sep = dpath.values(data, "products.*.price", separator=".")
134
```
135
136
## Advanced Search Patterns
137
138
### Glob Pattern Syntax
139
140
dpath supports rich glob patterns for flexible searching:
141
142
```python
143
# Single-level wildcards
144
dpath.search(data, "users/*") # All direct children of users
145
dpath.search(data, "users/*/age") # Age field of all users
146
147
# Multi-level wildcards
148
dpath.search(data, "users/**") # Everything under users (recursive)
149
dpath.search(data, "**/age") # All age fields at any depth
150
151
# Character classes and ranges
152
dpath.search(data, "users/[jb]*") # Users starting with 'j' or 'b'
153
dpath.search(data, "item[0-9]") # Items with single digit suffix
154
155
# Complex combinations
156
dpath.search(data, "*/settings/**/lang") # Lang settings at any depth under any top-level key
157
```
158
159
### Filtering Functions
160
161
Create sophisticated filters to narrow search results:
162
163
```python
164
# Filter by value type
165
def string_filter(value):
166
return isinstance(value, str)
167
168
strings = dpath.values(data, "**", afilter=string_filter)
169
170
# Filter by value content
171
def contains_email(value):
172
return isinstance(value, str) and "@" in value
173
174
emails = dpath.values(data, "**", afilter=contains_email)
175
176
# Filter by dictionary structure
177
def has_required_fields(value):
178
if not isinstance(value, dict):
179
return False
180
return all(field in value for field in ["name", "age", "email"])
181
182
valid_users = dpath.search(data, "users/*", afilter=has_required_fields)
183
184
# Combine filters
185
def active_adult_filter(user):
186
return (isinstance(user, dict) and
187
user.get("active", False) and
188
user.get("age", 0) >= 18)
189
```
190
191
### Working with Generators
192
193
When using `yielded=True`, work efficiently with large datasets:
194
195
```python
196
# Process results incrementally
197
def process_large_dataset(data):
198
for path, value in dpath.search(data, "records/**", yielded=True):
199
# Process one item at a time without loading everything into memory
200
if meets_criteria(value):
201
yield transform(value)
202
203
# Early termination
204
def find_first_match(data, pattern, condition):
205
for path, value in dpath.search(data, pattern, yielded=True):
206
if condition(value):
207
return path, value
208
return None, None
209
210
# Collect specific information
211
paths_and_types = [(path, type(value).__name__)
212
for path, value in dpath.search(data, "**", yielded=True)]
213
```
214
215
## Integration with Other Operations
216
217
Search operations integrate seamlessly with other dpath functions:
218
219
```python
220
# Find then modify
221
matching_paths = [path for path, _ in dpath.search(data, "users/*/active", yielded=True)]
222
for path in matching_paths:
223
dpath.set(data, path, False)
224
225
# Search and extract for processing
226
user_ages = dpath.values(data, "users/*/age")
227
average_age = sum(user_ages) / len(user_ages)
228
229
# Complex workflows
230
active_users = dpath.search(data, "users/*", afilter=lambda u: u.get("active"))
231
for user_path, user_data in dpath.search(active_users, "**", yielded=True):
232
# Process each active user
233
pass
234
```