@@ -91,7 +91,7 @@ extended-JSON **strings**.
9191products = norbix.hub.database.find_records(" products" , filter = ' {"status": "active"}' , pageSize = 20 )
9292one = norbix.hub.database.find_one_record(" products" , " rec_1" )
9393created = norbix.hub.database.insert_record(" products" , document = ' {"title": "Lamp", "price": 12}' )
94- norbix.hub.database.update_one_record(" products" , " rec_1" , update = ' {"$set": {" price": 10} }' )
94+ norbix.hub.database.update_one_record(" products" , " rec_1" , update = ' {"price": 10}' )
9595norbix.hub.database.delete_record(" products" , " rec_1" )
9696total = norbix.hub.database.count_records(" products" , filter = ' {"status": "active"}' )
9797indexes = norbix.hub.database.get_collection_indexes(" products" )
@@ -111,6 +111,158 @@ update no longer matches.
111111test_schemas = Norbix(project_id = " ..." , api_key = " ..." , env = " TEST" ).hub.database.get_database_schemas()
112112```
113113
114+ ## Changing many records
115+
116+ ` update_many_records ` and ` delete_many_records ` change every record that
117+ matches ` filter ` . An empty filter (` '{}' ` ) matches ** every** record of the
118+ collection, so the gateway refuses it with ` CM-ERRORS-DATABASE-037 ` unless
119+ you also send ` allRecords=True ` . For ` update_many_records ` a missing filter
120+ counts as ` '{}' ` .
121+
122+ ``` python
123+ # Only the matching records:
124+ norbix.hub.database.update_many_records(" products" , filter = ' {"status": "draft"}' , update = ' {"status": "live"}' )
125+ # Every record — the flag says you mean it:
126+ norbix.hub.database.delete_many_records(" products" , filter = " {} " , allRecords = True )
127+ ```
128+
129+ Pass the flag with its wire name, ` allRecords ` (the SDK sends keyword
130+ arguments as given; on ` update_many_records ` it goes in the JSON body, on
131+ ` delete_many_records ` in the query). The marketplace built-ins ` db.update ` and
132+ ` db.delete ` take the same flag as the ` allRecords ` argument.
133+
134+ Other rules for record writes:
135+
136+ - ** Who may call the bulk methods.** A caller with only own-record rights
137+ (` createAsUser ` , ` updateOwn ` , ` deleteOwn ` ) may now call ` insert_many_records ` ,
138+ ` update_many_records ` and ` delete_many_records ` . The call changes only the
139+ caller's own records (before, it was refused with 403).
140+ - ** No ` $ ` operators in an update.** ` update_one_record ` / ` update_many_records `
141+ take the new values as a plain document. An update with ` $inc ` , ` $set ` and
142+ the like is refused with ` CM-ERRORS-DATABASE-035 ` (before, MongoDB failed
143+ at run time).
144+ - ** A broken record document** on insert one, insert many or replace is
145+ refused with ` CM-ERRORS-DATABASE-036 ` "Invalid record document" (before,
146+ ` 005 ` "Invalid filter document"). For insert many,
147+ ` err.errors[0].context["Index"] ` is the position of the bad document.
148+ - ** Soft-deleted records are not found.** Update, replace and change owner
149+ no longer match a soft-deleted record: it answers "not found", and a bulk
150+ update skips it.
151+ - ** Change owner** (` change_record_responsibility ` ) refuses a new owner who is
152+ not a user of the project in the request environment, with
153+ ` CM-ERRORS-MEMBERSHIP-USERS-012 ` .
154+
155+ ## Aggregates
156+
157+ - ` get_database_aggregate(id) ` returns ` joinedCollections ` : the schemas the
158+ pipeline joins (` $lookup ` / ` $graphLookup ` / ` $unionWith ` ), next to the
159+ start ` schemaId ` . The aggregate list rows do not carry it.
160+ - ` save_database_aggregate(...) ` refuses ` $out ` , ` $merge ` , ` $where ` ,
161+ ` $function ` and ` $accumulator ` (` CM-ERRORS-DATABASE-031 ` ) and a join it
162+ cannot follow (` 034 ` ); a pipeline that does not parse is refused with
163+ ` CM-ERRORS-AGGREGATES-002 ` .
164+ - A pipeline (run, test or saved-and-executed) that joins a collection the
165+ caller cannot read is refused with ` 033 ` ; a join to a name that is not a
166+ schema in the request environment with ` 032 ` .
167+ - ` test_database_aggregate(...) ` needs ` database:create ` ** or**
168+ ` database:update ` on ` database:aggregate:{schemaId} ` , plus read. A caller
169+ with read rights only gets 403.
170+
171+ ## Schemas: rename and delete
172+
173+ - ` rename_database_schema(id, title="...") ` sends only the new title. The old
174+ ` renameUniqueName ` switch is gone: a name that another schema in the same
175+ environment already uses is always refused with ` CM-ERRORS-SCHEMA-002 `
176+ ("Two schemas cannot share one collection"); the error context carries
177+ ` SchemaName ` . The schema name is the MongoDB collection name, so two schemas
178+ can never share it.
179+ - ` delete_database_schema(id) ` is also refused while a saved aggregate starts
180+ from the schema or joins it: ` CM-ERRORS-SCHEMA-018 ` . The message names the
181+ aggregates; ` err.errors[0].context ` has ` BlockerAggregateIds ` and
182+ ` BlockerAggregateNames ` . (A schema that a trigger uses is refused with
183+ ` CM-ERRORS-SCHEMA-017 ` , ` BlockerTriggerIds ` .)
184+ - ` save_database_schema(viewId=...) ` with an id that is not a schema of the
185+ request environment answers "schema not found".
186+ - ` update_database_schema_list_settings(...) ` refusals all use
187+ ` CM-ERRORS-SCHEMA-034 ` ; the context's ` Rule ` says which rule failed.
188+ - ` apply_database_schema_bundle(...) ` : a refused step keeps its own error code
189+ and context.
190+
191+ ## Database integrations per environment
192+
193+ ` get_database_integrations() ` lists only the integrations of the client's
194+ environment (` PROD ` when no ` env ` is set); each row has ` env ` . A saved
195+ integration has ` isSystemOwned ` (read only). Saving into an environment the
196+ project does not have is refused. An integration whose secret is empty in the
197+ request environment answers ` CM-ERRORS-DATABASE-038 ` .
198+
199+ ## Schema triggers per environment
200+
201+ Schema triggers are kept per environment. Every schema-trigger call works on
202+ the client's environment (the ` norbix-env ` header; ` PROD ` when none is set):
203+
204+ - ` get_schema_triggers() ` lists only that environment's triggers; each row
205+ has ` env ` .
206+ - ` get_schema_trigger(id) ` returns ` env ` , and ` schemaId ` is now the owning
207+ schema's id (` sch_… ` ). Before, it wrongly held the trigger's own id
208+ (` trg_… ` ).
209+ - ` enable_schema_trigger ` , ` disable_schema_trigger ` and ` delete_schema_trigger `
210+ act on the copy in that environment. No copy there answers
211+ ` CM-ERRORS-TRIGGERS-002 ` (not found).
212+ - ` save_schema_trigger(id=..., schemaId=...) ` with the id of a trigger that
213+ belongs to another schema answers ` CM-ERRORS-TRIGGERS-002 ` .
214+
215+ ``` python
216+ test = Norbix(project_id = " ..." , api_key = " ..." , env = " TEST" )
217+ test.hub.database.disable_schema_trigger(" trg_1" ) # the TEST copy only
218+ ```
219+
220+ ## Taxonomies
221+
222+ - ` get_database_taxonomies() ` rows have ` dependencyRefs ` in place of the old
223+ ` dependencyNames ` : one ` {"id": ..., "name": ...} ` pair per entry of
224+ ` dependencies ` , in the same order. A dependency that no longer exists keeps
225+ its place with ` "name": None ` .
226+
227+ ``` python
228+ for row in norbix.hub.database.get_database_taxonomies()[" list" ][" items" ]:
229+ names = [ref[" name" ] or f " (deleted { ref[' id' ]} ) " for ref in row.get(" dependencyRefs" ) or []]
230+ ```
231+
232+ - ` save_database_taxonomy(viewId=...) ` with the id of an existing taxonomy is
233+ an update and needs ` database:update ` on ` database:taxonomy:{viewId} ` .
234+ Without ` viewId ` (or with an unknown one) it is a create and needs
235+ ` database:create ` . An update replaces the whole taxonomy: send every field
236+ you want to keep.
237+ - Term reads by taxonomy name (term tree, merged tree) check
238+ ` database:read ` on ` database:term:{taxonomy id} ` ; the merged tree checks it
239+ on every nested taxonomy too.
240+ - ` get_database_taxonomy_tree(includeTerms=True) ` now fails when reading the
241+ terms fails. Before, it returned the taxonomies without terms.
242+ - Errors on term reads: ` CM-ERRORS-TAXONOMIES-010 ` the taxonomy name is
243+ unknown (the merged tree used to answer ` -003 ` ); ` -011 ` the term tree has
244+ more than 5000 terms (whole taxonomy, merged tree, ` includeTerms ` ); ` -005 `
245+ a taxonomy name longer than 40 characters.
246+
247+ ## Collection imports
248+
249+ Import CSV rows into a collection: ask for an upload URL, upload the file,
250+ analyze it, then create the import and follow it.
251+
252+ ``` python
253+ target = norbix.hub.database.request_import_upload_url(fileName = " products.csv" )[" result" ]
254+ httpx.put(target[" url" ], content = csv_bytes, headers = {" Content-Type" : target[" contentType" ]})
255+ preview = norbix.hub.database.analyze_import_file(file = target[" file" ], hasHeader = True )
256+ created = norbix.hub.database.create_collection_import(file = target[" file" ], schemaId = " sch_1" , hasHeader = True )
257+ status = norbix.hub.database.get_collection_import(created[" id" ])
258+ imports = norbix.hub.database.get_collection_imports()
259+ norbix.hub.database.delete_collection_import(created[" id" ])
260+ ```
261+
262+ The request fields are listed under ` RequestImportUploadUrlRequest ` ,
263+ ` AnalyzeImportFileRequest ` and ` CreateCollectionImport ` in
264+ ` references/hub_dtos.py ` .
265+
114266## Trees, list settings, embed and bundles
115267
116268- ` get_database_taxonomy_tree() ` — the taxonomy structure as a tree.
0 commit comments