Skip to content

Query

Provides an interactive query builder for Notion databases.

CheckboxCondition

Bases: GenericObject

Represents checkbox criteria in Notion.

Source code in src/notional/query.py
44
45
46
47
48
class CheckboxCondition(GenericObject):
    """Represents checkbox criteria in Notion."""

    equals: Optional[bool] = None
    does_not_equal: Optional[bool] = None

CompoundFilter

Bases: QueryFilter

Represents a compound filter in Notion.

Source code in src/notional/query.py
189
190
191
192
193
194
195
196
197
198
class CompoundFilter(QueryFilter):
    """Represents a compound filter in Notion."""

    class Config:
        """Pydantic configuration class to support keyword fields."""

        allow_population_by_field_name = True

    and_: Optional[List[QueryFilter]] = Field(None, alias="and")
    or_: Optional[List[QueryFilter]] = Field(None, alias="or")

Config

Pydantic configuration class to support keyword fields.

Source code in src/notional/query.py
192
193
194
195
class Config:
    """Pydantic configuration class to support keyword fields."""

    allow_population_by_field_name = True

CreatedTimeFilter

Bases: TimestampFilter

Represents a created_time filter in Notion.

Source code in src/notional/query.py
165
166
167
168
169
170
171
172
173
174
class CreatedTimeFilter(TimestampFilter):
    """Represents a created_time filter in Notion."""

    created_time: DateCondition
    timestamp: TimestampKind = TimestampKind.CREATED_TIME

    @classmethod
    def __compose__(cls, value):
        """Create a new `CreatedTimeFilter` using the given constraint."""
        return CreatedTimeFilter(created_time=value)

__compose__(value) classmethod

Create a new CreatedTimeFilter using the given constraint.

Source code in src/notional/query.py
171
172
173
174
@classmethod
def __compose__(cls, value):
    """Create a new `CreatedTimeFilter` using the given constraint."""
    return CreatedTimeFilter(created_time=value)

DateCondition

Bases: GenericObject

Represents date criteria in Notion.

Source code in src/notional/query.py
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
class DateCondition(GenericObject):
    """Represents date criteria in Notion."""

    equals: Optional[Union[date, datetime]] = None
    before: Optional[Union[date, datetime]] = None
    after: Optional[Union[date, datetime]] = None
    on_or_before: Optional[Union[date, datetime]] = None
    on_or_after: Optional[Union[date, datetime]] = None

    is_empty: Optional[bool] = None
    is_not_empty: Optional[bool] = None

    past_week: Optional[Any] = None
    past_month: Optional[Any] = None
    past_year: Optional[Any] = None
    next_week: Optional[Any] = None
    next_month: Optional[Any] = None
    next_year: Optional[Any] = None

FilesCondition

Bases: GenericObject

Represents files criteria in Notion.

Source code in src/notional/query.py
 98
 99
100
101
102
class FilesCondition(GenericObject):
    """Represents files criteria in Notion."""

    is_empty: Optional[bool] = None
    is_not_empty: Optional[bool] = None

FormulaCondition

Bases: GenericObject

Represents formula criteria in Notion.

Source code in src/notional/query.py
114
115
116
117
118
119
120
class FormulaCondition(GenericObject):
    """Represents formula criteria in Notion."""

    string: Optional[TextCondition] = None
    checkbox: Optional[CheckboxCondition] = None
    number: Optional[NumberCondition] = None
    date: Optional[DateCondition] = None

LastEditedTimeFilter

Bases: TimestampFilter

Represents a last_edited_time filter in Notion.

Source code in src/notional/query.py
177
178
179
180
181
182
183
184
185
186
class LastEditedTimeFilter(TimestampFilter):
    """Represents a last_edited_time filter in Notion."""

    last_edited_time: DateCondition
    timestamp: TimestampKind = TimestampKind.LAST_EDITED_TIME

    @classmethod
    def __compose__(cls, value):
        """Create a new `LastEditedTimeFilter` using the given constraint."""
        return LastEditedTimeFilter(last_edited_time=value)

__compose__(value) classmethod

Create a new LastEditedTimeFilter using the given constraint.

Source code in src/notional/query.py
183
184
185
186
@classmethod
def __compose__(cls, value):
    """Create a new `LastEditedTimeFilter` using the given constraint."""
    return LastEditedTimeFilter(last_edited_time=value)

MultiSelectCondition

Bases: GenericObject

Represents a multi_select criteria in Notion.

Source code in src/notional/query.py
60
61
62
63
64
65
66
class MultiSelectCondition(GenericObject):
    """Represents a multi_select criteria in Notion."""

    contains: Optional[str] = None
    does_not_contains: Optional[str] = None
    is_empty: Optional[bool] = None
    is_not_empty: Optional[bool] = None

NumberCondition

Bases: GenericObject

Represents number criteria in Notion.

Source code in src/notional/query.py
31
32
33
34
35
36
37
38
39
40
41
class NumberCondition(GenericObject):
    """Represents number criteria in Notion."""

    equals: Optional[Union[float, int]] = None
    does_not_equal: Optional[Union[float, int]] = None
    greater_than: Optional[Union[float, int]] = None
    less_than: Optional[Union[float, int]] = None
    greater_than_or_equal_to: Optional[Union[float, int]] = None
    less_than_or_equal_to: Optional[Union[float, int]] = None
    is_empty: Optional[bool] = None
    is_not_empty: Optional[bool] = None

PeopleCondition

Bases: GenericObject

Represents people criteria in Notion.

Source code in src/notional/query.py
89
90
91
92
93
94
95
class PeopleCondition(GenericObject):
    """Represents people criteria in Notion."""

    contains: Optional[UUID] = None
    does_not_contain: Optional[UUID] = None
    is_empty: Optional[bool] = None
    is_not_empty: Optional[bool] = None

PropertyFilter

Bases: QueryFilter

Represents a database property filter in Notion.

Source code in src/notional/query.py
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
class PropertyFilter(QueryFilter):
    """Represents a database property filter in Notion."""

    property: str

    rich_text: Optional[TextCondition] = None
    phone_number: Optional[TextCondition] = None
    number: Optional[NumberCondition] = None
    checkbox: Optional[CheckboxCondition] = None
    select: Optional[SelectCondition] = None
    multi_select: Optional[MultiSelectCondition] = None
    date: Optional[DateCondition] = None
    people: Optional[PeopleCondition] = None
    files: Optional[FilesCondition] = None
    relation: Optional[RelationCondition] = None
    formula: Optional[FormulaCondition] = None

PropertySort

Bases: GenericObject

Represents a sort instruction in Notion.

Source code in src/notional/query.py
208
209
210
211
212
213
class PropertySort(GenericObject):
    """Represents a sort instruction in Notion."""

    property: Optional[str] = None
    timestamp: Optional[TimestampKind] = None
    direction: Optional[SortDirection] = None

Query

Bases: GenericObject

Represents a query object in Notion.

Source code in src/notional/query.py
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
class Query(GenericObject):
    """Represents a query object in Notion."""

    sorts: Optional[List[PropertySort]] = None
    filter: Optional[QueryFilter] = None
    start_cursor: Optional[UUID] = None
    page_size: int = MAX_PAGE_SIZE

    @validator("page_size")
    def valid_page_size(cls, value):
        """Validate that the given page size meets the Notion API requirements."""

        assert value > 0, "size must be greater than zero"
        assert value <= MAX_PAGE_SIZE, "size must be less than or equal to 100"

        return value

valid_page_size(value)

Validate that the given page size meets the Notion API requirements.

Source code in src/notional/query.py
224
225
226
227
228
229
230
231
@validator("page_size")
def valid_page_size(cls, value):
    """Validate that the given page size meets the Notion API requirements."""

    assert value > 0, "size must be greater than zero"
    assert value <= MAX_PAGE_SIZE, "size must be less than or equal to 100"

    return value

QueryBuilder

A query builder for the Notion API.

:param endpoint: the session endpoint used to execute the query :param datatype: an optional class to capture results :param params: optional params that will be passed to the query

Source code in src/notional/query.py
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
class QueryBuilder:
    """A query builder for the Notion API.

    :param endpoint: the session endpoint used to execute the query
    :param datatype: an optional class to capture results
    :param params: optional params that will be passed to the query
    """

    def __init__(self, endpoint, datatype=None, **params):
        """Initialize a new `QueryBuilder` for the given endpoint."""

        self.endpoint = endpoint
        self.datatype = datatype
        self.params = params

        self.query = Query()

    def filter(self, filter=None, **kwargs):
        """Add the given filter to the query."""

        if filter is None:
            if isinstance(self.endpoint, SearchEndpoint):
                filter = SearchFilter.parse_obj(kwargs)
            elif "property" in kwargs:
                filter = PropertyFilter.parse_obj(kwargs)
            elif "timestamp" in kwargs and kwargs["timestamp"] == "created_time":
                filter = CreatedTimeFilter.parse_obj(kwargs)
            elif "timestamp" in kwargs and kwargs["timestamp"] == "last_edited_time":
                filter = LastEditedTimeFilter.parse_obj(kwargs)
            else:
                raise ValueError("unrecognized filter")

        elif not isinstance(filter, QueryFilter):
            raise ValueError("filter must be of type QueryFilter")

        # use CompoundFilter when necessary...

        if self.query.filter is None:
            self.query.filter = filter

        elif isinstance(self.query.filter, CompoundFilter):
            self.query.filter.and_.append(filter)

        else:
            old_filter = self.query.filter
            self.query.filter = CompoundFilter(and_=[old_filter, filter])

        return self

    def sort(self, sort=None, **kwargs):
        """Add the given sort elements to the query."""

        # XXX should this support ORM properties also?
        # e.g. - query.sort(property=Task.Title)
        # but users won't always use ORM for queries...

        if sort is None:
            sort = PropertySort(**kwargs)

        elif not isinstance(filter, PropertySort):
            raise ValueError("sort must be of type PropertySort")

        # use multiple sorts when necessary

        if self.query.sorts is None:
            self.query.sorts = [sort]

        else:
            self.query.sorts.append(sort)

        return self

    def start_at(self, page_id):
        """Set the start cursor to a specific page ID."""

        self.query.start_cursor = page_id

        return self

    def limit(self, count):
        """Limit the number of results to the given count."""

        self.query.page_size = count

        return self

    def execute(self):
        """Execute the current query and return an iterator for the results."""

        if self.endpoint is None:
            raise ValueError("cannot execute query; no endpoint provided")

        logger.debug("executing query - %s", self.query)

        query = self.query.dict()

        if self.params:
            query.update(self.params)

        return EndpointIterator(self.endpoint, datatype=self.datatype)(**query)

    def first(self):
        """Execute the current query and return the first result only."""

        try:
            return next(self.execute())
        except StopIteration:
            logger.debug("iterator returned empty result set")

        return None

__init__(endpoint, datatype=None, **params)

Initialize a new QueryBuilder for the given endpoint.

Source code in src/notional/query.py
242
243
244
245
246
247
248
249
def __init__(self, endpoint, datatype=None, **params):
    """Initialize a new `QueryBuilder` for the given endpoint."""

    self.endpoint = endpoint
    self.datatype = datatype
    self.params = params

    self.query = Query()

execute()

Execute the current query and return an iterator for the results.

Source code in src/notional/query.py
320
321
322
323
324
325
326
327
328
329
330
331
332
333
def execute(self):
    """Execute the current query and return an iterator for the results."""

    if self.endpoint is None:
        raise ValueError("cannot execute query; no endpoint provided")

    logger.debug("executing query - %s", self.query)

    query = self.query.dict()

    if self.params:
        query.update(self.params)

    return EndpointIterator(self.endpoint, datatype=self.datatype)(**query)

filter(filter=None, **kwargs)

Add the given filter to the query.

Source code in src/notional/query.py
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
def filter(self, filter=None, **kwargs):
    """Add the given filter to the query."""

    if filter is None:
        if isinstance(self.endpoint, SearchEndpoint):
            filter = SearchFilter.parse_obj(kwargs)
        elif "property" in kwargs:
            filter = PropertyFilter.parse_obj(kwargs)
        elif "timestamp" in kwargs and kwargs["timestamp"] == "created_time":
            filter = CreatedTimeFilter.parse_obj(kwargs)
        elif "timestamp" in kwargs and kwargs["timestamp"] == "last_edited_time":
            filter = LastEditedTimeFilter.parse_obj(kwargs)
        else:
            raise ValueError("unrecognized filter")

    elif not isinstance(filter, QueryFilter):
        raise ValueError("filter must be of type QueryFilter")

    # use CompoundFilter when necessary...

    if self.query.filter is None:
        self.query.filter = filter

    elif isinstance(self.query.filter, CompoundFilter):
        self.query.filter.and_.append(filter)

    else:
        old_filter = self.query.filter
        self.query.filter = CompoundFilter(and_=[old_filter, filter])

    return self

first()

Execute the current query and return the first result only.

Source code in src/notional/query.py
335
336
337
338
339
340
341
342
343
def first(self):
    """Execute the current query and return the first result only."""

    try:
        return next(self.execute())
    except StopIteration:
        logger.debug("iterator returned empty result set")

    return None

limit(count)

Limit the number of results to the given count.

Source code in src/notional/query.py
313
314
315
316
317
318
def limit(self, count):
    """Limit the number of results to the given count."""

    self.query.page_size = count

    return self

sort(sort=None, **kwargs)

Add the given sort elements to the query.

Source code in src/notional/query.py
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
def sort(self, sort=None, **kwargs):
    """Add the given sort elements to the query."""

    # XXX should this support ORM properties also?
    # e.g. - query.sort(property=Task.Title)
    # but users won't always use ORM for queries...

    if sort is None:
        sort = PropertySort(**kwargs)

    elif not isinstance(filter, PropertySort):
        raise ValueError("sort must be of type PropertySort")

    # use multiple sorts when necessary

    if self.query.sorts is None:
        self.query.sorts = [sort]

    else:
        self.query.sorts.append(sort)

    return self

start_at(page_id)

Set the start cursor to a specific page ID.

Source code in src/notional/query.py
306
307
308
309
310
311
def start_at(self, page_id):
    """Set the start cursor to a specific page ID."""

    self.query.start_cursor = page_id

    return self

QueryFilter

Bases: GenericObject

Base class for query filters.

Source code in src/notional/query.py
123
124
class QueryFilter(GenericObject):
    """Base class for query filters."""

RelationCondition

Bases: GenericObject

Represents relation criteria in Notion.

Source code in src/notional/query.py
105
106
107
108
109
110
111
class RelationCondition(GenericObject):
    """Represents relation criteria in Notion."""

    contains: Optional[UUID] = None
    does_not_contain: Optional[UUID] = None
    is_empty: Optional[bool] = None
    is_not_empty: Optional[bool] = None

SearchFilter

Bases: QueryFilter

Represents a search property filter in Notion.

Source code in src/notional/query.py
145
146
147
148
149
class SearchFilter(QueryFilter):
    """Represents a search property filter in Notion."""

    property: str
    value: str

SelectCondition

Bases: GenericObject

Represents select criteria in Notion.

Source code in src/notional/query.py
51
52
53
54
55
56
57
class SelectCondition(GenericObject):
    """Represents select criteria in Notion."""

    equals: Optional[str] = None
    does_not_equal: Optional[str] = None
    is_empty: Optional[bool] = None
    is_not_empty: Optional[bool] = None

SortDirection

Bases: str, Enum

Sort direction options.

Source code in src/notional/query.py
201
202
203
204
205
class SortDirection(str, Enum):
    """Sort direction options."""

    ASCENDING = "ascending"
    DESCENDING = "descending"

TextCondition

Bases: GenericObject

Represents text criteria in Notion.

Source code in src/notional/query.py
18
19
20
21
22
23
24
25
26
27
28
class TextCondition(GenericObject):
    """Represents text criteria in Notion."""

    equals: Optional[str] = None
    does_not_equal: Optional[str] = None
    contains: Optional[str] = None
    does_not_contain: Optional[str] = None
    starts_with: Optional[str] = None
    ends_with: Optional[str] = None
    is_empty: Optional[bool] = None
    is_not_empty: Optional[bool] = None

TimestampFilter

Bases: QueryFilter

Represents a timestamp filter in Notion.

Source code in src/notional/query.py
159
160
161
162
class TimestampFilter(QueryFilter):
    """Represents a timestamp filter in Notion."""

    timestamp: TimestampKind

TimestampKind

Bases: str, Enum

Possible timestamp types.

Source code in src/notional/query.py
152
153
154
155
156
class TimestampKind(str, Enum):
    """Possible timestamp types."""

    CREATED_TIME = "created_time"
    LAST_EDITED_TIME = "last_edited_time"