Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elasticsearch High Level Rest Client - java Map with typed (sub) fields - dates, numbers etc

(clarification copied from a comment)

I have a java.util.Map that has different key value pairs, and some of the values are dates, some numbers, some are strings, and some are also java.util.Maps that can also contain all kinds of above mentioned types. I am able to put it into the index, I see that the elasticsearch mapping is created automatically with correct field types, now I want to retrieve that Map and see dates, numbers, strings and nested Maps instead of what I currently have - just strings and Maps

Further story:

I'm putting a java.util.Map in Elasticsearch using the following code:

public void putMap(String key, Map<String, ?> value) {
    try {
        IndexRequest ir = Requests.indexRequest(_index)
                .id(key)
                .type("doc")
                .source(value);

        Factory.DB.index(ir); // the high level rest client here

    } catch (IOException ex) {
        throw new RuntimeException(ex);
    }
}

I am not able to create mappings explicitly as per my task.

For one of my indices it has created the mapping like this, which is quite fine:

{
"rex": {
"mappings": {
  "doc": {
    "properties": {
      "variables": {
        "properties": {
          "customer": {
            "properties": {
              "accounts": {
                "properties": {
                  "dateOpen": {
                    "type": "date"
                  },
                  "id": {
                    "type": "text",
                    "fields": {
                      "keyword": {
                        "type": "keyword",
                        "ignore_above": 256
                      }
                    }
                  }
                }
              },
              "dateOfBirth": {
                "type": "date"
              },
              "firstName": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              },
              "id": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              },

              "lastName": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              },
              "middleName": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
}
}

Now I am retrieving my structure back with the following code,

public Map<String, ?> getMap(String key) {
    try {

        GetRequest gr = new GetRequest(_index, "doc", key);

        try {
            GetResponse response = Factory.DB.get(gr);

            if (!response.isExists()) {
                return null;
            }

            Map<String, ?> ret = response.getSourceAsMap();

            return ret;
        } catch (ElasticsearchStatusException ee) {
            if (ee.status().getStatus() == RestStatus.NOT_FOUND.getStatus()) {
                return null;
            } else {
                throw new RuntimeException(ee);
            }
        }

    } catch (IOException ex) {
        throw new RuntimeException(ex);
    }
}

The dates are returned as strings like "1957-04-29T00:00:00.000Z"

There's no Java object to map this document to as I have only Maps of Maps/Lists/values.

How do I make the Java Rest Client respect the mapping the Elasticsearch created for the index? response.getFields() returns empty map.

In case it is impossible (like 'source is json/strings by design' etc etc), I am ready to retrieve the mapping in the most convenient form possible and walk through the result by myself. The code to retrieve elasticsearch mapping will be appreciated.

Big thank you!

like image 555
fedd Avatar asked May 05 '18 17:05

fedd


1 Answers

If you still want to fetch the type mapping and do the conversion manually, the Indices API has a Get Mapping command you can invoke with the Java Low Level REST Client.

String getMapping(RestHighLevelClient client, String index, String type) throws IOException {
    String endpoint = index + "/_mapping/" + type; 
    Response response = client.getLowLevelClient().performRequest("GET", endpoint);
    return EntityUtils.toString(response.getEntity());
}

But really I would recommend instead using something like Jackson for data binding. Bind the Map you get from Elasticsearch to a Java object that models the document, and let Jackson handle the type conversion for you.

like image 70
dnault Avatar answered Oct 03 '22 20:10

dnault