0
# Query System
1
2
Sophisticated query language and parsing system for filtering library content with support for field matching, regular expressions, numeric comparisons, and boolean logic. The query system enables powerful library searches and filtering.
3
4
## Capabilities
5
6
### Query Parsing Functions
7
8
Functions for parsing query strings into Query objects that can be executed against the database.
9
10
```python { .api }
11
def query_from_strings(strings: List[str], model_cls: Type) -> Query:
12
"""
13
Parse a list of query strings into a single Query object.
14
15
Parameters:
16
- strings: List of query string components
17
- model_cls: Item or Album class for context
18
19
Returns:
20
Combined Query object for database execution
21
"""
22
23
def parse_sorted_query(query_string: str, model_cls: Type) -> Tuple[Query, Sort]:
24
"""
25
Parse a query string that may include sort specifications.
26
27
Parameters:
28
- query_string: Complete query string with optional sort
29
- model_cls: Item or Album class for context
30
31
Returns:
32
Tuple of (Query object, Sort object)
33
"""
34
35
def parse_query_part(part: str, model_cls: Type) -> Query:
36
"""
37
Parse a single query component into a Query object.
38
39
Parameters:
40
- part: Single query string component
41
- model_cls: Item or Album class for context
42
43
Returns:
44
Query object for the component
45
"""
46
```
47
48
### Base Query Classes
49
50
Core query interface and base implementations for different query types.
51
52
```python { .api }
53
class Query:
54
"""Base class for all query types."""
55
56
def match(self, item: Union[Item, Album]) -> bool:
57
"""
58
Test whether this query matches an item or album.
59
60
Parameters:
61
- item: Item or Album object to test
62
63
Returns:
64
True if the query matches the object
65
"""
66
67
def clause(self) -> Tuple[str, List[Any]]:
68
"""
69
Generate SQL WHERE clause for database execution.
70
71
Returns:
72
Tuple of (SQL string, parameter list)
73
"""
74
75
class FieldQuery(Query):
76
"""Base class for queries that operate on specific fields."""
77
78
def __init__(self, field: str, pattern: Any, fast: bool = True):
79
"""
80
Initialize field-based query.
81
82
Parameters:
83
- field: Name of the field to query
84
- pattern: Pattern or value to match against
85
- fast: Whether to use fast database operations when possible
86
"""
87
```
88
89
### String Query Types
90
91
Query types for matching text fields with various string comparison methods.
92
93
```python { .api }
94
class StringQuery(FieldQuery):
95
"""Query for exact or partial string matches."""
96
97
def __init__(self, field: str, pattern: str, fast: bool = True):
98
"""
99
Create string matching query.
100
101
Parameters:
102
- field: Field name to search
103
- pattern: String pattern to match (supports wildcards)
104
- fast: Use database string operations when possible
105
"""
106
107
class SubstringQuery(StringQuery):
108
"""Query for substring matches (case-insensitive)."""
109
110
def __init__(self, field: str, pattern: str):
111
"""
112
Create substring matching query.
113
114
Parameters:
115
- field: Field name to search
116
- pattern: Substring to find within field values
117
"""
118
119
class RegexpQuery(FieldQuery):
120
"""Query using regular expressions for pattern matching."""
121
122
def __init__(self, field: str, pattern: str):
123
"""
124
Create regular expression query.
125
126
Parameters:
127
- field: Field name to search
128
- pattern: Regular expression pattern
129
"""
130
```
131
132
### Numeric Query Types
133
134
Query types for numeric comparisons and range operations.
135
136
```python { .api }
137
class NumericQuery(FieldQuery):
138
"""Query for numeric field comparisons."""
139
140
def __init__(self, field: str, point: float, interval: float = 0):
141
"""
142
Create numeric comparison query.
143
144
Parameters:
145
- field: Numeric field name
146
- point: Target value for comparison
147
- interval: Range around target (0 for exact match)
148
"""
149
150
class DateQuery(FieldQuery):
151
"""Query for date/time field comparisons."""
152
153
def __init__(self, field: str, date: str, precision: str = 'day'):
154
"""
155
Create date comparison query.
156
157
Parameters:
158
- field: Date field name (year, month, day, added, mtime)
159
- date: Date string in various formats
160
- precision: Precision level ('year', 'month', 'day')
161
"""
162
163
class DurationQuery(FieldQuery):
164
"""Query for duration/length field comparisons."""
165
166
def __init__(self, field: str, duration: str):
167
"""
168
Create duration comparison query.
169
170
Parameters:
171
- field: Duration field name (typically 'length')
172
- duration: Duration string (e.g., '3:30', '210s')
173
"""
174
```
175
176
### Boolean Logic Queries
177
178
Query types for combining multiple queries with logical operations.
179
180
```python { .api }
181
class AndQuery(Query):
182
"""Query that matches when ALL subqueries match."""
183
184
def __init__(self, subqueries: List[Query]):
185
"""
186
Create AND logic query.
187
188
Parameters:
189
- subqueries: List of Query objects that must all match
190
"""
191
192
class OrQuery(Query):
193
"""Query that matches when ANY subquery matches."""
194
195
def __init__(self, subqueries: List[Query]):
196
"""
197
Create OR logic query.
198
199
Parameters:
200
- subqueries: List of Query objects, any of which can match
201
"""
202
203
class NotQuery(Query):
204
"""Query that matches when subquery does NOT match."""
205
206
def __init__(self, subquery: Query):
207
"""
208
Create NOT logic query.
209
210
Parameters:
211
- subquery: Query object to negate
212
"""
213
```
214
215
### Special Query Types
216
217
Additional query types for specific use cases and edge conditions.
218
219
```python { .api }
220
class TrueQuery(Query):
221
"""Query that always matches (returns all items)."""
222
223
def match(self, item: Any) -> bool:
224
return True
225
226
class FalseQuery(Query):
227
"""Query that never matches (returns no items)."""
228
229
def match(self, item: Any) -> bool:
230
return False
231
232
class PathQuery(FieldQuery):
233
"""Query for filesystem path matching with platform-aware comparisons."""
234
235
def __init__(self, field: str, pattern: str):
236
"""
237
Create path matching query.
238
239
Parameters:
240
- field: Path field name (typically 'path')
241
- pattern: Path pattern with filesystem-appropriate separators
242
"""
243
244
class CollectionQuery(Query):
245
"""Query for matching items within specific collections or playlists."""
246
247
def __init__(self, collection_name: str):
248
"""
249
Create collection membership query.
250
251
Parameters:
252
- collection_name: Name of collection to match against
253
"""
254
```
255
256
## Query Language Syntax
257
258
### Basic Field Matching
259
260
```python
261
# Field equals value
262
items = lib.items('artist:Beatles')
263
items = lib.items('year:1969')
264
265
# Field contains substring
266
items = lib.items('title:love')
267
268
# Case-insensitive matching is default
269
items = lib.items('artist:beatles') # Matches "Beatles"
270
```
271
272
### Wildcards and Patterns
273
274
```python
275
# Wildcards (* and ?)
276
items = lib.items('artist:*Beatles*') # Contains "Beatles"
277
items = lib.items('title:Hey?') # "Hey" followed by one char
278
279
# Regular expressions (prefix with ~)
280
items = lib.items('artist:~(Beatles|Rolling Stones)')
281
items = lib.items('title:~^Love') # Starts with "Love"
282
```
283
284
### Numeric Comparisons
285
286
```python
287
# Exact values
288
items = lib.items('year:1969')
289
items = lib.items('track:5')
290
291
# Ranges (inclusive)
292
items = lib.items('year:1960..1970') # 1960 to 1970
293
items = lib.items('length:180..240') # 3-4 minutes
294
295
# Comparisons
296
items = lib.items('year:..1970') # Up to 1970
297
items = lib.items('year:1980..') # 1980 and later
298
```
299
300
### Date Queries
301
302
```python
303
# Specific dates
304
items = lib.items('added:2024-01-01')
305
items = lib.items('mtime:2024-01-01..2024-01-31')
306
307
# Relative dates
308
items = lib.items('added:1d..') # Added in last day
309
items = lib.items('added:1w..') # Added in last week
310
items = lib.items('added:1m..') # Added in last month
311
```
312
313
### Boolean Logic
314
315
```python
316
# AND (default when multiple terms)
317
items = lib.items('artist:Beatles year:1969')
318
319
# OR (comma-separated)
320
items = lib.items('artist:Beatles,Stones')
321
322
# NOT (prefix with -)
323
items = lib.items('artist:Beatles -album:Anthology')
324
325
# Grouping with parentheses
326
items = lib.items('(artist:Beatles year:1960..1970) genre:Rock')
327
```
328
329
### Path Queries
330
331
```python
332
# Path contains
333
items = lib.items('path:/music/rock/')
334
335
# Path patterns
336
items = lib.items('path:*.flac') # FLAC files only
337
items = lib.items('path:~/Music/*') # In Music directory
338
```
339
340
## Query Usage Examples
341
342
### Library Queries
343
344
```python
345
from beets.library import Library
346
from beets.dbcore.queryparse import query_from_strings, parse_sorted_query
347
348
lib = Library('/path/to/library.db', '/music')
349
350
# Simple queries
351
rock_items = lib.items('genre:Rock')
352
recent_albums = lib.albums('added:2024..')
353
354
# Complex queries
355
query_parts = ['artist:Beatles', 'year:1960..1970', '-album:Anthology']
356
beatles_60s = lib.items(' '.join(query_parts))
357
358
# Sorted queries
359
query, sort = parse_sorted_query('artist:Beatles year+', lib.Item)
360
sorted_items = lib.items(query, sort)
361
```
362
363
### Programmatic Query Building
364
365
```python
366
from beets.dbcore.query import AndQuery, StringQuery, NumericQuery
367
368
# Build queries programmatically
369
artist_query = StringQuery('artist', 'Beatles')
370
year_query = NumericQuery('year', 1969)
371
combined = AndQuery([artist_query, year_query])
372
373
# Use with library
374
items = lib.items(combined)
375
```
376
377
### Custom Query Types
378
379
```python
380
from beets.dbcore.query import FieldQuery
381
382
class GenreQuery(FieldQuery):
383
"""Custom query for genre matching with normalization."""
384
385
def __init__(self, field: str, genre: str):
386
# Normalize genre names
387
normalized = genre.lower().replace('-', ' ')
388
super().__init__(field, normalized)
389
390
def match(self, item):
391
item_genre = item.genre.lower().replace('-', ' ')
392
return self.pattern in item_genre
393
394
# Register custom query type
395
from beets.dbcore.queryparse import query_classes
396
query_classes['genre'] = GenreQuery
397
```
398
399
## Sort Operations
400
401
```python { .api }
402
class Sort:
403
"""Represents sorting specification for query results."""
404
405
def __init__(self, field: str, ascending: bool = True):
406
"""
407
Create sort specification.
408
409
Parameters:
410
- field: Field name to sort by
411
- ascending: True for ascending, False for descending
412
"""
413
414
class MultipleSort(Sort):
415
"""Sort by multiple fields in priority order."""
416
417
def __init__(self, sorts: List[Sort]):
418
"""
419
Create multi-field sort.
420
421
Parameters:
422
- sorts: List of Sort objects in priority order
423
"""
424
```
425
426
### Sort Syntax
427
428
```python
429
# Single field sorts
430
items = lib.items('artist:Beatles', sort='year+') # Ascending by year
431
items = lib.items('artist:Beatles', sort='year-') # Descending by year
432
433
# Multiple field sorts
434
items = lib.items('genre:Rock', sort='artist+ year-') # Artist asc, year desc
435
```
436
437
## Error Handling
438
439
```python { .api }
440
class InvalidQueryError(Exception):
441
"""Raised when query string cannot be parsed."""
442
443
class InvalidSortError(Exception):
444
"""Raised when sort specification is invalid."""
445
```
446
447
Common query errors:
448
- Invalid field names
449
- Malformed regular expressions
450
- Invalid date formats
451
- Syntax errors in complex queries