surreal-orm-lite 0.34.2__tar.gz → 0.35.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/CHANGELOG.md +192 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/PKG-INFO +373 -51
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/README.md +372 -50
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/pyproject.toml +1 -1
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/__init__.py +8 -1
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/connection_manager.py +105 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/model_auth.py +49 -19
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/model_base.py +293 -26
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/query_set.py +329 -72
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/schema.py +17 -11
- surreal_orm_lite-0.35.2/src/surreal_orm_lite/search.py +1390 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/subquery.py +4 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/transaction.py +18 -5
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/.gitignore +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/LICENSE +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/Makefile +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/_sdk.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/aggregations.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/auth.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/cache.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/changefeed.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/concurrency.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/connection_config.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/constants.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/decoding.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/define_parser.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/edge.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/embedded.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/encoding.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/enum.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/exceptions.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/functions.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/geo.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/introspection.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/live.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/model_ddl.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/model_generator.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/py.typed +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/q.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/routing.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/schema_state.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/signals.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/surql_literal.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/testing/__init__.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/testing/database.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/testing/fixtures.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/time_travel.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/types.py +0 -0
- {surreal_orm_lite-0.34.2 → surreal_orm_lite-0.35.2}/src/surreal_orm_lite/utils.py +0 -0
|
@@ -5,6 +5,198 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.35.2] - 2026-10-22
|
|
9
|
+
|
|
10
|
+
An auth model whose signup field is declared `Field(exclude=True)` is refused whatever its schema
|
|
11
|
+
mode (#299): v0.34.1 refused it only for a `SCHEMAFULL` table, and on any other table its
|
|
12
|
+
`update()` or `upsert()` dropped the stored password hash. No new public API.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- **An auth model with an excluded signup field raises `TypeError` whatever its schema mode and
|
|
17
|
+
`with_table`** (#299). For a model built on `AuthenticatedUserMixin` with a field SIGNUP writes
|
|
18
|
+
that `Field(exclude=True)` keeps out of `model_dump()` (`password: str = Field(exclude=True)`),
|
|
19
|
+
`table_ddl()`, `define_table()`, `access_ddl()` and `define_access()` (with or without the
|
|
20
|
+
table) now raise `TypeError` before any request on a `SCHEMALESS` table, with no declared schema
|
|
21
|
+
mode and with `with_table=False`, as v0.34.1 did for a `SCHEMAFULL` table. `signup()` and
|
|
22
|
+
`signin()` raise the same error before any request, so an access defined by hand cannot leave
|
|
23
|
+
such a record either. The message names the model and the fields in declaration order and keeps
|
|
24
|
+
the advice: remove `exclude=True` and leave the field out where you serialise the model instead
|
|
25
|
+
(`model_dump(exclude={"password"})`). A model that relied on `exclude=True` now fails at
|
|
26
|
+
start-up instead of losing data later. Auth models without `exclude=True`, and models that are
|
|
27
|
+
not auth models, render and apply as before.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- **An auth model no longer loses its stored password hash at `update()` or `upsert()`** (#299).
|
|
32
|
+
Both replace the record with what `model_dump()` writes, so an auth model whose signup field was
|
|
33
|
+
declared `Field(exclude=True)` lost the stored value at its first `update()` or `upsert()`, with
|
|
34
|
+
no error, on a `SCHEMALESS` table (a model with neither `table_type` nor `schema_mode`) or with
|
|
35
|
+
`define_access(with_table=False)` over an optional column defined by hand
|
|
36
|
+
(`TYPE option<string>`): the hash became `NONE` and every later signin failed. With a required
|
|
37
|
+
column (`TYPE string`), the write was refused instead. Such a model is now refused before it
|
|
38
|
+
writes a record (see Changed). README section 19, "User models" and a row of "ORM behaviour:
|
|
39
|
+
SurrealDB 2.x vs 3.x" describe it; the behaviour is the same on both lines.
|
|
40
|
+
|
|
41
|
+
### Notes
|
|
42
|
+
|
|
43
|
+
- **Undeclared keys in list items on SurrealDB 3.x** (#301): the 0.34.1 entry below says 3.x
|
|
44
|
+
keeps a key a `SCHEMAFULL` table does not define "in the items of any list of objects,
|
|
45
|
+
`FLEXIBLE` or not". It keeps it only in the items of a list whose items define a field, such as
|
|
46
|
+
a list of sub-models `define_table()` writes, and in an object inside them that defines one. It
|
|
47
|
+
refuses it in the items of a list that defines no item field (an `array<object>` defined by hand
|
|
48
|
+
without `v.*.n`), at the top level and in a sub-model outside such a list. SurrealDB's
|
|
49
|
+
documentation of `FLEXIBLE` says every object of a `SCHEMAFULL` table, list items included,
|
|
50
|
+
accepts only the keys defined for it, so do not rely on 3.x keeping such a key. README section
|
|
51
|
+
35, the v0.7.0 and v0.34.0 rows of "ORM behaviour: SurrealDB 2.x vs 3.x" and the embedded
|
|
52
|
+
engine's "Model table DDL (v0.34.0)" row now state the exception. No ORM behaviour changes.
|
|
53
|
+
- Server pins unchanged: tested against SurrealDB 3.3.4 and 2.7.0 and on the embedded engine,
|
|
54
|
+
plus 3.1.5 for backward compatibility.
|
|
55
|
+
|
|
56
|
+
## [0.35.0] - 2026-10-22
|
|
57
|
+
|
|
58
|
+
Full-text search: a queryset searches text with the full SurrealDB-ORM's `search()`, and scores and
|
|
59
|
+
highlights the matches with `SearchScore` and `SearchHighlight` (plus `SearchOffsets`), over the
|
|
60
|
+
analyzers and full-text indexes a model declares with `Analyzer` and `Index` and applies with
|
|
61
|
+
`define_indexes()`. SurrealDB tokenises, matches, scores (BM25) and highlights on the server; the
|
|
62
|
+
ORM binds every search text and highlight tag as a query variable. The same code runs on SurrealDB
|
|
63
|
+
2.x, 3.x and the embedded engine, each line getting its own index keyword. The first minor of Tier
|
|
64
|
+
3's search phase.
|
|
65
|
+
|
|
66
|
+
### Added
|
|
67
|
+
|
|
68
|
+
- **`QuerySet.search(**field_queries)`**, the full SurrealDB-ORM's method: keeps the records whose
|
|
69
|
+
fields match. Each keyword names a field — a Python field name (an aliased field searches its
|
|
70
|
+
column) or a dotted path — and the text to match, bound, never written into the statement. The
|
|
71
|
+
terms of one text are AND-ed, and so are the fields; each field gets the next match reference, in
|
|
72
|
+
call order and across calls. `search()` applies wherever `filter()` does, AND-ed after
|
|
73
|
+
`filter()`, `Q` and `nearby()`: `exec()`, `first()`, `get()` without an id, `count()`,
|
|
74
|
+
`exists()`, the aggregations, `values()` groups, `bulk_update()`, `bulk_delete()`, `patch()` and a
|
|
75
|
+
`Subquery`, whose references start again at 0 (the full ORM applies it to `exec()` only).
|
|
76
|
+
`get(<id>)` and `all()` ignore it, as they ignore `filter()`.
|
|
77
|
+
- **`SearchScore(ref)` and `SearchHighlight(open_tag, close_tag, ref)`**, the full ORM's search
|
|
78
|
+
annotations, **and `SearchOffsets(ref)`**, an addition (SurrealDB's `search::offsets()`), in
|
|
79
|
+
`annotate()`: the BM25 score (a `float`), the text with each matched term between the tags (bound
|
|
80
|
+
as query variables), and where the matched terms sit. `exec()`, `first()` and `get()` without an
|
|
81
|
+
id set each value as an attribute named after its alias, never written back (`model_dump()`,
|
|
82
|
+
`save()` and `update()` leave it out); `order_by("-relevance")` sorts on an alias. A reference no
|
|
83
|
+
`search()` gave, or an alias that would shadow a field, its column or an attribute of the model,
|
|
84
|
+
raises `ValueError` before any request.
|
|
85
|
+
- **`Analyzer` and `Index`**, with the argument names of the full ORM's `DefineAnalyzer` and
|
|
86
|
+
`CreateIndex`: an analyzer's name, tokenizers (`blank`, `camel`, `class`, `punct`), filters
|
|
87
|
+
(`ascii`, `lowercase`, `uppercase`, `ngram`, `edgengram`, `snowball`, `mapper`), `function=` and
|
|
88
|
+
`comment=`; a plain index, a `UNIQUE` one (`unique=True`) or a full-text one (`search_analyzer=`,
|
|
89
|
+
an `Analyzer` or the name of an analyzer the database defines), which covers one field and takes
|
|
90
|
+
`bm25=` (`True`, or a `(k1, b)` pair) and `highlights=True`. Every argument is checked at
|
|
91
|
+
construction, more strictly than the server where its answer would be silently wrong.
|
|
92
|
+
- **`SurrealConfigDict(indexes=[Index(...)])`** declares a model's indexes, checked when the class
|
|
93
|
+
is defined (distinct names, one full-text index per column, one `Analyzer` per analyzer name);
|
|
94
|
+
`get_indexes()` returns them in declaration order, and `Model.index_ddl()` renders their
|
|
95
|
+
`DEFINE ANALYZER` and `DEFINE INDEX` statements without a connection, for SurrealDB 3.x or, with
|
|
96
|
+
`line=2`, for 2.x.
|
|
97
|
+
- **`await Model.define_indexes()`** applies them on the model's connection: it reads what the
|
|
98
|
+
database holds (`INFO FOR DB`, `INFO FOR TABLE`) and, when a full-text index is declared, the
|
|
99
|
+
server line (`client.version()`), then defines what is missing and redefines what changed, one
|
|
100
|
+
statement per request, and returns the statements applied — `[]` on an unchanged database, so it
|
|
101
|
+
is safe at start-up. `overwrite=False` defines only what is missing (`IF NOT EXISTS`); `tx=` joins
|
|
102
|
+
a transaction. Nothing is removed: **`await Model.remove_index(name)`** removes an index by name
|
|
103
|
+
(`REMOVE INDEX IF EXISTS`; `missing_ok=False` raises `SurrealDbNotFoundError` for a missing one).
|
|
104
|
+
- **`SurrealDBConnectionManager.define_analyzer(analyzer)` and `remove_analyzer(analyzer)`**, for
|
|
105
|
+
the analyzers, which belong to the database rather than to a table: `define_analyzer()` defines
|
|
106
|
+
one on the current connection, redefining it only when it changed, and returns the statements
|
|
107
|
+
applied; `remove_analyzer()` removes one by name and refuses one an index uses.
|
|
108
|
+
- README section 36, "Full-text search (`search()`, `SearchScore`, `SearchHighlight`, `Analyzer`,
|
|
109
|
+
`Index`)" (declaring and applying, searching, scoring and highlighting, what SurrealDB decides,
|
|
110
|
+
changing an analyzer, transactions, live queries, what is not supported, a note for users of the
|
|
111
|
+
full SurrealDB-ORM), seven rows of the "ORM behaviour: SurrealDB 2.x vs 3.x" table and one of the
|
|
112
|
+
embedded engine's, and the example notebook `examples/v0.35.0_full_text_search.ipynb`.
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
ENGLISH = Analyzer("english", ["class", "blank"], ["lowercase", "ascii", "snowball(english)"])
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class Article(BaseSurrealModel):
|
|
119
|
+
model_config = SurrealConfigDict(
|
|
120
|
+
table_name="article",
|
|
121
|
+
indexes=[Index("article_title", "title", search_analyzer=ENGLISH, bm25=True, highlights=True)],
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
id: str | None = None
|
|
125
|
+
title: str
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
await Article.define_table()
|
|
129
|
+
await Article.define_indexes() # FULLTEXT on SurrealDB 3.x, SEARCH on 2.x; [] when nothing changed
|
|
130
|
+
articles = await (
|
|
131
|
+
Article.objects()
|
|
132
|
+
.search(title="physics")
|
|
133
|
+
.annotate(relevance=SearchScore(0), snippet=SearchHighlight("<b>", "</b>", 0))
|
|
134
|
+
.order_by("-relevance")
|
|
135
|
+
.exec()
|
|
136
|
+
)
|
|
137
|
+
# each article carries .relevance (a float) and .snippet (its title, "<b>physics</b>" highlighted)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Fixed
|
|
141
|
+
|
|
142
|
+
- **A transaction error no longer quotes a bound search text**: a failed statement whose reason
|
|
143
|
+
reports a missing full-text index ("no suitable index supporting the expression") is cut after
|
|
144
|
+
the expression's operator (`… the expression: slug @0@`), since SurrealDB 3.x quotes the bound
|
|
145
|
+
value after it. The server's text stays reachable as the error's suppressed `__context__`, as
|
|
146
|
+
for a reason that quotes the query.
|
|
147
|
+
|
|
148
|
+
### Notes
|
|
149
|
+
|
|
150
|
+
- **The index keyword follows the server line**, read from `client.version()`: `FULLTEXT` on
|
|
151
|
+
SurrealDB 3.x, `SEARCH` on 2.x and the embedded engine, each of which refuses the other's. The ORM
|
|
152
|
+
writes one field per full-text index on both lines.
|
|
153
|
+
- **Only what changed is redefined**: an unchanged index is never redefined, so start-up never
|
|
154
|
+
rebuilds. A rebuild re-reads the table: seconds per thousands of records on SurrealDB 2.x and the
|
|
155
|
+
embedded engine.
|
|
156
|
+
- **A changed analyzer rebuilds its indexes**: SurrealDB keeps the tokens of the records an index
|
|
157
|
+
holds when their analyzer changes, so a search would miss them. When `define_indexes()` or
|
|
158
|
+
`define_analyzer()` redefines an analyzer, it rebuilds every index of the database that uses it
|
|
159
|
+
(`REBUILD INDEX`) and logs one warning naming them (logger `surreal_orm_lite.search`). In a
|
|
160
|
+
transaction, `define_indexes(tx=…)` refuses an analyzer to define or redefine, and
|
|
161
|
+
`define_analyzer(tx=…)` a redefinition with indexes to rebuild, before any DDL: call
|
|
162
|
+
`define_analyzer()` without `tx=` first.
|
|
163
|
+
- **`remove_analyzer()` is safe on SurrealDB 2.x**: 2.x and the embedded engine accept removing an
|
|
164
|
+
analyzer an index uses, then refuse every write to the table; the ORM refuses it first on every
|
|
165
|
+
line, with SurrealDB 3.x's own message.
|
|
166
|
+
- **A live query's searched subquery is checked**: before a live query whose `Subquery` searches,
|
|
167
|
+
the ORM checks that each searched field has a full-text index and raises `SurrealDbError` naming
|
|
168
|
+
the field otherwise, before the live query starts.
|
|
169
|
+
- **BM25 scores depend on the line**: a term found in more than half the documents scores `0.0` on
|
|
170
|
+
SurrealDB 3.x and below zero on 2.x and the embedded engine, where a text holding such a term
|
|
171
|
+
therefore scores lower than without it. On 2.x and the embedded engine, BM25 statistics count an
|
|
172
|
+
index's records again at each rebuild or redefinition and keep counting deleted records, so the
|
|
173
|
+
scores depend on the index's history (the matches do not): a documented 2.x limitation. SurrealDB
|
|
174
|
+
3.x keeps them exact; compare scores within one query, and upgrade to 3.x where scores must not
|
|
175
|
+
depend on the index's history.
|
|
176
|
+
- **Refused before any request** (`SurrealDbError`): `search()` with `as_of()`, in either order and
|
|
177
|
+
in a subquery under it (the index holds the records' current text); with `live()` and `watch()`,
|
|
178
|
+
since SurrealDB accepts a live query whose condition holds a full-text match but never notifies
|
|
179
|
+
it — search in a `Subquery` instead, which notifies the records created or updated to match; and
|
|
180
|
+
with `changes()`. A search on a field without a full-text index raises `SurrealDbError` naming the
|
|
181
|
+
field instead of the server's error, which on SurrealDB 3.x quotes the search text, in a
|
|
182
|
+
transaction too. **Indexes on materialized views** are refused (`ValueError` when the class is
|
|
183
|
+
defined): SurrealDB 3.x does not index the records a view gains after the index is defined.
|
|
184
|
+
- **Call `define_table()` before `define_indexes()`**: on a table that does not exist yet,
|
|
185
|
+
SurrealDB's `DEFINE INDEX` creates it `TYPE ANY SCHEMALESS PERMISSIONS NONE`, which
|
|
186
|
+
`define_table()` replaces but `define_table(overwrite=False)` leaves as it is.
|
|
187
|
+
- **SurrealDB 3.1** (tested with 3.1.5 for backward compatibility): two `search()` fields AND-ed
|
|
188
|
+
can also return records that match only one of them, scored `0.0` on the other; a field without a
|
|
189
|
+
full-text index answers no record instead of an error; and a live query whose subquery searches
|
|
190
|
+
such a field starts.
|
|
191
|
+
- **Deferred**: `search()` with `as_of()` on a versioned SurrealDB 3.x server; indexes on
|
|
192
|
+
materialized views once SurrealDB indexes the records a view gains; 3.x's boolean match operators
|
|
193
|
+
(`@AND@`/`@OR@`) and `highlight_all`; `indexes=[Index(...)]` in generated models;
|
|
194
|
+
`define_index()`/`remove_index()` on the connection manager for tables without a model;
|
|
195
|
+
`Index(concurrently=True)`. Vector indexes come in v0.36.0, `hybrid_search()` in v0.37.0, and
|
|
196
|
+
analyzers and indexes in migrations in v0.38.0.
|
|
197
|
+
- Server pins unchanged: tested against SurrealDB 3.3.4 and 2.7.0 and on the embedded engine,
|
|
198
|
+
plus 3.1.5 for backward compatibility.
|
|
199
|
+
|
|
8
200
|
## [0.34.1] - 2026-10-22
|
|
9
201
|
|
|
10
202
|
Follow-ups to the review of v0.34.0's model table DDL (#296): `define_access()` and
|