Page tree
Skip to end of metadata
Go to start of metadata

Overview

This page presents the simplest-possible example of a stand-alone Java application that sends a query to AIE from the command line and displays the search results in the console window. It demonstrates the fundamental features that all such search clients must possess.   

JavaClientAPISearchBoxes

View incoming links.

Create a Search Application in Java

The examples shown here presume that the AIE index contains documents from the Factbook (Quick Start Tutorial) demo.  The search application is sufficiently generic to work with any AIE documents, however.  

Create the Project

Create the plusjava project from the Using Java APIs page in AIE Designer.  Then follow the procedures on this page.

Sample Data

The query application demonstrated here is sufficiently generic to use with any AIE project.  For sake of demonstration, we started the FactBook demo and loaded the "city" feed.  At the end of the example we'll search for "London" and AIE will return multiple matches.

Create a New Class

To create a new Java class in AIE Designer for this application:

  1. In Designer, right-click the project node in the Project Explorer view.  In this example the project is plusjava.
  2. Select New > Class from the context menu.  This exposes the New Java Class dialog.
  3. In the New Java Class dialog, set the Source Folder to plusjava/src.
  4. In the New Java Class dialog, enter a new package name such as com.acme.client.  The object is to create a package that does not already exist in the project to prevent any possible name collisions.
  5. Continuing in the Java Class dialog, enter a name for the new class.  In this example we used SearchQueryTest
  6. Click the Finish button to create the class.  This will open the new class in an editing view.
  7. For the purposes of this exercise, open the SearchQueryTest.java file (attached to this page) and paste the content into the editor.  Save the file.

 

Import Required Classes

The SearchQueryTest.java file begins with a package statement and a list of imported classes.  (The library files that support these classes are already part of the Eclipse project generated by createproject.)

SearchQueryTest.java
import java.io.IOException; 
import java.util.ListIterator;

import com.attivio.sdk.search.QueryRequest;
import com.attivio.sdk.search.QueryResponse;
import com.attivio.sdk.AttivioException;
import com.attivio.sdk.search.QueryLanguages;
import com.attivio.sdk.search.SearchField;
import com.attivio.sdk.search.SearchDocument;
import com.attivio.sdk.search.facet.FacetBucket;
import com.attivio.sdk.search.facet.FacetRequest;
import com.attivio.sdk.search.facet.FacetResponse;
import com.attivio.sdk.search.facet.ScopeFacetRequest;
import com.attivio.sdk.search.fields.StoredField;
import com.attivio.sdk.client.AieClientFactory;
import com.attivio.sdk.client.SearchClient;
import com.attivio.sdk.service.Platform;
import com.attivio.sdk.service.ServiceFactory;

Class Declaration

The SearchQueryTest class will connect to a "remote" AIE server.  The host and port number are stored in variables in this example.

SearchQueryTest.java
public class SearchQueryTest {
  public QueryRequest request;
  
  // Change these to point to your AIE server
  private static final String host = "localhost";
  private static final int port = 17000;

Setting up Main

To run this class as an independent application, we must supply it with a main method. 

SearchQueryTest.java
  public static void main(String[] args) throws IOException {

The main() method expects a string array of arguments, which will be fed in from the command line.  It throws appropriate exceptions.

Create a Search Client

Use the ServiceFactory to instantiate a SearchClient object.

SearchQueryTest.java
    // create an attivio search client
    System.setProperty("AIE_ZOOKEEPER", "localhost:16980");
    Platform.instance.setProjectName("remote"); // must be the name of the remote project
    try (SearchClient searchClient = ServiceFactory.getService(SearchClient.class)) {

Once we have the searchClient object, we can set its clientWorkFlow value.  The default search workflow is called "search". 

SearchQueryTest.java
    // Must set a workflow: default workflow is "search"
    searchClient.setClientWorkflow("search");

Create a Query Request

Now we can create a QueryRequest  object and set some of its properties. 

SearchQueryTest.java
    // The queryString comes from the command line
    QueryRequest request = new QueryRequest(args[0], QueryLanguages.ADVANCED);

It suits the current example to use the Advanced Query Language.  Your application might find QueryLanguages.SIMPLE to be adequate.

Optional Extras

At this point in the process we can optionally experiment with some additional minor features, or just skip this section and go on to the next one.

Add Security Filter

The page Custom Index Security With ACLs points to this page for an example of how to use Access Control Lists to restrict the results of a query.  Set up the query client as shown on this page, and then issue a query that is filtered by access-control tokens.  We do this by adding a filter to the query.  Filters are ANDed with the query, so only those documents that match the filter are returned.

Presuming that the documents have an acl field containing UserID and GroupID values (as demonstrated on the Custom Index Security With ACLs page), we can create a query filter to match a specific UserID or GroupID:

    // Add a filter (filters are ANDed to the current query) which matches acls on UserID or GroupID
    // request.addFilter("OR(acl:" + UserID + ", acl:" + GroupID + ")", QueryLanguages.ADVANCED);

In practice, you would have to supply the UserID and GroupID values, which is beyond the scope of this example.

Relational Joins

The page that describes the Relational Querying JOIN feature points to this page to show how to do JOIN queries via the Java Client API.

This is very simple.  Just set up the search application as shown on this page, and then plug in a string containing the text of the JOIN query, like this:

QueryRequest request = new QueryRequest("JOIN(table:country, INNER(table:city, on=country), INNER(table:medal, on=country), INNER(table:news, on=country))", QueryLanguages.ADVANCED);

This JOIN query is from the Quick Start Tutorial example. The query returns a list of all countries that have at least one city, have won at least one Olympic medal, and that are currently in the news.

Add Fields

If you don't tell AIE which fields to return, it helpfully returns them all.  Alternately, you can demand them all, as shown here:

SearchQueryTest.java
    // Return all fields.
    request.addField("*");

If you would like AIE to return a specific set of fields or field expressions, you must request them one at a time, like this:

SearchQueryTest.java
    // Return specific fields.
   request.addField( new StoredField("title") ); 
   request.addField( new StoredField("date") ); 
   request.addField( new StoredField("text") );

Pagination

You can use the Search Application Example (on this page) to experiment with AIE's pagination commands.  The setOffset() and setRows() methods control which results will be returned from the index.  You can used these methods to page through a long list of result items in the user interface.  Insert these lines of code just before the line that executes the search:

SearchQueryTest.java
    // Demonstrate pagination
    request.setOffset(5);  // Skip the first 5 items.
    request.setRows(5);    // Show 5 result items.

This example skips over the first five result items (the first page of results that has already been viewed.  It then displays the next five items (the current page). 

Note that pagination interacts with AIE's features for Sorting Results.

Static Named Facet

The default name of a facet is the name of the field that is being faceted. When that isn't desired, we can apply a name to the facet and retrieve it on that basis.

SearchQueryTest.java
    // Add a static facet request (a "named" facet)
    FacetRequest namedFacet = new FacetRequest("Feelings");
    namedFacet.setField("sentiment");
    request.addFacet(namedFacet);

In this example, "sentiment" is the field the facet is based on. "Feelings" is the arbitrary name we chose to use instead.

We'll retrieve the facet data by name later on this page.

Scope Facet

This optional feature is presented for people who want to issue a scope search (such as "text:scope(location)") and get a scope facet in the search results.

SearchQueryTest.java
    // Add a scope facet request. Requires that you issue a 
    // scope query such as "text:scope(location)". 
    //request.addFacet( new ScopeFacetRequest("text", "location") );

This scope search asks AIE for all documents where any location entity appears in the text field.  For more information, see Scope Search and Scope Facets.

Business Center Profile

You can have your query use a published Attivio Business Center profile.  All you need is the name of the profile.

SearchQueryTest.java
     // Set Search Profile. Use name of a published ABC Profile
     //request.setSearchProfile("newsonly"); 

Execute the Query

The request object is all ready to go. To execute the query, pass the request to the searchClient.search() method.

SearchQueryTest.java
    // Execute search request, get response
    QueryResponse response = searchClient.search(request);

This method returns a QueryResponse  object, which we have bound to the variable response.

Display Returned Documents

The remainder of SearchQueryTest:

  • Prints out the number of matching documents.
  • Prints out the (optional) facets.
  • Iterates over documents in the response object. 
  • Iterates over fields in each document.
  • If necessary, iterates over multiple values found in a field.
  • Prints document information to the command window.

Number of Matching Documents

First, let's display the number of matching documents in the result set.

SearchQueryTest.java
    // Display results

    System.out.println("");
    System.out.println("Total documents matched: " + response.getDocuments().getNumberFound());

Display (Optional) Named Facet

Once we have the QueryResponse object, we can extract facets by name. Here we extract and print out the content of the "Feelings" facet.

    // Print out named facet 
    FacetResponse facet = response.getFacet("Feelings");
    System.out.println("Facet: Feelings");
        for (FacetBucket bucket : facet) {
            System.out.println(" " + bucket.getDisplayValue() + " " + bucket.getCount());
        }

Display (Optional) Scope Facet

If you are following the optional scope facet demonstration, this is where we retrieve and display the facet buckets.

SearchQueryTest.java
    // Print out scope facet
    facet = response.getFacet("text:location");
    System.out.println("Facet: text:location");
    for (FacetBucket bucket : facet) {
        System.out.println(" " + bucket.getDisplayValue() + " " + bucket.getCount());
        }

 

For more information, see Scope Search and Scope Facets.

Iterate over Response Documents

For each document, we need to iterate over the document's fields and field values.

SearchQueryTest.java
    // Iterate through documents
    for (SearchDocument document : response.getDocuments()) {

      System.out.println("");

      // Print document ID
      System.out.println("document ID: " + document.getId());

Each block of document output begins with the document ID number, as shown above.

Next is the loop that processes the fields within the document:

SearchQueryTest.java
      // Iterate through fields
      for (Field<?> field : document) {

        // Print field name
        System.out.print("  " + field.getName());

This is followed by the nested loop that prints out the values of the field.  Most fields are single-valued, but some are multi-valued. The logic has to check and branch to handle the two situations.

 

SearchQueryTest.java
        // Check field value count
        if (field.size() == 1) {

          // Print single value
          System.out.println(": " + field.getDisplayValue());
        } else {

          // Print multiple values
          int numFieldValues = field.getValues().toArray().length;
          System.out.print(" {" + numFieldValues + " values}:");
          int valNum = 0;
          for (Object value: field.getValues()) {
            valNum++;
            if (value instanceof String) {
              System.out.print(" {" + valNum + "} " + value);
            } else if (value instanceof Integer) {
              String valueString = "" + value;
              System.out.print(" {" + valNum + "} " + valueString);
            }
          }

          System.out.println("");
        }
      }
    }

Catch Exceptions

We have to include some error-catching boilerplate at the end.  This independent Java application might have to handle an AttivioException generated by the Java Client library.  Note that this application is not a part of AIE, however, and therefore should not throw an error.  AIE isn't there to catch it.

SearchQueryTest.java
    } catch (AttivioException e) {
      System.err.println("Category: " + e.getErrorCode().getCategory());
      System.err.println("Code: " + e.getErrorCode().getCode());
      e.printStackTrace();
      
    }
  }
}
  

Compile the Class

To compile SearchQueryTest in Designer, select the project in the Project Explorer. Click the Project menu, and select Clean.  Then select Project  >  Build Project.

Run the Program

To run SearchQueryTest from Designer, we'll have to create a Run Configuration for it.  

  1. In Designer, navigate from the Run menu to Run Configurations.  This opens the Run Configurations dialog box.
  2. Right-click Java Applications and select New
  3. Create a new SearchQueryTest run application in project plusjava.
  4. On the Arguments Tab, paste in the query string.  You can simply search for the keyword "London", or for the scope-facet experiment, enter a scope search such as "text:scope(location)".

    Run Configuration Command-Line Arguments
    London
  5. On the Arguments Tab, paste in the following VM arguments. (Edit the file path to suit your situation.)

    Run Configuration VM Arguments
    -Dattivio.log.printStackTraces=true -Dattivio.log.level=INFO 
    -Dattivio.log.directory="C:\attivio-projects\plusjava\build\logs"
  6. Apply the changes.   Close the dialog box.
 

Example Search Run

AIE must be running!

It should go without saying that the AIE server should be up and running before you attempt to run SearchQueryTest. 

To run the application, use the Eclipse Run menu.  Select the Run command.

In the following example the query is "London".  If the target AIE project is a FactBook demo with the cities feed loaded, our query should return multiple cities.  This output appears in the Designer console view. Each city result looks like this one:

Console Output
document ID: CITY-United Kingdom-London
  .score: 2.1944141
  .id: CITY-United Kingdom-London
  .zone: default
  title: London
  teaser: Greater London
  language: English
  languages: English
  size: 6967500
  table: city
  date: 2014-09-04T18:35:08
  text: Greater London
  position: -0.0022,0.8990
  latitude: 51.506325
  longitude: -0.127144
  sourcepath: C:\attivio42\conf\factbook\content\cities.csv
  filename: cities.csv
  location {2 values}: {1} London {2} Greater London
  conversionErrorCode: 0
  country: United Kingdom
  newDate: 04 Sep 2014

 

 

 

  • No labels