Provides classes and interfaces to interact with an embedded instance of the eXist XML database. This API layer takes advantage of Java 5.0 language features to make interaction with the database easier. If you choose to use this API, you should not use any other eXist packages directly or attempt to interface with eXist through other means at the same time.

The API's style is styled after that of the java.util collection classes. Preference is given to using nouns as accessor names directly (e.g. size()) rather than prefixed with set or get (e.g. getSize()). All collection-like classes implement Iterable so that they can be used in JDK 5.0 for-each loops. Many classes offer facets—auxiliary objects that offer access to a themed subset of functionality (e.g. {@link org.exist.fluent.Folder#documents() Folder.documents()} for functions related to the documents in a folder) or that view the object in a special way (e.g. {@link org.exist.fluent.ItemList#values() ItemList.values()} that views a list of items as a list of their effective string values). Finally, some classes offer a {@link java.lang.StringBuffer StringBuffer}-style interface for chaining calls, providing a miniature domain specific language within their context (e.g. {@link org.exist.fluent.QueryService QueryService} or {@link org.exist.fluent.ElementBuilder ElementBuilder}).

Folders and documents

The entry point to this API is the {@link org.exist.fluent.Database Database} class. After you've started up the database and obtained a handle by logging in a user, you can start accessing the database's contents. The database is laid out as a hierarchy of {@link org.exist.fluent.Folder folders}, with the root folder called "/". You can obtain a reference to a folder via {@link org.exist.fluent.Database#getFolder(String) Database.getFolder()}, or create a folder via {@link org.exist.fluent.Database#createFolder(String) Database.createFolder()} (supplying an absolute path in each case, e.g. "/top/other").

Each folder also contains documents. You can retrieve them either absolutely, through {@link org.exist.fluent.Database#getDocument(String) Database.getDocument()}, or locally, through {@link org.exist.fluent.Folder.DocumentsFacet#get(String) Folder.documents().get()}. A document has either binary or {@link org.exist.fluent.XMLDocument XML} contents; only the XML documents will be indexed and participate in queries. You can add documents to the database either by loading them from external sources with {@link org.exist.fluent.Folder.DocumentsFacet#load(org.exist.fluent.Name,org.exist.fluent.Source.XML) Folder.documents().load(Name, Source.XML)} for XML documents, or {@link org.exist.fluent.Folder.DocumentsFacet#load(org.exist.fluent.Name,org.exist.fluent.Source.Blob) Folder.documents().load(Name, Source.Blob)} for binary ones. For example, to load all files in a directory into a folder you could do:

void loadDirectoryIntoFolder(File directory, Folder destination) {
  for (File file : directory.listFiles()) {
    if (!file.isFile()) continue;
    // assume that a file is XML iff its filename ends with ".xml"
    boolean xmlFile = file.getName().toLowerCase().endsWith(".xml");
    destination.documents().load(
      Name.keepOverwrite(),
      xmlFile ? Source.xml(file) : Source.blob(file)
    );
  }
}
You can also build XML files element by element directly using {@link org.exist.fluent.Folder.DocumentsFacet#build(org.exist.fluent.Name) Folder.documents().build(Name)}. This method returns a builder, which accepts a sequence of construction commands ending with {@link org.exist.fluent.ElementBuilder#commit() commit()}. For example, to build this small XML document:
<contact>
  <name kind='first'>Piotr</name>
  <name kind='family'>Kaminski</name>
</contact>
you could use the following code:
void buildContact(Folder folder) {
  folder.documents().build(Name.create("pk-record"))
    .elem("contact")
      .elem("name").attr("kind", "first").text("Piotr").end("name")
      .elem("name").attr("kind", "family").text("Kaminski").end("name")
    .end("contact")
  .commit();

Queries

Once XML documents have been built or loaded into the database, you can query them using XQuery. To run a query, you must first obtain a {@link org.exist.fluent.QueryService QueryService}, typically by calling {@link org.exist.fluent.Resource#query() query()} on any {@link org.exist.fluent.Resource Resource}: the query will be executed in the context of the resource from which it was obtained. Some typical resources are {@link org.exist.fluent.Folder folders} and {@link org.exist.fluent.Document documents}, but you can also refine your results by querying within the context of an {@link org.exist.fluent.ItemList ItemList} or {@link org.exist.fluent.Item Item} obtained from a previous query.

Once you're secured a query service, you must call a query execution method appropriate for the kind of result you are expecting. The most general method is {@link org.exist.fluent.QueryService#all(String,Object...) all()}, which simply returns all results as an {@link org.exist.fluent.ItemList ItemList} in the order specified by the query (usually document order). If you don't care about the order, you can use {@link org.exist.fluent.QueryService#unordered(String,Object...) unordered()}, which may allow the database to further optimize the query's performance. If you know your query will return precisely one item, you should use {@link org.exist.fluent.QueryService#single(String,Object...) single()}, which will return an {@link org.exist.fluent.Item Item} or fail with a {@link org.exist.fluent.DatabaseException DatabaseException} if the query returns an unexpected number of items. If you know the query will return at most one item, you should use {@link org.exist.fluent.QueryService#optional(String,Object...) optional()}, which will return an {@link org.exist.fluent.Item Item} or a special Item placeholder if the result was empty (you can check which one it is with {@link org.exist.fluent.Item#extant() extant()} if needed). There are other specialized query methods as well.

When running a query, you often need to pass parameters into the query expression. The easiest way to do this is to add any desired arguments to the query execution call; they will automatically be bound to XQuery variables of the form $_1, $_2, etc. You can also set variables using the {@link org.exist.fluent.QueryService#let(String,Object) let()} method.

Here is a sample query over documents of the form given in the previous section. Note that there are no special methods for accessing a node's attributes—queries are used for everything.

void printNames(Folder folder) {
  for (Node contact : folder.query().all("//contact").nodes()) {
    for (Node name : contact.query().all("name").nodes()) {
      System.out.print(name.query().single("@kind").value());
      System.out.print("=");
      System.out.print(name.value());
      System.out.print(" ");
    }
    System.out.println();
  }
}

Namespaces

Every object that might need to interpret XML element tags or attribute names has its own {@link org.exist.fluent.NamespaceMap namespace bindings} that map prefixes to namespace URIs. Each set of bindings inherits from the bindings of the database object it was derived from, creating a chain that goes all the way back to the original database instance. Thus, setting the desired bindings via {@link org.exist.fluent.Database#namespaceBindings Database.namespaceBindings()} will make them available throughout your code. On the other hand, any object can add on to and override the namespace bindings without affecting its ancestors to allow for more complicated situations.