🚧

Always handle exceptions, corner cases!

Please keep in mind that trough network error or through other unexpected issues may an error occurs while getting the recommendation from or while adding events to the recommendation engine. This error can cause a program exception which always has to be handled in the proper way. Without handling the possible exceptions your code may terminate at an unexpected place and your site will operate abnormally. Even if you do not get a valid list of recommended items as a result of the recommendation request take care about hiding the recommendation widget or filling it with relevant items. Please ensure the safe error handling. Gravity does not take any responsibility for any damages caused by the lack or deficiency of error handling.

🚧

Proguard settings

Please do not forget to add the following setting if you use Proguard, maybe it could cause some issue."-keep class com.gravityrd.* { ; }" should be added to the proguard.cfg file

❗️

Each website has their own kind of items, with their own metadata and their own type of events. Please consult with your integration engineer about what applies to your case, and tailor the parameters described here as such.


Source code JavaDoc

A full JavaDoc style API documentation is available as well:

Guide

Client setup

  1. Download our Java client and add it to your project (note if you are not using some kind of dependency management system you'll also need the data object library - jsondto).
    a. You can download it using Maven dependency, it will also download jsondto. Example of Maven dependency is below:
    <dependencies>
        ...
        <dependency>
            <groupId>com.gravityrd</groupId>
            <artifactId>java-client</artifactId>
            <version>1.5.2</version>
        </dependency>
    </dependencies>
  1. Accessing the recommendation engine is done via a GravityClient object. The required parameters are provided by the integration engineer. For example:
    create connection object
GravityClient client = new GravityClient();
client.setRemoteUrl("https://<CUSTOMERID>-<SERVERLOCATION>.gravityrd-services.com/grrec-<CUSTOMERID>-war/WebshopServlet");
client.setUserName("CustomersUserName");
client.setPassword("CustomersPassword");

Add events

Each website has its own subset of valid event types, a valid list in your case is usually determined together with your integration engineer. Event types that you must integrate are part of the integration document. Each event have some data fields which usually has to be filled with the appropriate value. Event fields are the following:

Field nameDescriptionAPI
eventTypeThe type of the event specifies the user action which triggered the event.more
userIdThe identifier of the user whose action is logged.more
itemIdThe identifier of the item related to the users action.more
timeThe UNIX timestamp of the event in seconds.more
recommendationIdThis has to be filled if the action is related to a recommendation got from the recommendation engine. The recommendation id is always passed along the list of recommended items.more
cookieIdTo be able to identify guest users or registered users who are not logged in we always require to set the cookie id for every event.more

Additional information are stored in name-values. The set of name-values are not predefined we only have a list of recommended name-values for the various event types but new name-values can be defined by our customers as well. On a source code level the call of add event would look like the following:
send events to Reco

GravityEvent event_view = new GravityEvent();
event_view.cookieId = "abcdef123456789";
event_view.eventType = "VIEW";
event_view.itemId = "123s";
event_view.userId = "123u";
event_view.recommendationId = null; // not due recommendation
event_view.time = (int) (System.currentTimeMillis() / 1000);
GravityNameValue page = new GravityNameValue("unitPrice", "60.5");
event_view.nameValues = new GravityNameValue[] { page };
GravityEvent event_buy = new GravityEvent();
event_buy.cookieId = "abcdef123456789";
event_buy.eventType = "BUY";
event_buy.itemId = "123s";
event_buy.userId = "123u";
event_buy.recommendationId = "ASsadas3q232432-32432432-34"; // due to the recommendation
                                                            // with id
event_buy.time = (int) (System.currentTimeMillis() / 1000);
GravityNameValue unitPrice = new GravityNameValue("unitPrice", "60.5");
GravityNameValue quantity = new GravityNameValue("quantity", "3");
GravityNameValue orderId = new GravityNameValue("orderId", "103q432");
event_buy.nameValues = new GravityNameValue[] { unitPrice, quantity, orderId };
GravityEvent[] events = new GravityEvent[] { event_view, event_buy };
boolean isAsync = false;
try {
    client.addEvents(events, isAsync);
} catch (GravityRecEngException gre) {
    System.err.println("Error happened by adding events!");
    System.err.println("Message: " + gre.getMessage() + " Fault info: " + gre.faultInfo);
} catch (IOException ioe) {
    System.err.println("Error happened by adding events!");
    System.err.println("Message: " + ioe.getMessage());
}

❗️

addEvents(...) can be called in asynchronous and synchronous way. We suggest to use asynchronous call.
-With asynchronous call you'll get shorter response times specially while adding multiple events at the same time.
-The synchronous call is slower but it ensures that the sent events are added to the recommendation engine's database or it reports an error otherwise.

🚧

recommendationId is for passing the id of the recommendation which resulted that the users action (e.g. the user bought a book from the 'Recommended for you' box).

📘

Testing if everything were saved: It is possible to check if all the events and its name-values were saved. You only have to log in into the DASH and you can browse the stored information on the event page.

Items

All recommendable entities are considered items in the system.

Add or update items

Below it's how to add/update items on a source code level via Java calls. You should also set all of the item parameters in case of an update.

GravityItem item = new GravityItem();
item.title = "Samsung Galaxy";
item.hidden = false;
item.itemId = "123s";
GravityNameValue price = new GravityNameValue("price", "60.5");
GravityNameValue screenSize = new GravityNameValue("screenSize", "4.7");
item.nameValues = new GravityNameValue[] { price, screenSize };
GravityItem[] itemsToAdd = new GravityItem[] { item };
boolean isAsync = true;
try {
    client.addItems(itemsToAdd, isAsync);
} catch (GravityRecEngException gre) {
    System.err.println("Error happened by adding events!");
    System.err.println("Message: " + gre.getMessage() + " Fault info: " + gre.faultInfo);
} catch (IOException ioe) {
    System.err.println("Error happened by adding events!");
    System.err.println("Message: " + ioe.getMessage());
}

Delete items

Deletion can be also done by using addItems function. The only caveat is that you need to set the hidden parameter to true. You should also set all of the item parameters in case of a deletion, unless instructed otherwise by the integration engineer.
delete a recommendable entity

GravityItem item = new GravityItem();
item.title = "Samsung Galaxy";
item.fromDate = (int) (System.currentTimeMillis() / 1000);
item.toDate = 0;
item.hidden = true;
item.itemId = "123s";
GravityNameValue price = new GravityNameValue("unitPrice", "60.5");
GravityNameValue screenSize = new GravityNameValue("screenSize", "4.7");
item.nameValues = new GravityNameValue[] { price, screenSize };
GravityItem[] itemsToAdd = new GravityItem[] { item };
boolean isAsync = false;
try {
    client.addItems(itemsToAdd, isAsync);
} catch (GravityRecEngException gre) {
    System.err.println("Error happened by adding events!");
    System.err.println("Message: " + gre.getMessage() + " Fault info: " + gre.faultInfo);
} catch (IOException ioe) {
    System.err.println("Error happened by adding events!");
    System.err.println("Message: " + ioe.getMessage());
}

Users

Users are to who the recommendation is made too.

Add or update users

Here follows how to achieve this in Java source code:
add or update users

GravityUser user = new GravityUser();
user.hidden = false;
user.userId = "123u";
GravityNameValue registerDateAsUnixTimestamp = new GravityNameValue("registerDateAsUnixTimestamp", "1377879305");
GravityNameValue gender = new GravityNameValue("gender", "male");
GravityNameValue zip = new GravityNameValue("zip", "213324");
user.nameValues = new GravityNameValue[] { registerDateAsUnixTimestamp, gender, zip };
GravityUser[] usersToAdd = new GravityUser[] { user };
boolean isAsync = false;
try {
    client.addUsers(usersToAdd, isAsync);
} catch (GravityRecEngException gre) {
    System.err.println("Error happened by adding users!");
    System.err.println("Message: " + gre.getMessage() + " Fault info: " + gre.faultInfo);
} catch (IOException ioe) {
    System.err.println("Error happened by adding users!");
    System.err.println("Message: " + ioe.getMessage());
}

Recommendation request

🚧

Handle corner cases (empty recommendation list, network timeout, etcetera)!

Please keep in mind that trough network error or through other unexpected issues may an error occurs while getting the recommendation from or while adding events to the recommendation engine. This error can cause a program exception which always has to be handled in the proper way. Without handling the possible exceptions your code may terminate at an unexpected place and your site will operate abnormally. Even if you do not get a valid list of recommended items as a result of the recommendation request take care about hiding the recommendation widget or filling it with relevant items. Please ensure the safe error handling. Gravity does not take any responsibility for any damages caused by the lack or deficiency of error handling.

The output of the recommendation request is a list of items, and it may be acquired via the GravityClient object.

Note that no rendering is done by the recommendation engine, it provides only raw data. We ensure short answer time for the recommendation requests, nevertheless asynchronous rendering is recommended.
*simple recommendation request

// the unique user identifier
String userId = "testUser1";
// the unique cookie id
String cookieId = "abcdef123456789";
// the context of the recommendation is represented by a complex object
GravityRecommendationContext recommendationContext = new GravityRecommendationContext();
// Scenario id is indicating that the recommended items will be displayed
// on the main page and it uses personalized recommendation logic.
recommendationContext.scenarioId = "MAIN_PAGE_PERSONAL";
// the number of recommended items
recommendationContext.numberLimit = 10;
// the time of the recommendation is requested for
recommendationContext.recommendationTime = (int)(System.currentTimeMillis() / 1000);
// requesting the recommendation
GravityItemRecommendation itemRecommendation = null;
try {
    itemRecommendation =
        client.getItemRecommendation(userId, cookieId, recommendationContext);
} catch (GravityRecEngException e) {
    System.err.println("Error happened by getting the item recommendation!");
    System.err.println("Message: " + e.getMessage() + " Fault info: " + e.faultInfo);
}
// iterating through the returned items
if (itemRecommendation != null) {
    int count = 0;
    for (String itemId: itemRecommendation.itemIds) {
        count++;
        System.out.println(count + ". recommended item is: " + itemId);
    }
}

By parameterizing the GravityRecommendationContext object there are several possibilities to customize what item metadata information (and how many) is returned in the answer GravityItemRecommendation object.

Filter items already displayed on page

Usually it is not desired to show the same item multiple times on the same screen. To force the recommendation engine not to recommend items already visible on the page you have to pass their identifiers along the recommendation request:
no duplicated recommendations on page

// array containing the ids of items displayed on the page
String[] pageItemIds = new String[] {
    "item1", "item2", "..."
};
// creating the appropriate NameValues and adding them to the recommendation context
GravityNameValue nameValues = new GravityNameValue[pageItemIds.length];
int i = 0;
for (String itemId: pageItemIds) {
    recommendationContext.nameValues[i] = new GravityNameValue("itemOnPage", $itemId);
    i++;
}

If you have multiple Gravity recommendations on a single page then we suggest to use the getItemRecommendationBulk function which handles the filtering for the already recommended items.

Filter items from recommendation

The items can be filtered based of meta data attached to them. The filtering is controlled with name-value. In the next example we only allow books indicated as "new" to be recommended. This example works only if the item catalog contains a name-value with name "isNew" and with possible values of either "0" or "1".
do not recommend some items

// for filtering the recommended items a name-value starting with "filter." has to be set
GravityNameValue filter = new GravityNameValue("filter.isNew", "1");
recommendationContext.nameValues = new GravityNameValue[] {filter};

Get bulk item recommendation

The bulk item recommendation can be used if more than one recommendation box is appered on one page. By using this we can filter out the result of the first recommendation from the second recommendation result.
request multiple recommendations in a single request (also ensure no duplicates between them)

String userId = "testUser1"; // the unique user identifier
String cookieId = "abcdef123456789"; // the unique cookie id
  
// the context of the first (personalized) recommendation
GravityRecommendationContext recommendationContextPers = new GravityRecommendationContext();
// Scenario id is indicating that the recommended items will be displayed on the main page and it uses personalized recommendation logic.
recommendationContextPers.scenarioId = "MAIN_PAGE_PERSONAL";
recommendationContextPers.numberLimit = 10; // the number of recommended items 
// the time of the recommendation is requested for
recommendationContextPers.recommendationTime = (int)(System.currentTimeMillis() / 1000);
 
// the context of the second (popular items) recommendation
GravityRecommendationContext recommendationContextPop = new GravityRecommendationContext();
// Scenario id is indicating that the recommended items will be displayed on the main page and it returns popular items.
recommendationContextPop.scenarioId = "MAIN_PAGE_POPULAR";
recommendationContextPop.numberLimit = 5; // the number of recommended items 
// the time of the recommendation is requested for
recommendationContextPop.recommendationTime = (int)(System.currentTimeMillis() / 1000);
  
// the recommendation context array
GravityRecommendationContext[] recommendationContexts = new GravityRecommendationContext[]{recommendationContextPers, recommendationContextPop};
  
GravityItemRecommendation[] itemRecommendations = null; // requesting the recommendation
try {
    itemRecommendations = client.getItemRecommendationBulk(userId, cookieId, recommendationContexts);
} catch (GravityRecEngException e) {
    System.err.println("Error happened by getting the item recommendation!");
    System.err.println("Message: " + e.getMessage() + " Fault info: " + e.faultInfo);
}
// iterating through the returned items
if (itemRecommendations != null) {
    for (GravityItemRecommendation itemRecommendation: itemRecommendations) {
        System.out.println("recommendationId: " + itemRecommendation.recommendationId);
        int count = 0;
        for (String itemId: itemRecommendation.itemIds) {
            count++;
            System.out.println(count + ". recommended item is: " + itemId);
        }
    }
}

Get similar item recommendations

If you want display recommendations on the item details pages the identifier of the item the page is about has to be set beside setting the appropriate scenarioId. For this the corresponding scenario must support similar recommendations.
items similar to the current one

// Scenario id is indicating that the recommended items will be displayed
// on the item details page.
recommendationContext.scenarioId = "ITEM_PAGE";
// Setting the id of the item the details page is about.
GravityNameValue pageItemId = new GravityNameValue("currentItemId","ItemID_9999999");
recommendationContext.nameValues = new GravityNameValue[]{pageItemId};
```In this case the current item will be considered while generating the recommendation.

###Personalized search results
For personalized search results you should set up the search keywords (*searchString*) and the filtering parameters, such as *filter.categoryId*. The *pagingOffset* parameter can be used for set up the offset for paging.
**personalized recommendation on a search page**
```java
// scenarioId is indicating that the recommendation is used for search result ordering
recommendationContext.scenarioId = "SEARCH_RESULT";
// the number of items listed in the search result
recommendationContext.numberLimit = 50;
// the offset of the paging
GravityNameValue pagingOffset = new GravityNameValue("pagingOffset","0");
// the keywords in the search query
GravityNameValue searchString = new GravityNameValue("searchString","laptop bag");
// the current categoryId used for filtering the results
GravityNameValue filterCategoryId = new GravityNameValue("filter.categoryId","3456");
recommendationContext.nameValues = new GravityNameValue[]{pagingOffset, searchString, filterCategoryId};

Specify item metadata to return with recommendations

If you need some more information about the recommended items as its identifiers it can be included into the GravityItemRecommendation if the requested information was contained in the item catalog and it was requested in the GravityRecommendationContext context.For example if its hard for the bookstore to generate the URL for the cover image of the recommended books it can be requested in the GravityRecommendationContext:
set item meta information to also provide with the recommendation request

// setting the NameValues which has to be contained in the answer
recommendationContext.resultNameValues = new GravityNameValue[] {
    "ImageUrl"
};
// requesting the recommendation
itemRecommendation = null;
try {
    itemRecommendation =
        client.getItemRecommendation(userId, cookieId, recommendationContext);
} catch (GravityException e) {
    System.err.println("Error happened by getting the item recommendation!");
    System.err.println("Message: " + e.getMessage() + " Fault info: " + e.faultInfo);
}
// reading the recommended items with the ImageUrl-s
if (itemRecommendation != null) {
    for (GravityItem item: itemRecommendation.items) {
        String imageUrl = "";
        GravityNameValue[] itemNameValues = item.nameValues;
        for (GravityNameValue itemNameValue: itemNameValues) {
            if (itemNameValue.name.equalsIgnoreCase("ImageUrl")) {
                imageUrl = itemNameValue.value;
            }
        }
        System.out.println("ItemId: " + item.itemId + " ImageUrl: " + imageUrl);
    }
}

Recommendation context builder

Above java-client version 1.3.0 we provide a class named RecommendationContextBuilder, which can be helpful to create RecommendationContext objects in a modern and incremental way. The mandatory parameters (scenarioId, numberLimit) are set up in the constructor, and for every other property in GravityRecommendationContext we provide setters, and if it makes sense we provide setters for mutliple values too.For details check https://gravityrd.github.io/java-client/javadoc/
Example:

GravityRecommendationContext context = new GravityRecommendationContextBuilder("ITEM_PAGE", 10)
    .addNameValue("itemId", "I-10001")
    .addNameValue("split", "A")
    .addResultNameValue("title")
    .addResultNameValues(ImmutableList.of("category", "price"))
    .addResultNameValueFilter("category", ImmutableList.of("cat-A", "cat-B"))
//    .addFacet(Facets.term("category", 10).order(Order.TERM))  // discontinued feature
    .build();

Discontinued feature: Client-side Faceting

❗️

The below feature is discontinued

This feature is not supported any more, left here for reference for older integrations.

Some (mostly search related) scenarios support faceting. In these cases you can configure the facets you want to have, in the GravityRecommendationContext, before issueing the item recommendation request. The facets will be returned in the GravityItemRecommendation object.The best way to configure/use faceting is to use the FacetBuilder classes (provided by the Facets class, respectively TermFacetBuilder, RangeFacetBuilder), combined with GravityRecommendationContextBuilder.

For details check https://gravityrd.github.io/java-client/javadoc/

There are currently two types of facets supported:
-term facet: counts the number of term occurences for a given field
-range facet: counts how many result items are in the specified intevalsExample:

GravityRecommendationContext context = new GravityRecommendationContextBuilder("SEARCH", 100)
    .addNameValue("keyword", "red")
    .addResultNameValues(ImmutableList.of("title", "category", "price"))
    .addFacet(Facets.term("category", 5).order(Order.COUNT))
    .addFacet(Facets.range("price")
        .addRange(0.0, 5.0)
        .addRange(5.0,20.0)
        .addRange(20.0,100.0)
        .addRange(100.0,null))
    .build(); 

The above example:
-will return the top 100 items which match the keyword "red" (depends on the scenario).
-it will also return the top 5 facet-buckets for the field "category", which means the top 5 category names, which contain the most of the resulting documents
-it will also return the number of items falling into the ranges [0.0,5.0), [5.0,20.0), [20.0,100.0), [100.0, INFINITY).

More examples:

GravityRecommendationContext recommendationContext = new GravityRecommendationContextBuilder("SEARCH_RESULT", 12)
        .addNameValue("query", "bere")
        .addResultNameValue("stores")
        .addResultNameValue("brand")
        .addFacet(FacetBuilder.term("brand", 10))
        .addFacet(FacetBuilder.term("stores", 10))
        .addFacet(FacetBuilder.range("avg_price")
                .addRange(0.0, 2.0)
                .addRange(2.0, 5.0)
                .addRange(5.0, 10.0)
                .addRange(10.0, 25.0)
                .addRange(25.0, null))
        .build();
 
GravityRecommendationContext recommendationContext = new GravityRecommendationContextBuilder("SEARCH_RESULT", 12)
        .addNameValue("query", "bere")
        .addResultNameValue("stores")
        .addResultNameValue("brand")
        .addFacet(FacetBuilder.term("brand", 10)
                .filterValue("tuborg")
                .filterValue("burger")
                .filterValue("dab")
                .filterLogic(FilterLogic.OR))
        .addFacet(FacetBuilder.term("stores", 10)
                .filterValue("ct01")
                .filterValue("ae01")
                .filterLogic(FilterLogic.AND))
        .addFacet(FacetBuilder.dynamicRange("avg_price", 10, 2).filterValue(0.0, 30.0))
        .build();

The response can be read from the GravityItemRecommendation object’s facet field. Check for classes TermFacetResponse, RangeFacetResponse in the javadoc.

❗️

The above feature is discontinued

This feature is not supported any more, left here for reference for older integrations.

Privacy-related

Query user metadata

String userId = "testUser1";
GravityUser user = null;
try {
    user = client.getUserByUserId(userId);
    System.out.println(user);
} catch (GravityRecEngException e) {
    System.err.println("Error happened by getting the user metadata!");
    System.err.println("Message: " + e.getMessage() + " Fault info: " + e.faultInfo);
} 

or by cookieId:

String cookieId = "abcdef123456789";
GravityUser user = null;
try {
    user = client.getUserByCookieId(cookieId);
    System.out.println(user);
} catch (GravityRecEngException e) {
    System.err.println("Error happened by getting the user metadata!");
    System.err.println("Message: " + e.getMessage() + " Fault info: " + e.faultInfo);
} 

Query event history

String userId = "testUser1";
int limit = 100; // limit upper limit for returned events. If 0 or negative a default limit will be used
GravityEvent[] events = null;
try {
    events = client.getEventsByUserId(userId, limit);
    System.out.println(Arrays.deepToString(events));
} catch (GravityRecEngException e) {
    System.err.println("Error happened by getting the event history!");
    System.err.println("Message: " + e.getMessage() + " Fault info: " + e.faultInfo);
} 

or by cookieId:

String cookieId = "abcdef123456789";
int limit = 100; // limit upper limit for returned events. If 0 or negative a default limit will be used
GravityEvent[] events = null;
try {
    events = client.getEventsByCookieId(cookieId, limit);
    System.out.println(Arrays.deepToString(events));
} catch (GravityRecEngException e) {
    System.err.println("Error happened by getting the event history!");
    System.err.println("Message: " + e.getMessage() + " Fault info: " + e.faultInfo);
} 

Delete full event history

String cookieId = "abcdef123456789";
try {
    client.optOutCookie(cookieId);
} catch (GravityRecEngException e) {
    System.err.println("Error happened by deleting the event history!");
    System.err.println("Message: " + e.getMessage() + " Fault info: " + e.faultInfo);
} 

Delete full user metadata and event history

String userId = "testUser1";
try {
    client.optOutUser(userId);
} catch (GravityRecEngException e) {
    System.err.println("Error happened by deleting the event history and user metadata!");
    System.err.println("Message: " + e.getMessage() + " Fault info: " + e.faultInfo);
}