PhoenixmlDb
Database Extensions
Custom XQuery functions, phx:metadata(), and extension development
#Database Extensions
PhoenixmlDb extends XQuery with database-specific functions in the phx: namespace. These functions provide access to document metadata and database features directly from XQuery expressions.
#The phx: Namespace
The engine's extension functions live in https://schemas.phoenixml.dev/2026/db, and the engine
binds the prefix phx to it on every query path. You do not declare it:
phx:metadata($node, 'dbxml:name')
A container's ContainerOptions.DefaultNamespaces bindings are also in scope, and a query's own
declare namespace overrides either.
dbxmlis not bound, anddbxml:means something different here. The engine binds onlyphxfor functions.dbxmlis reserved for the metadata namespace (https://schemas.phoenixml.dev/2026/meta) in Core's namespace registry.The
dbxml:you see on the metadata keys below is a literal key prefix matched by the metadata provider — not a namespace prefix. It is a plain part of the key string, so it works whether or notdbxmlis bound as a prefix. Two different things share the spelling; only one of them is a namespace.
#phx:metadata($node, $key)
Retrieves a specific metadata value for the document containing the given node.
#Signature
phx:metadata($node as node(), $key as xs:string) as item()?
#Parameters
|
Parameter |
Type |
Description |
|---|---|---|
|
|
|
Any node belonging to the target document |
|
|
|
The metadata key to retrieve |
#Return Type
item()? — The metadata value, or empty sequence if the key does not exist.
#System Metadata Keys
PhoenixmlDb provides built-in system metadata keys carrying a literal dbxml: prefix. These are
key strings, not namespace-qualified names — pass them as ordinary strings:
|
Key |
Type |
Description |
|---|---|---|
|
|
|
Document name within its container |
|
|
|
MIME content type ( |
|
|
|
ISO 8601 creation timestamp |
|
|
|
ISO 8601 last-modified timestamp |
|
|
|
Document size in bytes |
|
|
|
Number of nodes in the document |
#Examples
(: Get the document name :)
phx:metadata(., "dbxml:name")
(: Get a user-defined metadata value :)
phx:metadata(., "author")
(: Filter documents by metadata :)
for $doc in collection('products')
where phx:metadata($doc, "author") = "admin"
return $doc/product/name/text()
(: Use system metadata in results :)
for $doc in collection('orders')
return <info>
<name>{phx:metadata($doc, "dbxml:name")}</name>
<size>{phx:metadata($doc, "dbxml:size")}</size>
<author>{phx:metadata($doc, "author")}</author>
</info>
#phx:metadata($node)
Retrieves all user metadata for the document containing the given node as an XQuery map.
#Signature
phx:metadata($node as node()) as map(xs:string, item()?)
#Parameters
|
Parameter |
Type |
Description |
|---|---|---|
|
|
|
Any node belonging to the target document |
#Return Type
map(xs:string, item()?) — A map of all user-defined metadata key-value pairs.
#Examples
(: Get all metadata as a map :)
let $meta := phx:metadata(.)
return map:keys($meta)
(: Iterate over metadata entries :)
let $meta := phx:metadata(.)
for $key in map:keys($meta)
return concat($key, " = ", $meta($key))
#Practical Examples
#Filtering by Metadata
(: Find documents modified after a specific date :)
for $doc in collection('reports')
let $modified := phx:metadata($doc, "dbxml:modified")
where $modified > "2024-01-01T00:00:00Z"
return phx:metadata($doc, "dbxml:name")
#Combining Content and Metadata Queries
(: Find large orders created by a specific user :)
for $doc in collection('orders')
let $order := $doc/order
where $order/total > 1000
and phx:metadata($doc, "author") = "system"
order by xs:decimal($order/total) descending
return <result>
<document>{phx:metadata($doc, "dbxml:name")}</document>
<total>{$order/total/text()}</total>
<created>{phx:metadata($doc, "dbxml:created")}</created>
</result>
#Metadata in FLWOR Expressions
(: Group documents by author :)
for $doc in collection('articles')
let $author := phx:metadata($doc, "author")
group by $author
return <author name="{$author}">
<count>{count($doc)}</count>
<documents>{
for $d in $doc
return <doc>{phx:metadata($d, "dbxml:name")}</doc>
}</documents>
</author>
#Custom C# Function Extensions
You can register custom XQuery functions from C# by extending the XQueryFunction base class.
#Writing a Custom Function
using PhoenixmlDb.Core;
using PhoenixmlDb.Query.Ast;
public sealed class MyCustomFunction : XQueryFunction
{
public override QName Name => new(
FunctionNamespaces.Local, "my-function");
public override XdmSequenceType ReturnType =>
XdmSequenceType.String;
public override IReadOnlyList<FunctionParameterDef> Parameters =>
[
new() {
Name = new QName(NamespaceId.None, "input"),
Type = XdmSequenceType.String
}
];
public override ValueTask<object?> InvokeAsync(
IReadOnlyList<object?> arguments,
ExecutionContext context)
{
var input = arguments[0]?.ToString() ?? "";
return ValueTask.FromResult<object?>(
input.ToUpperInvariant());
}
}
#Registering the Function
var library = FunctionLibrary.Standard;
library.Register(new MyCustomFunction());
#Using from XQuery
declare namespace local = "http://www.w3.org/2005/xquery-local-functions";
local:my-function("hello") (: "HELLO" :)
#Comparison with Berkeley DB XML
PhoenixmlDb's phx:metadata() function is inspired by Berkeley DB XML but with some differences:
|
Feature |
Berkeley DB XML |
PhoenixmlDb |
|---|---|---|
|
Namespace URI |
|
|
|
Function prefix |
|
|
|
Metadata access |
|
|
|
All metadata |
Not available |
|
|
System keys |
|
|
|
Custom functions |
Java-based |
C#-based with |
The system-key spelling is the one piece of Berkeley DB XML's convention that carries over
verbatim. The function prefix does not: dbxml is reserved for the metadata namespace here, so
functions are phx:.