Loading TOC...
Java Application Developer's Guide (PDF)

Java Application Developer's Guide — Chapter 8

Working With Semantic Data

This chapter discusses the following topics related to using the Java Client API to load semantic triples, manage semantics graphs, and query semantic data:

Introduction

This chapter focuses on details specific to using the Java Client API for semantic operations. For more details and general concepts, see the Semantics Developer's Guide.

The graph management capabilities of the Java Client API enable you to manipulate managed triples stored in MarkLogic. For example, you can create, modify, or delete a graph using a GraphManager. For details, see Creating and Managing Graphs.

You can insert unmanaged triples into MarkLogic using standard document operations. Use the DocumentManager interfaces to insert XML or JSON documents with triples embedded in them. Unmanaged triples are indexed and searchable, just like managed triples, but you use typical XML and JSON document permissions and interfaces to control them. Unmanaged triples enable you to store semantic data alongside the content to which it applies. Example: Loading, Managing, and Querying Triples illustrates the use of an unmanaged triple.

Triples can also be made available for queries through the use of MarkLogic features such as the following. See the listed topics for details.

You can use the Java Client API to query all types of semantic data using the SPARQLQueryManager interface. You can evaluate both SPARQL and SPARQL Update queries. For more details, see Querying Semantic Triples With SPARQL and Using SPARQL Update to Manage Graphs and Graph Data.

Overview of Common Semantic Tasks

You can use the SPARQL, semantic query, and semantic graph interfaces of MarkLogic

The following table lists some common tasks related to Semantics, along with the interfaces best suited for completing the task using the Java Client API. For a complete list of interfaces, see Java Client API Documentation. All of the following interfaces are in the package com.marklogic.client.semantics.

If you want to Then use
Load semantic triples into a named graph or the default graph without using SPARQL Update.

GraphManager.write or GraphManager.writeAs

For details, see Creating or Overwriting a Graph.

Manage graphs or graph data with SPARQL Update.

SPARQLQueryManager.executeUpdate

For details, see Using SPARQL Update to Manage Graphs and Graph Data.

Read a semantic graph from the database.

GraphManager.read or GraphManager.readAs

For details, see Reading Triples from a Graph.

Query semantic data with SPARQL

SPARQLQueryManager.executeAsk

SPARQLQueryManager.executeConstruct

SPARQLQueryManager.executeDescribe

SPARQLQueryManager.executeSelect

For details, see Querying Semantic Triples With SPARQL.

Manage graph permissions.

GraphManager.writePermissions

GraphManager.mergePermissions

GraphManager.deletePermissions

For details, see Managing Permissions.

Creating and Managing Graphs

Use the GraphManager interface to perform graph management tasks such as creating, reading, updating, and deleting graphs. This section contains the following topics related to graph management tasks:

GraphManager Interface Summary

The following table summarizes key GraphManager methods. For a complete list of methods, see the Java Client API Documentation.

GraphManager Method Description

write

writeAs

Create or overwrite a graph. If the graph already exists, the effect is the same as removing the graph and then recreating it from the input data. For details, see Creating or Overwriting a Graph.

read

readAs

Retrieve triples from a specific graph. For details, see Reading Triples from a Graph.

replaceGraphs

replaceGraphsAs

Remove triples from all graphs, and then insert the quads in the input data set. Unmanaged triples are not affected. The effect is the same as first calling GraphManager.deleteGraphs, and then inserting the quads. For details, see Replacing Quad Data in Graphs.

merge

mergeAs

Add triples to a named graph or the default graph. If the graph does not exist, it is created. For more details, see Adding Triples to an Existing Graph.

mergeGraphs

mergeGraphsAs

Add quads to the graphs specified in the input quad data. Any graphs that do not already exist are created. Adding Triples to an Existing Graph
delete Delete a specific graph. Deleting a Graph.
deleteGraphs Delete all graphs. Unmanaged triples are not affected. For details, see Deleting a Graph.

writePermissions

mergePermissions

deletePermissions

Manage graph permissions. You can also set graph permissions during write and merge operations. For details, see Managing Permissions.

Creating a GraphManager Object

Operations on graphs, such as loading triples and reading a graph, require a com.marklogic.client.semantics.GraphManager object. To create a GraphManager, use DatabaseClient.newGraphManager.

For example, the following code snippet creates a DatabaseClient, and then uses it to create a GraphManager.

import com.marklogic.client.DatabaseClientFactory;
import com.marklogic.client.DatabaseClient;
import com.marklogic.client.DatabaseClient.DigestAuthContext;
import com.marklogic.client.semantics.GraphManager;
...
DatabaseClient client = DatabaseClientFactory.newClient(
    "localhost", 8000, "myDatabase", 
    new DigestAuthContext("username", "password"));
GraphManager gmgr = client.newGraphManager();

You do not have to create a new DatabaseClient object to create a GraphManager. You can re-use any client object previously created by your application that represents the desired connection to MarkLogic.

Specifying the Triple Format

When reading and writing triples, you must specify the triples format MIME type. You can specify the format in the following ways:

  • Use the withMimeType method on your triples Handle to set the format on a per source basis. For example, the following code initializes a FileHandle for reading triples in Turtle format:
    import com.marklogic.client.io.FileHandle;
    ...
    FileHandle fileHandle = 
      new FileHandle(new File("example.ttl"))
        .withMimetype(RDFMimeTypes.TURTLE);
  • Use GraphManager.setDefaultMimeType to set a format to be used across all operations performed through a given GraphManager. For example, the following code sets the default MIME type to Turtle:
    import com.marklogic.client.io.FileHandle;
    ...
    GraphManager graphMgr = ...;
    graphMgr.setDefaultMimeType(RDFMimeTypes.TURTLE);

Setting a default MIME type frees you from setting the MIME type on every triples handle and enables use of the GraphManager.*As methods, such as GraphManager.writeAs and GraphManager.readAs. For example:

graphMgr.setDefaultMimeType(RDFMimeTypes.TURTLE);
...
graphMgr.writeAs(
    someGraphURI, new FileHandle(new File("example.ttl")));

Set the MIME type to one of the values exposed by the RDFMimeTypes class, such as RDFMimeTypes.RDFJSON or RDFMimeTypes.TURTLE. For more details about triples formats accepted by MarkLogic, see Supported RDF Triple Formats in Semantics Developer's Guide.

To learn more about Handles, see Using Handles for Input and Output.

Creating or Overwriting a Graph

Use GraphManager.write and GraphManager.writeAs to create or overwrite a graph. If a graph already exists with the specified URI, the effect is the same as removing the existing graph and then recreating it f rom the input triple data.

Note that if you use GraphManager.write to load quads, any graph URI in a quad is ignored in favor of the graph URI parameter passed into write.

For example, the following code loads triples from a file into a graph. For the complete example, see Example: Loading, Managing, and Querying Triples.

public static void loadGraph(String filename, String graphURI, String format) {
    System.out.println("Creating graph " + graphURI);
    FileHandle tripleHandle = 
      new FileHandle(new File(filename)).withMimetype(format);
    graphMgr.write(graphURI, tripleHandle);
}

Use the following procedure to load semantic triples into a graph in MarkLogic.

  1. If you have not already done so, create a com.marklogic.client.semantics.GraphManager. as described in Creating a GraphManager Object. For example:
    GraphManager graphMgr = client.newGraphManager();
  2. Create a Handle associated with the input triples. The Handle type depends on the source for your content, such as a file or in-memory data. For example, the following Handle can be used to read triples in Turtle format from a file:
    FileHandle tripleHandle = 
      new FileHandle(new File("example.ttl"));
  3. If no default triples format is set on your GraphManager, specify the triples format for the Handle, using the withMimetype method. For more details, see Querying Semantic Triples With SPARQL. For example:
    tripleHandle.withMimeType(RDFMimeTypes.TURTLE);
  4. Write the triples to MarkLogic using GraphManager.write . For example:
    1. To load triples into a named graph, specify the graph URI as the graph URI parameter. For example:
      graphMgr.write(someGraphURI, tripleHandle);
    2. To load triples into the default graph, specify GraphManager.DEFAULT_GRAPH as the graph URI parameter. For example:
      graphMgr.write(GraphManager.DEFAULT_GRAPH, tripleHandle);
  5. If your application no longer needs to connect to MarkLogic, release the connection resources by calling the DatabaseClient object's release() method.
    client.release();

As an alternative to GraphManager.write, if you already have triples in an in-memory object, you can use GraphManager.writeAs to short circuit explicit creation of a handle. For example:

graphManager.setDefaultMimeType(RDFMimeTypes.RDFJSON);
...
Object graphData = ...;
graphMgr.writeAs(someGraphURI, graphData);

For more details on this technique, see Shortcut Methods as an Alternative to Creating Handles.

Reading Triples from a Graph

Use GraphManager.read or GraphManager.readAs to read the contents of a graph in MarkLogic. You must specify the serialization format from the triples, either on the read Handle or the GraphManager; for details, see Specifying the Triple Format.

The following example retrieves the contents of the default graph, in Turtle format and makes the results available to the application through a StringHandle:

StringHandle triples = graphMgr.read(
    GraphManager.DEFAULT_GRAPH,
    new StringHandle().withMimetype(RDFMimeTypes.TURTLE));
//...work with the triples as one big string

For a complete example, see Example: Loading, Managing, and Querying Triples.

Replacing Quad Data in Graphs

Use GraphManager.replaceGraphs and GraphManager.replaceGraphsAs to remove all quads from all graphs and then insert quad data into the graphs specified in the new quads. Unmanaged triples are not affected by this operation. The end result is the same as first calling GraphManager.delete (or deleteAs) and then inserting the quads.

The quad data can be in either NQuad or TriG format. Set the MIME type appropriate, as described in Specifying the Triple Format.

The following example adds a single triple in Turtle format to the default graph. This triple is passed via a StringHandle.

StringHandle quadHandle = new StringHandle()
    .with(someQuadData)
    .withMimetype(RDFMimeTypes.NQUAD);
graphMgr.replaceGraphs(quadHandle);

The following example performs a similar operation, using replaceAs:

graphMgr.setDefaultMimeType(RDFMimeTypes.NQUAD);
...
File graphData = new File(filename);
graphMgr.replaceGraphsAs(graphData);

Adding Triples to an Existing Graph

Use GraphManager.merge or GraphManager.mergeAs to merge triples into an existing graph. You must specify the serialization format from the triples, either on the read Handle or the GraphManager; for details, see Specifying the Triple Format.

For quad data, use mergeGraphs or mergeGraphsAs. For details, see Adding Quads into an Existing Graph.

The following example adds a single triple in Turtle format to the default graph. This triple is passed via a StringHandle.

StringHandle stringHandle = new StringHandle()
    .with("<http://example.org/subject2> " +
          "<http://example.org/predicate2> " +
          "<http://example.org/object2> .")
    .withMimetype(RDFMimeTypes.TURTLE);
graphMgr.merge("myExample/graphUri", stringHandle);

The following example performs a similar operation, using mergeAs:

graphMgr.setDefaultMimeType(RDFMimeTypes.TURTLE);
...
Object graphData = ...;
graphMgr.mergeAs(someGraphURI, graphData);

Adding Quads into an Existing Graph

Use GraphManager.mergeGraphs and GraphManager.mergeGraphsAs to add quads to an existing graph. If a quad specifies the URI of an existing graph, the quad data is merged into that graph. If no such graph exists, the graph is created.

The quad data can be in either NQuad or TriG format. Set the MIME type appropriate, as described in Specifying the Triple Format.

The following example adds a single triple in Turtle format to the default graph. This triple is passed via a StringHandle.

StringHandle quadHandle = new StringHandle()
    .with(someQuadData)
    .withMimetype(RDFMimeTypes.NQUAD);
graphMgr.mergeGraphs(quadHandle);

The following example performs a similar operation, using mergeAs:

graphMgr.setDefaultMimeType(RDFMimeTypes.NQUAD);
...
File graphData = new File(filename);
graphMgr.mergeGraphsAs(graphData);

Deleting a Graph

Use GraphManager.delete to remove a single graph. Use GraphManager.deleteGraphs to delete all graphs.

The following example removes a single graph with the specified URI:

graphMgr.delete(someGraphURI);

The following example removes all graphs:

graphMgr.deleteGraphs(someGraphURI);

For a complete example, see Example: Loading, Managing, and Querying Triples.

Querying Semantic Triples With SPARQL

To query semantic data using SPARQL, create a SPARQLQueryDefinition, and then evaluate it using one of the SPARQLQueryManager.execute* methods. You can configure many aspects of your query, such as variable bindings, the graphs to which to apply the query, the query optimization level.

Basic Steps for SPARQL Query Evaluation

Evaluating a SPARQL query consists of the following basic steps:

  1. Create a query manager using DatabaseClient.newSPARQLQueryManager. For example:
    DatabaseClient client = ...;
    SPARQLQueryManager sqmgr = client.newSPARQLManager();
  2. Create a query using SPARQLQueryManager.newSPARQLQueryDefinition and configure the query as needed. For example:
    SPARQLQueryDefinition query = sqmgr.newSPARQLQueryDefinition(
      "SELECT * WHERE { ?s ?p ?o } LIMIT 10")
      .withBinding("o", "http://example.org/object1");
  3. Evaluate the query and receive results by calling one of the execute* methods of SPARQLQueryManager. For example, use executeSelect for a SELECT query:
    JacksonHandle results = new JacksonHandle();
    results.setMimetype(SPARQLMimeTypes.SPARQL_JSON));
    results = sqmgr.executeSelect(query, results);

The format of the results you receive depends on the type of query you evaluate and how you configure your results Handle and/or the SPARQLQueryManager. For details, see Handling Query Results.

For example, the following evaluates a SPARQL SELECT query and returns the results as JSON. For a complete example, see Example: Loading, Managing, and Querying Triples.

SPARQLQueryManager qm = client.newSPARQLQueryManager();
SPARQLQueryDefinition query = qm.newQueryDefinition(
    "SELECT ?person " +
    "WHERE { ?person <http://example.org/marklogic/predicate/livesIn> \"London\" }"
);

JsonNode results = qm.executeSelect(query, new JacksonHandle()).get();
// ... Process results

Handling Query Results

The layout and available format of the results from a SPARQL query depend on the type of query. For details, see the following topics:

SELECT Results

A SPARQL SELECT query returns a SPARQL result set, serialized as JSON, XML, or plain text comma-separated values (CSV). You must set the MIME type on your results ReadHandle as appropriate for the results format you want to use. The supported MIME types for a SELECT query are defined by com.marklogic.client.semantics.SPARQLMimeTypes.

For example, JacksonHandle implements SPARQLResultsReadHandle and JSONReadHandle, so you should set the handle MIME type to SPARQLMimeTypes.SPARQL_JSON when receiving SELECT query results through a JacksonHandle:

JacksonHandle handle = new JacksonHandle();
handle.setMimeType(SPARQLMimeTypes.SPARQL_JSON);

The following table summarizes the supported Handle and MIME type combinations:

Handle Type MIME Type Result
JSONREADHandle SPARQLMimeTypes.SPARQL_JSON SPARQL results, serialized as JSON. For details on this format, see https://www.w3.org/TR/sparql11-results-json/.
XMLReadHandle SPARQLMimeTypes.SPARQL_XML SPARQL results, serialized as XML. For details on this format, see https://www.w3.org/TR/2013/REC-rdf-sparql-XMLres-20130321/.
Other Handle types SPARQLMimeTypes.SPARQL_CSV SPARQL results, serialized as CSV, with line per query solution. For details on this format, see https://www.w3.org/TR/2013/REC-sparql11-results-csv-tsv-20130321/.

For examples of the raw XML, JSON, and CSV results, see the examples in Response Output Formats in the Semantics Developer's Guide.

CONSTRUCT and DESCRIBE Results

CONSTRUCT and DESCRIBE queries return triples. You can request the results in any of the triples formats defined by com.marklogic.client.semantics.RDFMimeTypes, except TRIG. For best performance, use the N-triples format (RDFMimeTypes.NTRIPLES).

When using a JSONReadHandle, set the handle MIME type to RDFMimeTypes.RDF_JSON. This produces results in RDF/JSON format.

When using an XMLReadHandle, set the handle MIME type to RDFMimeTypes.RDF_XML. This produces results in RDF/XML format.

Any TriplesReadHandle implementation that handle plain text can use any of the RDFMimeTypes, such as NTRIPLE, NQUADS, or TURTLE.

For RDF/XML. use an XMLReadHandle with the MIME type set to RDFMimeTypes.RDF_XML.

To set the handle MIME type, use the setMimeType method. For example:

JacksonHandle handle = new JacksonHandle();
handle.setMimeType(RDFMimeTypes.RDFJSON);
ASK Results

An ASK query always returns a simple boolean value. For details, see QueryManager.executeAsk.

Defining Variable Bindings

If your query depends on runtime variable definitions, you can define variable bindings one at a time using the fluent SPARQLQueryDefinition.withBinding definition, or build up a set of bindings using SPARQLBindings and then attach them to the query using SPARQLQueryDefinition.setBindings.

The following example incrementally attaches bindings to a query definition using withBindings:

// incrementally attach bindings to a query
SPARQLQueryDefinition query = ...;
query.withBinding("o", "http://example.org/object1")
     .withBinding(...);

The following example builds up a setting of bindings and then attaches them all to the query at once, using setBindings:

// build up a set of bindings and attach them to a query
SPARQLBindings bindings = new SPARQLBindings();
bindings.bind("o", "http://example.org/object1");
bindings.bind(...);
query.setBindings(bindings);

Both SPARQLQueryDefinition.withBinding and SPARQLBindings enable you to specify a language tag or RDF type for the bound value.

For more details, see SPARQLBindings in the Java Client API Documentation.

Limiting the Number of Results

When you evaluate a SPARQL SELECT query, by default, all results are returned. You can limit the number of results returned in a 'page' using SPARQLQueryManager.setPageLength or a SPARQL LIMIT clause. You can retrieve successive pages of results by repeatedly calling executeSelect with a different page start position. For example:

// Change the max page length
sqmgr.setPageLength(NRESULTS);

// Fetch at most the first N results
long start = 1;
JacksonHandle results = sparqlMgr.executeSelect(query, handle, start);

// Fetch the next N results
start += N;
JacksonHandle results = sparqlMgr.executeSelect(query, handle, start);

Inferencing Support

Inferencing enables discovery of new 'facts' based on a combination of data and rules for understanding that data. The Java Client API includes the following features that facilitate inferencing:

Enabling or Disabling Automatic Inferencing

When automatic inferencing is enabled, MarkLogic can apply a default inferencing ruleset at query time. Default ruleset management is a function of the REST Management API. However, you can enable or disable the use of a default ruleset at query time using SPARQLQueryDefinition.withIncludeDefaultRulesets. Use of the default ruleset is enabled by default.

For more details, see Rulesets in the Semantics Developer's Guide.

Associating a Rule Set with a Query

You can explicitly apply a ruleset at query time rather than implicitly using the default ruleset.

The Java Client API includes the com.marklogic.client.semantics.SPARQLRuleset class with a set of built-in rulesets and a factory method to enable you to use custom rulesets. To associate a ruleset with a query, use SPARQLQueryDefinition.withRuleset or SPARQLQueryDefinition.setRulesets.

For more details, see Rulesets in the Semantics Developer's Guide.

Querying Triples with the Optic API

The Optic features of the Java Client API enable you to query semantic data without using SPARQL. The Optic features of the Java Client API are covered in detail in Optic Java API for Relational Operations.

To perform a semantic query using the Optic API, construct a query plan using com.marklogic.client.expression.PlanBuilder, and then evaluate it using com.marklogic.client.row.RowManager. For example, the following function creates a query plan that finds 'persons who live in London', based on the data from Example: Loading, Managing, and Querying Triples.

public static void opticQuery() {
    RowManager rowMgr = client.newRowManager();
    PlanBuilder p = rowMgr.newPlanBuilder();
    PlanPrefixer predPrefixer = 
        p.prefixer("http://example.org/marklogic/predicate/");
    Plan plan = 
        p.fromTriples(
            p.pattern(p.col("person"), 
            predPrefixer.iri("livesIn"), 
            p.xs.string("London")));

    RowSet<RowRecord> results = rowMgr.resultRows(plan);

    System.out.println("OPTIC: Persons who live in London:");
    for (RowRecord row: results) {
        System.out.println("  " + row.getString("person"));
    }
}

When the above function runs as part of the end to end example, it produces output of the following form:

OPTIC: Persons who live in London:
  http://example.org/marklogic/person/Jane_Smith
  http://example.org/marklogic/person/John_Smith
  http://example.org/marklogic/person/Mother_Goose

Example: Loading, Managing, and Querying Triples

The following example program demonstrates how to perform the following tasks:

  • Load triples into a graph. These triples become 'managed' triples. Use the operations discussed in Creating and Managing Graphs for graph management.
  • Load a document containing a triple. This becomes an unmanaged triple. It is indexed and can be queried, but it is not managed through the graph operations.
  • Execute a semantic query using either SPARQL or the Optic API.
  • Removed a graph, thereby removing all the managed triples in the graph.

This example creates a graph from the following input data. Copy and paste this data to a file, and then modify the variable tripleFilename in the example code to point to this file.

<http://example.org/marklogic/person/John_Smith> <http://example.org/marklogic/predicate/livesIn> "London"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/marklogic/person/Jane_Smith> <http://example.org/marklogic/predicate/livesIn> "London"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/marklogic/person/Jack_Smith> <http://example.org/marklogic/predicate/livesIn> "Glasgow"^^<http://www.w3.org/2001/XMLSchema#string> .

The example data contains triple data that define relationships of the form 'Person X livesIn Y'. The query run by the example finds all persons who live in London. The program runs the query several times:

  • After loading triples into the default graph. This query matches John Smith and Jack Smith.
  • After loading a document containing an unmanaged triple that asserts Mother Goose lives in London. This query matches John Smith, Jack Smith, and Mother Goose.
  • After removing the default graph. Only the unmanaged triple remains, so the query matches only Mother Goose.
  • After removing the document containing the unmanaged triple. No matches are found.

The example code demonstrates how to use a SPARQL query and an Optic query to fetch the same information. For more details, see Querying Semantic Triples With SPARQL and Querying Triples with the Optic API.

Before running the following program, modify the definition of the client and tripleFilename variables to match your environment.

package examples;

import java.io.File;

import com.fasterxml.jackson.databind.JsonNode;
import com.marklogic.client.DatabaseClient;
import com.marklogic.client.DatabaseClientFactory;
import com.marklogic.client.document.JSONDocumentManager;
import com.marklogic.client.expression.PlanBuilder;
import com.marklogic.client.expression.PlanBuilder.Plan;
import com.marklogic.client.io.FileHandle;
import com.marklogic.client.io.Format;
import com.marklogic.client.io.JacksonHandle;
import com.marklogic.client.io.StringHandle;
import com.marklogic.client.row.RowManager;
import com.marklogic.client.row.RowRecord;
import com.marklogic.client.row.RowSet;
import com.marklogic.client.semantics.GraphManager;
import com.marklogic.client.semantics.RDFMimeTypes;
import com.marklogic.client.semantics.SPARQLQueryDefinition;
import com.marklogic.client.semantics.SPARQLQueryManager;
import com.marklogic.client.type.PlanPrefixer;

public class Graphs {
    static DatabaseClient client = DatabaseClientFactory.newClient(
        "localhost", 8000, "Documents",
        new DatabaseClientFactory.DigestAuthContext(
            "username", "password"));
    static private GraphManager graphMgr = client.newGraphManager();
    static private String graphURI = GraphManager.DEFAULT_GRAPH;
    static private String tripleFilename = "/path/to/your/file.ttl";
    static private String unmanagedTripleDocURI = "mothergoose.json";

    // Load managed triples from a file into a graph in MarkLogic
    public static void loadGraph(String filename, String graphURI, String format) {
        System.out.println("Creating graph " + graphURI);
        FileHandle tripleHandle = 
            new FileHandle(new File(filename)).withMimetype(format);
        graphMgr.write(graphURI, tripleHandle);
    }
    
    // Insert a document that includes an unmanaged triple.
    public static void addUnmanagedTriple() {
        System.out.println("Inserting doc containing an unmanaged triple...");
        StringHandle contentHandle = new StringHandle(
            "{ \"name\": \"Mother Goose\"," +
                "\"triple\" : {" +
                    "\"subject\" : \"http://example.org/marklogic/person/Mother_Goose\"," +
                    "\"predicate\" : \"http://example.org/marklogic/predicate/livesIn\"," +
                    "\"object\" : {" +
                      "\"value\" : \"London\"," +
                      "\"datatype\" : \"http://www.w3.org/2001/XMLSchema#string\"" +
            "} }  }").withFormat(Format.JSON);
        JSONDocumentManager jdm = client.newJSONDocumentManager();
        jdm.write(unmanagedTripleDocURI, contentHandle);
    }
    
    public static void deleteUnmanagedTriple() {
        System.out.println("Removing doc containing unmanaged triple...");
        JSONDocumentManager jdm = client.newJSONDocumentManager();
        jdm.delete(unmanagedTripleDocURI);
    }
    
    public static void readGraph(String graphURI, String format) {
        System.out.println("Reading graph " + graphURI);
        StringHandle triples = 
            graphMgr.read(graphURI, new StringHandle().withMimetype(format));
        System.out.println(triples);
    }
    
    // Delete a graph. Unmmanaged triples are unaffected.
    public static void deleteGraph(String graphURI) {
        System.out.println("Deleting graph " + graphURI);
        graphMgr.delete(graphURI);
    }
    
    // Evaluate a SPARQL query.
    public static void sparqlQuery() {
        SPARQLQueryManager qm = client.newSPARQLQueryManager();
        SPARQLQueryDefinition query = qm.newQueryDefinition(
            "SELECT ?person " +
            "WHERE { ?person <http://example.org/marklogic/predicate/livesIn> \"London\" }"
        );

        JsonNode results = 
            qm.executeSelect(query, new JacksonHandle()).get();
        JsonNode matches = results.path("results").path("bindings");
        System.out.println("SPARQL: Persons who live in London:");
        for (int i = 0; i < matches.size(); i++) {
            String subject = 
                matches.get(i).path("person").path("value").asText();
            System.out.println("  " + subject);
        }
    }
    
    public static void opticQuery() {
        RowManager rowMgr = client.newRowManager();
        PlanBuilder pb = rowMgr.newPlanBuilder();
        PlanPrefixer predPrefixer = 
            pb.prefixer("http://example.org/marklogic/predicate/");
        Plan plan = pb.fromTriples(
            pb.pattern(
                pb.col("person"), 
                predPrefixer.iri("livesIn"), 
                pb.xs.string("London")));

        RowSet<RowRecord> results = rowMgr.resultRows(plan);
        System.out.println("OPTIC: Persons who live in London:");
        for (RowRecord row: results) {
            System.out.println("  " + row.getString("person"));
        }
    }
    
    public static void main(String[] args) {
        loadGraph(tripleFilename, graphURI, RDFMimeTypes.TURTLE);
        readGraph(graphURI, RDFMimeTypes.TURTLE);
        
        // Query the graph for persons who live in London. 
        // Should find 2 matches.
        sparqlQuery();
        
        // Add a document containing an unmanaged triple. Query again.
        // Should find 3 matches.
        addUnmanagedTriple();
        sparqlQuery();
        
        // Perform the same query using the Optic API
        opticQuery();
        
        // Delete the created graph. Unmanaged triple remains.
        // Query should find 1 match.
        deleteGraph(graphURI);
        sparqlQuery();
        
        // Remove the document containing the unmanaged triple.
        // Query should find no matches.
        deleteUnmanagedTriple();
        sparqlQuery();
        
        client.release();
    }
}

When you run the example, you should see output similar to the following. Whitespace has been added to the output to more easily distinguish between the operations.

Creating graph com.marklogic.client.semantics.GraphManager.DEFAULT_GRAPH
@prefix p1: <http://example.org/marklogic/predicate/> .
@prefix p0: <http://example.org/marklogic/person/> .
p0:Jane_Smith   p1:livesIn      "London" .
p0:Jack_Smith   p1:livesIn      "Glasgow" .
p0:John_Smith   p1:livesIn      "London" .

SPARQL: Persons who live in London:
  http://example.org/marklogic/person/Jane_Smith
  http://example.org/marklogic/person/John_Smith
  http://example.org/marklogic/person/Mother_Goose

Inserting a document containing an unmanaged triple...
SPARQL: Persons who live in London:
  http://example.org/marklogic/person/Jane_Smith
  http://example.org/marklogic/person/John_Smith
  http://example.org/marklogic/person/Mother_Goose

OPTIC: Persons who live in London:
  http://example.org/marklogic/person/Jane_Smith
  http://example.org/marklogic/person/John_Smith
  http://example.org/marklogic/person/Mother_Goose

Deleting graph com.marklogic.client.semantics.GraphManager.DEFAULT_GRAPH
SPARQL: Persons who live in London:
  http://example.org/marklogic/person/Mother_Goose

Removing document containing unmanaged triple...
SPARQL: Persons who live in London:

Using SPARQL Update to Manage Graphs and Graph Data

You can use SPARQL Update to insert, update, or delete triples and graphs, as an alternative to the graph management interface described in Creating and Managing Graphs. You cannot use SPARQL Update to operate on unmanaged triples.

To learn about SPARQL Update, see SPARQL Update in the Semantics Developer's Guide.

To use SPARQL Update with the Java Client API, follow the same procedure as for SPARQL query, but initialize your SPARQLQueryDefinition with update rather than query code, and use SPARQLQueryManager.executeUpdate to evaluate the update. For details, see Basic Steps for SPARQL Query Evaluation.

For example, the following code adds a single triple to the default graph. You can add this function to the example framework in Example: Loading, Managing, and Querying Triples to experiment with SPARQL Update.

public static void sparqlUpdate() {
    SPARQLQueryManager qm = client.newSPARQLQueryManager();
    SPARQLQueryDefinition query = qm.newQueryDefinition(
        "PREFIX exp: <http://example.org/marklogic/people/>" +
        "PREFIX pre: <http://example.org/marklogic/predicate/>" +
        "INSERT DATA {" +
        "  exp:Humpty_Dumpty pre:livesIn \"London\" ." +
        "  }"
    );
    System.out.println("Inserting a triple using SPARQL Update");
    qm.executeUpdate(query);
}

A successful SPARQL Update returns no results.

You can bind variables to values using the procedure described in Defining Variable Bindings.

Third Party Semantic Framework Integrations

MarkLogic provides Java integration with the following 3rd party semantic frameworks. You can use these integration libraries in conjunction with the Java Client API.

RDF4J/Sesame Framework

The Eclipse RDF4J Framework (formerly known as 'Sesame') is a Java API for processing and handling RDF data, including creating, parsing, storing, inferencing, and querying over this data. The MarkLogic RDF4J API is an RDF4J Repository implementation that exposes MarkLogic semantic capabilities to Java developers familiar with RDF4J. You can use the MarkLogic RDF4J API in conjunction with the Java Client API.

You can access the MarkLogic RDF4J API through Maven Central or GitHub. For more details, see:

Jena RDF API

The Jena RDF API is a Java API for processing and handling RDF data, including creating, parsing, storing, inferencing, and querying over this data. Java developers who are familiar with the Jena RDF API can now use that same API to access RDF data in MarkLogic. The MarkLogic Jena API exposes MarkLogic semantic capabilities to Java developers familiar with Jena. You can use the MarkLogic Jena API in conjunction with the Java Client API.

You can access the MarkLogic Jena API through Maven Central or GitHub. For more details, see:

Managing Permissions

Permissions on semantic data are managed at either the graph or document level, depending on whether the triples are managed or unmanaged. Querying and reading semantic data requires read permissions on either the containing graph (managed triples) or document (unmanaged triples).

This section covers the following topics related to controlling permissions on semantic data:

Default Graph Permissions and Required Privileges

All graphs created and managed using the Java, REST, or Node.js Client APIs grant 'read' capability to the rest-reader role and 'update' capability to the rest-writer role. These default permissions are always assigned to a graph, even if you do not explicitly specify them.

If you explicitly specify other permissions when creating a graph, the default permissions are still set, in addition to the permissions you specify.

You can use custom roles to limit access to selected users on a graph by graph basis. Your custom roles must include equivalent rest-reader and rest-writer privileges. Otherwise, users with these roles cannot use the Java Client API to manage or query semantic data. For details, see Controlling Access to Documents and Other Artifacts in the REST Application Developer's Guide.

For more information on the MarkLogic security model, see the Security Guide.

Setting Graph Permissions

When you create a graph with the Java Client API, MarkLogic assigns a set of default permissions, even if you do not specify any explicit permissions; for details, see Default Graph Permissions and Required Privileges. You can modify permissions on a graph as a standalone operation or as part of another operation, such as when creating or merging graphs.

Graph permissions are encapsulated in a GraphPermissions object. To create a set of graph permissions, use GraphManager.newGraphPermissions or GraphManager.permission.

To modify permissions standalone, use the following GraphManager methods:

  • GraphManager.writePermissions
  • GraphManager.mergePermissions
  • GraphManager.deletePermissions

To modify permissions as part of another operation, such as GraphManager.write or GraphManager.merge, include a GraphPermissions object in your call.

The following example sets the permissions on the graph with URI 'myExample/graphUri'. The code grants the role 'example_manager' read and update permissions on the graph.

graphMgr.writePermissions("myExample/graphUri",
    graphMgr.permission("example_manager", Capability.READ)
            .permission("example_manager", Capability.UPDATE));

The following example sets the graph permissions as part of a graph merge operation:

graphMgr.merge(
  "myExample/graphUri", someTriplesHandle,
  graphManager.permission("role1", Capability.READ)
              .permission("role2", Capability.READ, Capability.UPDATE));

Retrieving Graph Permissions

To retrieve permissions metadata for a named graph or the default graph, use GraphManager.getPermissions. Explore the resulting GraphPermissions object using Map operations. The Map keys are role names, such as 'rest-reader', and the values are the capabilities.

For example, the following code fetches the permissions on the default graph and prints the capabilities associated with the 'rest-reader' role:

GraphPermissions permissions =
     graphMgr.getPermissions(GraphManager.DEFAULT_GRAPH);
System.out.println(permissions.get("rest-reader");

Managing Permissions on Unmanaged Triples

Unmanaged triples are stored in documents, alongside other content, rather than being inserted into the triple store. You control access to unmanaged triples through the permissions on the documents that contain them.

For example, a SPARQL query will only return a matching unmanaged triple if the user running the query has read permissions on the document that contains the triple.

Permissions are considered document metadata. Set permissions using the DocumentManager interface and a MetadataHandle. For example, set permissions using DocumentMetadataHandle.setPermissions, and then including the metadata handle in a call to DocumentManager.write. For more details, see Reading, Modifying, and Writing Metadata.

For more information document permissions, see Protecting Documents in the Security Guide.

« Previous chapter
Next chapter »