Michail Michailidis
Michail Michailidis

Reputation: 12171

SearchHits in ElasticSearch contain no fields using Java API

I am running this code to return matches

    public ArrayList<InnerText> suggest(String text, boolean isPersonal){

            TransportClient transportClient = new TransportClient();
            Client ESclient = transportClient.addTransportAddress(new InetSocketTransportAddress("localhost", 9300));
            OrientGraph graph = new OrientGraph(ConnectionStrings.dbConnection);
            Map<String, Object> template_params = new HashMap<>();
            template_params.put("innerText", text);
            SearchResponse response = ESclient.prepareSearch()
                    .setTemplateName("innerText_match")
                    .setTemplateType(ScriptService.ScriptType.FILE)
                    .setTemplateParams(template_params)
                    .execute()
                    .actionGet();
            int index = 0;
            ArrayList<InnerText> list = new ArrayList<InnerText>();
            for (SearchHit hit : response.getHits()) {

                //String uuid = hit.field(InnerTextProps.uuid).toString();
                //InnerText innerText = vertexToInnerText(graph.getVertexByKey("InnerText.uuid",uuid),false);
                //list.add(null);
                //hit fields map is  org.elasticsearch.util.collect.EmptyImmutableMap
            }
            return list; 
    }

and I am getting hits back but the fields map for each of the SearchHits is empty (org.elasticsearch.util.collect.EmptyImmutableMap).Hits have correct id and type though. When using Sense plugin I see that things are stored and retrieved properly with the same query. What am I doing wrong?

Thanks!

Upvotes: 5

Views: 3869

Answers (1)

mvreijn
mvreijn

Reputation: 2942

After some exploration of the different methods, I have found that:

  • SearchHit.sourceAsMap() will give you access to the data if no specific fields were requested
  • SearchHit.getFields() will give you access to the specific fields that were requested in your query using SearchResponse.addFieldDataField( String )

When the entire source is requested (as is the default) the Map object will contain either String values or subsequent Map values:

Map<String,Object> contents = hit.sourceAsMap();
for ( String name : contents.keySet() )
{
    Object value = contents.get(name);
    System.out.println( "Hit field: "+name+ " object: "+value.getClass().getName() );
    if ( contents.get(name) instanceof java.util.HashMap )
    {
        // Handle map object
    }
    else
    {
        // Handle string value
    }
}

Obviously when you request specific fields you can handle those using the SearchHit and SearchHitField APIs which are easier IMHO.

EDIT

After some more exploration, retrieving the values from the SearchHitField object is not so easy after all:

        Map<String,SearchHitField> fields = hit.getFields();
        System.out.println( "Hit fields: "+fields.size() );
        for ( SearchHitField field : fields.values() )
        {
            System.out.println( " Hit field: "+field.getName() );
            System.out.println( " Value: "+field.getValue() );
        }

Will yield only null for the values in my case. To be continued...

EDIT2: Final Solutions

First, the addFieldDataField() method only works if you specify raw fields to retrieve. Using analyzed fields will result in the error "Field data loading is forbidden on <fieldname>".

Secondly when you retrieve fields using addField(), always specify leaf fields or the query will fail.

  • Use SearchResponse.addFieldDataField( <fieldname>.raw )
  • Use SearchResponse.addField( <leaffieldname> )

Now you can retrieve that field's content using SearchHitField.getValue(). Good luck.

Upvotes: 5

Related Questions