Store Functions
Updated: All opened stores are now kept in main memory. As a result, a newly opened store will not overwrite the default store anymore. The functions have been enriched with a parameter to supply the name of a store.
This module provides functions to organize values in a persistent main-memory key-value store.
Stores are useful if data (a system configuration, maps serving as indexes) needs to be repeatedly accessed. A store is persistent: Contents will be written to disk at shutdown time (unless WRITESTORE is disabled), and the serialized store will be retrieved from disk as soon as the store is used for the first time. The default store will be stored in a binary store.basex file in the database directory.
In addition, custom stores can be read and written, and can be addressed via an additional $name parameter. Custom stores have filenames with the pattern store-NAME.basex.
The Cache provides similar functionality, but its entries have a limited lifetime and are transient, i.e., will never be persisted.
Functions of this module are nondeterministic and side-effecting: Updates will immediately be visible, and a repeated call of the same function may yield different results if the contents of the store have changed.
Single operations are atomic, but a sequence of them is not. If a store is accessed by several clients at the same time, and if a value depends on entries that were read before, use store:update to turn the reads and writes into a single atomic operation.
All functions and errors are in the http://basex.org/modules/store namespace, to which the store prefix is statically bound.
| Signature | store:get(
$key as xs:string,
$name as xs:string? := ''
) as item()* |
|---|
| Summary | Retrieves an entry with the given $key from the default store, or from a custom store if $name is specified. If the addressed entry does not exist, an empty sequence is returned. |
|---|
| Signature | store:put(
$key as xs:string,
$value as item()*,
$name as xs:string? := ''
) as empty-sequence() |
|---|
| Summary | Stores an entry with the given $key and $value in the default store, or in a custom store if $name is specified:
- If the addressed store does not exist in main memory, it will either be opened from disk or created automatically.
- If the value is an empty sequence, its entry is removed.
- Before a value is stored, it is compactified (see
prof:shrink for more information). - Additionally, if a value refers to an opened database or is a lazy item, its contents are materialized in main memory.
- Values with function items are rejected.
|
|---|
| Signature | store:get-or-put(
$key as xs:string,
$put as fn() as item()*,
$name as xs:string? := ''
) as item()* |
|---|
| Summary | Retrieves an entry with the given $key from the default store, or from a custom store if $name is specified. The $put function will be invoked if the entry does not exist, and its result will be stored and returned instead. |
|---|
| Signature | store:remove(
$key as xs:string,
$name as xs:string? := ''
) as empty-sequence() |
|---|
| Summary | Removes an entry with the given $key from the default store, or from a custom store if $name is specified. No error will be raised if an addressed entry does not exist. |
|---|
Added: New function.
| Signature | store:update(
$update as fn(map(xs:string, item()*)) as map(xs:string, item()*),
$name as xs:string? := ''
) as xs:boolean |
|---|
| Summary | Atomically updates the default store, or a custom store if $name is specified. The $update function is called with the current entries of the store, and the returned map will replace them. Returns true if the entries have changed:
- No other store operation can be performed while the function is evaluated. This makes it possible to combine reads and writes that span multiple keys, such as a check that is followed by an update, into a single atomic operation. For the same reason, the function must not wait for parallel queries that access a store.
- A function that returns its argument unchanged leaves the store untouched and yields
false. This can be used to find out whether an update was applied. - If an error is raised in the function, the store remains unchanged.
- The function returns the new entries; it does not write them. It may read any store and update other stores, but a modification of the store that is being updated will be rejected.
- Values are compactified, and entries with empty values are dropped, as with
store:put.
|
|---|
| Errors | name | The specified name is invalid. | update | A store was modified while it was updated. |
|
|---|
| Examples | store:update(fn { map:put(., 'hits', (?hits otherwise 0) + 1) }) Increments a counter. As the value is read and written in a single operation, no increment will be lost if the query is run concurrently.
store:update(fn($entries) {
let $now := current-dateTime()
return map:filter($entries, fn($key, $value) { $value?expires > $now })
}, 'sessions') Discards all expired entries of the sessions store. No entry can be added by another query while the store is scanned. |
|---|
| Signature | store:keys(
$name as xs:string? := ''
) as xs:string* |
|---|
| Summary | Lists the names of all keys of the default store, or of a custom store if $name is specified. |
|---|
| Signature | store:list() as xs:string* |
|---|
| Summary | Lists the names of all custom stores. |
|---|
| Signature | store:read(
$name as xs:string? := ''
) as empty-sequence() |
|---|
| Summary | Retrieves the default store from disk, or a custom store if $name is specified. This function can be called if the store file has been changed on disk by another process while a BaseX instance is running. |
|---|
| Errors | io | The store could not be read or written. | name | The specified name is invalid. |
|
|---|
| Signature | store:write(
$name as xs:string? := ''
) as empty-sequence() |
|---|
| Summary | Writes the default store to disk, or to a custom store file if $name is specified. If a store is empty, its store file will be deleted. This function can be called to enforce the write operation before shutdown time. |
|---|
| Errors | io | The store could not be read or written. | name | The specified name is invalid. |
|
|---|
| Signature | store:delete(
$name as xs:string? := ''
) as empty-sequence() |
|---|
| Summary | Deletes the default store, or a custom store if $name is specified, both from main memory and from disk. |
|---|
| Errors | name | The specified name is invalid. |
|
|---|
Added: New function.
| Signature | store:close(
$name as xs:string? := ''
) as empty-sequence() |
|---|
| Summary | Closes a store and frees memory. Changes will be written to disk. |
|---|
| Signature | store:clear() as empty-sequence() |
|---|
| Summary | Clears the default store and removes all other stores both from main memory and from disk. |
|---|
Use Case 1: Create/update a system configuration in a running BaseX server instance
(: store an integer :)
store:put('version', 1),
(: retrieve existing or new value, store an element :)
let $license := store:get-or-put('license', fn() { 'free' })
let $type := if ($license = 'free') then 'Free' else 'Professional'
return store:put('info', <info>{ $type } License</info>),
(: store a map :)
store:put('data', { 'year': 2022 }),
(: serialize configuration to disk :)
store:write()
The configuration can be requested by further operations, e.g. a client request:
store:get('version')
The store will still be available if BaseX is restarted until it is cleared.
Use Case 2: Create index for fast lookup operations in the GUI
let $map := map:merge(
for $country in db:get('factbook')//country
for $religion in $country//religions
group by $religion
return map:entry($religion, data($country/@name))
)
return store:put('religions', $map)
A subsequent query can be used to access its contents:
store:get('religions')?Buddhism
Note that the store will eventually be written to disk unless it is invalidated before closing the GUI.
Use Case 3: Grant a lock if no conflicting lock exists
A check that is followed by a write is only reliable if no other request can interfere in between. store:update evaluates both steps in a single atomic operation. As the returned map is left unchanged if a conflicting lock is found, the result of the function indicates whether the lock was granted:
let $granted := store:update(fn($locks) {
if (map:keys($locks)[starts-with($path, .)]) {
$locks
} else {
map:put($locks, $path, $owner)
}
}, 'locks')
return if ($granted) { 'locked ' || $path } else { 'denied' }
| Code | Description |
|---|
io | The store could not be read or written. |
name | The specified name is invalid. |
update | A store was modified while it was updated. |
Version 13Version 11- Added: New
WRITESTORE option. - Updated: Values are compactified before being stored.
Version 10
⚡Generated with XQuery