Main Page » XQuery » Functions » Store Functions

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.

Conventions

All functions and errors are in the http://basex.org/modules/store namespace, to which the store prefix is statically bound.

Key-value Operations

store:get

Signature
store:get(
  $key   as xs:string,
  $name  as xs:string?  := ''
) as item()*
SummaryRetrieves 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.

store:put

Signature
store:put(
  $key    as xs:string,
  $value  as item()*,
  $name   as xs:string?  := ''
) as empty-sequence()
SummaryStores 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.

store:get-or-put

Signature
store:get-or-put(
  $key   as xs:string,
  $put   as fn() as item()*,
  $name  as xs:string?  := ''
) as item()*
SummaryRetrieves 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.

store:remove

Signature
store:remove(
  $key   as xs:string,
  $name  as xs:string?  := ''
) as empty-sequence()
SummaryRemoves 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.

store:update

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
SummaryAtomically 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
nameThe specified name is invalid.
updateA 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.

store:keys

Signature
store:keys(
  $name  as xs:string?  := ''
) as xs:string*
SummaryLists the names of all keys of the default store, or of a custom store if $name is specified.

Organize Stores

store:list

Signature
store:list() as xs:string*
SummaryLists the names of all custom stores.

store:read

Signature
store:read(
  $name  as xs:string?  := ''
) as empty-sequence()
SummaryRetrieves 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
ioThe store could not be read or written.
nameThe specified name is invalid.

store:write

Signature
store:write(
  $name  as xs:string?  := ''
) as empty-sequence()
SummaryWrites 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
ioThe store could not be read or written.
nameThe specified name is invalid.

store:delete

Signature
store:delete(
  $name  as xs:string?  := ''
) as empty-sequence()
SummaryDeletes the default store, or a custom store if $name is specified, both from main memory and from disk.
Errors
nameThe specified name is invalid.

store:close

Added: New function.

Signature
store:close(
  $name  as xs:string?  := ''
) as empty-sequence()
SummaryCloses a store and frees memory. Changes will be written to disk.

store:clear

Signature
store:clear() as empty-sequence()
SummaryClears the default store and removes all other stores both from main memory and from disk.

Examples

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' }

Errors

CodeDescription
ioThe store could not be read or written.
nameThe specified name is invalid.
updateA store was modified while it was updated.

Changelog

Version 13Version 11
  • Added: New WRITESTORE option.
  • Updated: Values are compactified before being stored.
Version 10
  • Added: New module added.

⚡Generated with XQuery