5

Is it possible to add a type mapping to an ElasticSearch Index with the Java API using a JSON schema?

I know that ElasticSearch uses the first document to create a mapping and therefor i could enhance my first document with json schema. But i want to create the types before indexing an document.

4 Answers 4

5

You could do something like:

String mapping = XContentFactory.jsonBuilder().startObject().startObject(typeName).startObject("properties")
                    .startObject("location").field("type", "geo_point").endObject()
                    .startObject("language").field("type", "string").field("index", "not_analyzed").endObject()
                    .startObject("user").startObject("properties").startObject("screen_name").field("type", "string").field("index", "not_analyzed").endObject().endObject().endObject()
                    .startObject("mention").startObject("properties").startObject("screen_name").field("type", "string").field("index", "not_analyzed").endObject().endObject().endObject()
                    .startObject("in_reply").startObject("properties").startObject("user_screen_name").field("type", "string").field("index", "not_analyzed").endObject().endObject().endObject()
                    .startObject("retweet").startObject("properties").startObject("user_screen_name").field("type", "string").field("index", "not_analyzed").endObject().endObject().endObject()
                    .endObject().endObject().endObject().string();
client.admin().indices().preparePutMapping(indexName).setType(typeName).setSource(mapping).execute().actionGet();

Or if you have your mapping as a String

String json = "{}";
PutMappingResponse response = client.admin().indices()
                    .preparePutMapping(index)
                    .setType(type)
                    .setSource(json)
                    .execute().actionGet();     
Sign up to request clarification or add additional context in comments.

1 Comment

how is this related to json schema ?
3

It is possible to apply elasticsearch json mapping using java api,

STEP 1) First create your mapping for the Elasticsearch type in a json file,

eg. resources/Customer.json

{
    "Customer": {
        "settings": {}, 
        "properties": { 
            "name": { "type":"String" , "index": "not_analyzed"}
        }
    }
}

STEP 2) create a java method to apply mapping from a json file, (see complete example here)

class EsUtils {

  public static Client client

  public static void applyMapping(String index, String type, String location) throws Exception {

            String source = readJsonDefn(location);

            if (source != null) {
                PutMappingRequestBuilder pmrb = client.admin().indices()
                                                      .preparePutMapping(index)
                                                      .setType(type);
                pmrb.setSource(source);
                MappingListener mappingListener = new MappingListener(pmrb)

                // Create type and mapping
                Thread thread = new Thread(mappingListener)

                thread.start();
                while (!mappingListener.processComplete.get()) {
                    System.out.println("not complete yet. Waiting for 100 ms")
                    Thread.sleep(100);

                }

            } else {
                   System.out.println("mapping error");
            }

       }

       public static String readJsonDefn(String url) throws Exception {
              //implement it the way you like 
              StringBuffer bufferJSON = new StringBuffer();

              FileInputStream input = new FileInputStream(new File(url).absolutePath);
              DataInputStream inputStream = new DataInputStream(input);
              BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));

              String line;

              while ((line = br.readLine()) != null) {
                             bufferJSON.append(line);
              }
              br.close();
              return bufferJSON.toString();
       }

    //runnable mapping listener
    static class MappingListener implements Runnable {
      PutMappingRequestBuilder requestBuilder;
      public AtomicBoolean processComplete;
      PutMappingActionListener actionListener;

      @Override
      void run() {
        try {
            requestBuilder.execute(actionListener)
        } catch (Exception e) {
            e.printStackTrace()
            this.processComplete.set(true)
        }
      }

      public MappingListener(PutMappingRequestBuilder requestBuilder) {
        this.processComplete = new AtomicBoolean(false);
        actionListener = new PutMappingActionListener(processComplete);
        this.requestBuilder = requestBuilder;
      }

     //action listener
     static class PutMappingActionListener implements ActionListener<PutMappingResponse> {
        public AtomicBoolean processComplete;

        public PutMappingActionListener(AtomicBoolean processComplete) {
            this.processComplete = processComplete;
        }

        void onResponse(PutMappingResponse response) {
            if (response.isAcknowledged()) {
                System.out.println("template successfully applied")
            }
            processComplete.set(true)
        }

        @Override
        void onFailure(Throwable throwable) {
            System.out.println("error applying mapping : " + throwable)
            throwable.printStackTrace()
            processComplete.set(true)
        }
      }
     } //end of mappinglistener

    }

STEP 3) call applyMapping() method to apply mapping passing your es client,

String index = "yourIndex"; //yourIndex
String type  = "Customer";
String location = "resources/Customer.json";

EsUtils.client = esClient; //pass your client
EsUtils.applyMapping(index, type, location);

STEP 4) query as you want,

SearchRequestBuilder builder = client.prepareSearch("yourIndex");
builder.addAggregation(AggregationBuilders.terms("nameterms")
                                          .field("name").size(0))
SearchResponse response = builder.execute().actionGet();

Complete Reference

Elasticsearch apply mapping

Elasticsearch mapping settings 'not_analyzed' and grouping by field in Java

2 Comments

In which package is MappingListener? I use API version 1.3.2 and can't find it there.
@Cengiz MappingListener is a runnable class, that handles when response received. See updated answer.
1

I've done it like in the answer of Prayag with the difference not using the MappingListener.

import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequestBuilder;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;

public class MyElasticSearchService {

  private static final String INDEX = "myIndex";

  private Client client;

  private JsonSchema jsonSchema;

  ... 

  public void createIndexAndTypes() {
    createIndex();
    createType(TYPE_FOO, this.jsonSchema.getFooSchema());
    createType(TYPE_BAR, this.jsonSchema.getBarSchema());
  }

  private void createType(final String type, final String schema) {
    final PutMappingRequestBuilder requestBuilder = this.client.admin().indices().preparePutMapping(INDEX).setType(type);
    final PutMappingResponse response = requestBuilder.setSource(schema).execute().actionGet();
  }
}



import lombok.Getter;

@Getter
public class JsonSchema {

  private String fooSchema;
  private String barSchema;

  public JsonSchema() {
    this.fooSchema = ClasspathUtil.getTextfileContent("foo_schema.json");
    this.barSchema = ClasspathUtil.getTextfileContent("bar_schema.json");
  }
}



import java.io.IOException;

import org.apache.commons.io.IOUtils;
import org.springframework.core.io.ClassPathResource;

public class ClasspathUtil {

    public static String getTextfileContent(final String filename) {
        try {
            return IOUtils.toString(new ClassPathResource(filename).getInputStream());
        } catch (final IOException e) {
            throw new IllegalStateException(e);
        }
    }
}

Comments

0

Please find attached a solution for your problem, described on a working code sample. I had issues using a Terms filter in ElasticSearch. The field indexrecord_uuid contains a UUID String value (e.g. 8457964d-72e4-4b96-9232-c0e90fccd57d) which did not properly respond to exact matching queries using the TermsFilterBuilder.

By calling the Elastcisearch analyzer API I found out that the structure of this is field ist not a single String, but gets split up into multiple tokens. The issue is described here in detail.

    GET /_analyze?analyzer=standard
{
  "8457964d-72e4-4b96-9232-c0e90fccd57d"
}

Returns the result:

{
   "tokens": [
      {
         "token": "8457964d",
         "start_offset": 5,
         "end_offset": 13,
         "type": "<ALPHANUM>",
         "position": 1
      },
      {
         "token": "72e4",
         "start_offset": 14,
         "end_offset": 18,
         "type": "<ALPHANUM>",
         "position": 2
      },
      {
         "token": "4b96",
         "start_offset": 19,
         "end_offset": 23,
         "type": "<ALPHANUM>",
         "position": 3
      },
      {
         "token": "9232",
         "start_offset": 24,
         "end_offset": 28,
         "type": "<NUM>",
         "position": 4
      },
      {
         "token": "c0e90fccd57d",
         "start_offset": 29,
         "end_offset": 41,
         "type": "<ALPHANUM>",
         "position": 5
      }
   ]
}

So to enable a query like this

    GET /backmeup/backup/_search?pretty
{
  "query": {
    "bool" : {
    "must" : [ {
      "match" : {
        "owner_id" : {
          "query" : "2",
          "type" : "boolean"
        }
      }
    },{
      "bool" : {
        "should" : {
          "bool" : {
            "must" : {
              "filtered" : {
                "query" : {
                  "match_all" : { }
                },
                "filter" : {
                  "terms" : {
                    "indexrecord_uuid" : [ "8457964d-72e4-4b96-9232-c0e90fccd57d"]
                  }
                }
              }
            }
          }
        },
        "minimum_should_match" : "1"
      }
    }]
  }
  }
}

I had to delete the index and update the field mapping and set it to not_analyzed.

{
"backup" : {
     "properties" : {
         "indexrecord_uuid" : {
             "type" : "string",
             "index" : "not_analyzed" 
         }
     }
 }
}

issue the PutMappingRequest

PutMappingRequestBuilder pmrb = this.client.admin().indices().preparePutMapping(INDEX_NAME)
                .setType("backup");
        pmrb.setSource(this.getIndexCustomFieldMapping());
        PutMappingResponse putMappingResponse = pmrb.execute().actionGet();
        if (!putMappingResponse.isAcknowledged()) {
            this.logger.error("Could not create index [" + INDEX_NAME + " ].");
        } else {
            this.logger.debug("Successfully created index [" + INDEX_NAME + " ].");
        }

To check if the existing version of the index in place already is configured to use this mapping configuration you can do by this

 /**
 * Checks if the currently existing version of the index was configured properly e.g. if the indexrecord_uuid field
 * mapping was set etc
 * 
 * check if the field mapping for indexrecord_uuid was set to not analyzed as this causes issues using the terms
 * filter on UUID Strings https://www.elastic.co/guide/en/elasticsearch/guide/current/_finding_exact_values.html
 * https://www.elastic.co/guide/en/elasticsearch/guide/current/analysis-intro.html#analyze-api
 * https://www.elastic.co/guide/en/elasticsearch/guide/current/_finding_exact_values.html
 */
private boolean checkIsIndexFieldMappingSet() {
    GetMappingsResponse mapping = this.client.admin().indices().prepareGetMappings(INDEX_NAME).get();
    try {
        HashMap props = (HashMap) mapping.getMappings().get(INDEX_NAME).get("backup").getSourceAsMap();
        if (props != null) {
            if (props.containsKey("properties")) {
                HashMap fieldMappings = ((HashMap) props.get("properties"));
                //check if the field mapping for indexrecord_uuid was set to not analyzed 
                if (fieldMappings.containsKey("indexrecord_uuid")) {
                    HashMap fieldIndexRecordMapping = (HashMap) fieldMappings.get("indexrecord_uuid");
                    if (fieldIndexRecordMapping.containsKey("index")
                            && fieldIndexRecordMapping.get("index").toString().equals("not_analyzed")) {
                        return true;
                    }
                }

            }
        }
    } catch (Exception e) {
        this.logger.debug("Index does not contain a 'not_analyzed' field mapping for indexrecord_uuid");
    }
    return false;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.