Search code examples
javamarklogicmarklogic-8

Marklogic Aggregate query with Java API


I am trying to get an aggregate query to run using the ML Java API, and am having a bit of trouble. I have followed the documentation, but there is a requirement for a values constraint, and i'm not really sure what that is supposed to look like. I tried the following:

    String options =
            "<options xmlns:search=\"http://marklogic.com/appservices/search\">" +
            "   <values name=\"average\">" +
            "       <range type=\"xs:string\">" +
            "           <element ns=\"\" name=\"content-id\"/>" +
            "       </range>" +
            "   </values>" +
            "</options>";

    StringHandle handle = new StringHandle(options);
    QueryOptionsManager optMgr = client.newServerConfigManager().newQueryOptionsManager();
    optMgr.writeOptions("average", handle);
    QueryManager queryManager = client.newQueryManager();
    StructuredQueryBuilder queryBuilder = queryManager.newStructuredQueryBuilder();
    ValuesDefinition valuesDefinition = queryManager.newValuesDefinition("average");
    valuesDefinition.setAggregate("avg");
    valuesDefinition.setQueryDefinition(queryBuilder.value(queryBuilder.element("content-id"),contentId));

    ValuesHandle results = queryManager.values(valuesDefinition, new ValuesHandle());

I took a stab at the options based on some other options i'm using. However, when I try to write the options it tells me Invalid Content: Unexpected Payload.

I get the feeling i'm going about this the wrong way. Essentially I want to find all documents that have a given value in the element "content-id", and then get the average of another element called "star-rating".

Should the options be set for "content-id" or "star-rating"? The documentation doesn't show the use of a queryDefinition, should I remove that? Modify it? Is there an easier way to do this in Java?

Edit: Forgot to mention, I also created an element range index on content-id with type string.


Solution

  • With the guidance of @SamMefford I was able to reach a solution. It looks like this:

        String options =
                "<options xmlns=\"http://marklogic.com/appservices/search\">" +
                "   <values name=\"star-rating\">" +
                "       <range type=\"xs:float\">" +
                "           <element ns=\"\" name=\"star-rating\"/>" +
                "       </range>" +
                "   </values>" +
                "</options>";
    
        StringHandle handle = new StringHandle(options);
        QueryOptionsManager optMgr = client.newServerConfigManager().newQueryOptionsManager();
        optMgr.writeOptions("star-rating", handle);
        QueryManager queryManager = client.newQueryManager();
        StructuredQueryBuilder queryBuilder = 
            queryManager.newStructuredQueryBuilder("star-rating");
        ValuesDefinition valuesDefinition = queryManager.newValuesDefinition("star-rating");
        valuesDefinition.setAggregate("avg");
        valuesDefinition.setQueryDefinition(
            queryBuilder.range(
                queryBuilder.element("content-id"),"string", 
                StructuredQueryBuilder.Operator.EQ,contentId
            )
        );
    
        String results = queryManager.values(valuesDefinition, new ValuesHandle())
           .getAggregate("avg").getValue();
    

    The options were created around the field that I wanted the average of. I created element indexes for both < star-rating > and < content-id >. The query then allowed me to filter to records with a specific content-id, and then get the average value of their star-ratings.