Java Application Developer's Guide (PDF)

Java Application Developer's Guide — Chapter 8

« Previous chapter
Next chapter »

Optic Java API for Relational Operations

The MarkLogic Optic API is implemented in JavaScript, XQuery, REST, and Java. A general overview and the JavaScript and XQuery implementations of the Optic API is described in Optic API for Multi-Model Data Access in the Application Developer's Guide. This chapter describes the Java Client implementation of the Optic API, which is very similar in structure to the JavaScript version of the Optic API.

This chapter has the following main sections:

Overview

The Optic Java Client API provides classes to support building a plan on the client, executing the plan on the server, and processing the response on the client.

On the server, the Optic API can yield a row that satisfies any of several common use cases:

  • A traditional flat list of atomic values with names and XML Schema atomic datatypes.
  • A dynamic JSON or XML document with substructure and leaf atomic values or mixed text.
  • An envelope with out-of-band metadata properties and relations for a list of documents.

The second use case can take advantage of document joins or constructor expressions. The third use case can take advantage of document joins.

On the client, the Optic Java Client API can consume a set of rows in one of the following ways:

  • As a single CSV, JSON, or XML payload with all of the rows in the set.
  • By iterating over each row with a Java map key-value interface, a pre-defined Plain Old Java Object (POJO) tree structure, or a JSON or XML document structure.

A structured value in a format alien to the response format is encoded as a string. In particular, when getting a CSV payload, a JSON or XML column value is encoded as a string. Similarly, when getting a JSON payload or row, an XML value is encoded as a string (and vice versa).

Getting Started

The Optic Java Client communicates with a REST App Server on MarkLogic.

  1. Download the MarkLogic Java Client API to your client system and configure, as described in Getting Started.
  2. You can use the preconfigured REST App Server at port 8000, as described in Choose a REST API Instance, however it is generally better that you create your own REST App Server. You can use the POST /v1/rest-apis call to quickly and conveniently create a REST App Server. For example, to create a REST App Server, named Optic, on a server named MLserver, you can simply enter:
    curl -X POST --anyauth -u admin:admin -H "Content-Type:application/json" \
    -d '{
       "rest-api": {"name": "Optic"} 
    }' \
    http://MLserver:8002/v1/rest-apis

    The Optic App Server will be assigned an unused port number and all of the required forests and databases will be created for it. The Optic database created for you will use the default Schemas database. However, you should create a unique schemas database and assign it to the Optic database.

To run the examples described in this chapter, do the following:

  1. Follow the steps in Load the Data in the SQL Data Modeling Guide to load the sample documents into the database. Use the database associated with your REST API instance (Optic) rather than the one used in the procedure.
  2. Follow the stops in Create Template Views in the SQL Data Modeling Guide to create views and insert the template view documents into the schema database assigned to the Optic database.

Java Packages

The following packages implement the Optic features in the Java API:

Package Description
com.marklogic.client.expression Provides classes for building Optic plan pipelines and expressions for execution on the REST server.
com.marklogic.client.row Provides classes for sending plan requests to and processing row responses from the REST server.
com.marklogic.client.type Provides interfaces that specify the type of an expression or value passed to a PlanBuilder method or returned from a RowRecord method.

See the MarkLogic Java API JavaDoc reference for details.

Structure of the Java Optic API

The Java Optic API is similar to the server-side JavaScript and XQuery implementations of the Optic API described in Optic API for Multi-Model Data Access in the Application Developer's Guide. This chapter describes the Java Client implementation of the Optic API, which is similar in structure.

The Optic API for Multi-Model Data Access chapter in the Application Developer's Guide contains the following main topics of interest to Java Optic developers:

Values and Expressions

The *Val interfaces represent client-side values typed with server data types. For example, the PlanBuilder.xs.decimal method constructs a client value with an xs.decimal data type.

The *Expr interfaces represent server expressions typed with server data types. For example, the PlanBuilder.fn.formatNumber method constructs a server expression to format the result of a numeric expression as a string expression.

Server expressions executed to produce the boolean expression for a where operation or the expression assigned to a column by the PlanBuilder.as function can take columns as arguments. The function call calculates the result of the expression for each row using the values of the columns in the row. For example, if the first argument to PlanBuilder.fn.formatNumber is a column, the formatted string will be produced for each row with the value of the column. The column must have a value in each row with the data type required in the expression.

The API provides some overloads for typical literal arguments of expression functions as a convenience.

The com.marklogic.client.type package has the marker interfaces for the server data types.

Items and Sequences

Some functions can take multiple values or expressions for a parameter. Such parameters have a sequence data type. A sequence data type can take either a single item of the data type or a sequence of the data type. The API provides constructor functions that take a varargs of items of the appropriate data type and return the sequence.

For instance, PlanBuilder.pattern takes a sequence for the subject, predicate, and object parameters.

The call can pass either one PlanTriplePosition instance (an XsAnyAtomicTypeVal, PlanColumn, or PlanParamExpr object) as the subject or use PlanBuilder.subject to construct a sequence of such objects to pass as the subject.

Atomic Values and Nodes in RowRecord

RowRecord provides the getKind metadata method for discovering the ColumnKind of a column in the current row (ATOMIC_VALUE, CONTENT, or NULL).

For an ATOMIC_VALUE column, the getDatatype metadata method reports the atomic data type.

You can call a get* getter to cast the value to the appropriate primitive or to a *Val type.

For a CONTENT column, the getContentFormat and getContentMimetype metadata methods report the format and mime type. The caller can pass the appropriate handle to the getContent getter to read the JSON, XML, binary, or text content (consistent with the Java API elsewhere).

Examples

The following two examples are based on documents and template views described in the Creating Template Views chapter in the SQL Data Modeling Guide.

List all of the employees in order of ID number.

package Optic;

import com.marklogic.client.DatabaseClient;
import com.marklogic.client.DatabaseClientFactory;
import com.marklogic.client.io.StringHandle;
import com.marklogic.client.expression.PlanBuilder;
import com.marklogic.client.expression.PlanBuilder.ModifyPlan;
import com.marklogic.client.row.RowManager;

public class optic4 {
   public static void main(String[] args) {
      DatabaseClient db = DatabaseClientFactory.newClient(
         "localhost", 8000, 
         new DatabaseClientFactory.DigestAuthContext("admin", "admin")
      );

      RowManager rowMgr = db.newRowManager();
      PlanBuilder p = rowMgr.newPlanBuilder();

      ModifyPlan plan = p.fromView("main", "employees")
         .select("EmployeeID", "FirstName", "LastName")
         .orderBy("EmployeeID")
         .offsetLimit(0, 25);

      System.out.println(
         rowMgr.resultDoc(plan, 
         new StringHandle().withMimetype("text/csv")).get()
         );

      return;
   }
}

Return the ID and full name for the employee with an EmployeeID of 3.

package Optic;

import com.marklogic.client.DatabaseClient;
import com.marklogic.client.DatabaseClientFactory;
import com.marklogic.client.io.StringHandle;
import com.marklogic.client.expression.PlanBuilder;
import com.marklogic.client.expression.PlanBuilder.ModifyPlan;
import com.marklogic.client.row.RowManager;
import com.marklogic.client.type.XsIntVal;

public class optic {

   public static void main(String[] args) {
      DatabaseClient db = DatabaseClientFactory.newClient(
         "MLserver", 8000, 
         new DatabaseClientFactory.DigestAuthContext("admin", "admin")
      );

      RowManager rowMgr = db.newRowManager();
          PlanBuilder p = rowMgr.newPlanBuilder();
          XsIntVal EmployeeID = p.xs.intVal(3);

      ModifyPlan plan = p.fromView("main", "employees")
          .where(p.eq(p.col("EmployeeID"), EmployeeID))
          .select("EmployeeID", "FirstName", "LastName")
          .orderBy("EmployeeID");

      System.out.println(
          rowMgr.resultDoc(plan, 
          new StringHandle().withMimetype("text/csv")).get()
          );

      return;
      }
}

The following example returns a list of the people who were born in Brooklyn in the form of a table with two columns, person and name. This is executed against the example dataset described in Loading Triples in the Semantics Developer's Guide. This example is the Java equivalent of the last JavaScript example described in fromView Examples in the Optic API for Multi-Model Data Access chapter in the Application Developer's Guide.

package Optic;

import com.marklogic.client.DatabaseClient;
import com.marklogic.client.DatabaseClientFactory;
import com.marklogic.client.DatabaseClientFactory.DigestAuthContext;
import com.marklogic.client.io.StringHandle;
import com.marklogic.client.expression.PlanBuilder;
import com.marklogic.client.row.RowManager;
import com.marklogic.client.type.PlanColumn;

public class optic2 {

    public static void main(String[] args) {
      DatabaseClient db = DatabaseClientFactory.newClient(
          "localhost", 8000, 
          new DigestAuthContext("admin", "admin")
      );
      RowManager rowMgr = db.newRowManager();
      PlanBuilder p = rowMgr.newPlanBuilder();

      PlanBuilder.Prefixer foaf =
          p.prefixer("http://xmlns.com/foaf/0.1");
      PlanBuilder.Prefixer onto =
          p.prefixer("http://dbpedia.org/ontology");
      PlanBuilder.Prefixer resource =
          p.prefixer("http://dbpedia.org/resource");

      PlanColumn person = p.col("person");

      PlanBuilder.QualifiedPlan plan = p.fromTriples(
        p.pattern(person, onto.iri("birthPlace"),
                  resource.iri("Brooklyn")),
        p.pattern(person, foaf.iri("name"), p.col("name"))
      );

      System.out.println(
          rowMgr.resultDoc(plan, 
          new StringHandle().withMimetype("text/csv")).get()
      );

      return;
    }
}
« Previous chapter
Next chapter »
Powered by MarkLogic Server | Terms of Use | Privacy Policy