Abhijith S
Abhijith S

Reputation: 532

Elasticsearch update the whole `_source` field based on search query

"_source": {
         "id": "5b1676493d21784208c36041",
         "label": "name",
         "properties": {
           "name": "patrick"
         },
         "updatedAt": 1528259039542
       }

I want to update this document based on id (not _id) with a new document.

Something like this:

    "_source": {
             "dataSource": "ELASTIC",
             "entity": "vertices",
             "label": "vertices",
             "id": "5b1676493d21784208c36041",
             "properties": {
                     "name": "patrick"
                  },
             "updatedAt": 1528259039542
           }

elasticsearch version: 6.2, ES Java api: 6.2

Upvotes: 2

Views: 4763

Answers (3)

Kwex
Kwex

Reputation: 4020

Thanks @jetnet for providing an idea to loop through all the entries in the map. When I used the script, it was still replacing the entire object, so had to adapt it as per the below to preserve the fields not supplied during an update operation. For context, if I first insert a document that has three objects each with 2 fields, if I then updated that document with the same 3 objects, but with 1 field each, the output is that I end up with a document with 3 objects each with 1 field instead of 3 objects with 2 fields each as I had originally.

I had to adapt @jetnet's script to give me the desired outcome of not overwriting object properties

POST /transaction_index/_update/33384637-3137-3132-5543-31304c4c3151
{
   "script": {
      "source": "if (ctx._source.Metadata == null || params.Metadata.Version >= ctx._source.Metadata.Version) { for (k in params.keySet()){ if (ctx._source[k] != null) { ctx._source[k].putAll(params.get(k)) } else { ctx._source.put(k, params.get(k)) } } } else { ctx.op = 'none' }",
      "params": {
         "Transaction": {
            "TransactionId": "33384637-3137-3132-5543-31304c4c3151",
            "TransactionKey": "Key1"
         },
         "Message": {
            "MessageId": "505a5953-374a-385a-4a48-52353549445a",
            "Context": "This is a test context"
         },
         "MessageDefinition": {
            "MessageDefinitionId": "a1c05e06-fa6b-40ce-992f-d083ff6c0243",
            "Code": 1010101010
         },
         "Metadata": {
            "Version": 1,
            "CreateTime": "2020-09-04T14:27:51.1986439+01:00",
            "IsLatest": true,
            "IsDummy": false,
            "VersionString": "20200903111111"
         }
    }
 },
 "scripted_upsert": true,
 "upsert": {}
}

    

Upvotes: 0

jetnet
jetnet

Reputation: 661

"ctx._source.putAll(params)" was a nice try, but unfortunately it moves all existing fields under _source.ctx.

So, the following works for me (ES 6.1):

"for (k in params.keySet()){if (!k.equals('ctx')){ctx._source.put(k, params.get(k))}}"

Upvotes: 1

Val
Val

Reputation: 217304

You can achieve what you want using the update by query API, basically like this:

POST index/_update_by_query
{
  "query": {
    "match": {
      "id": "5b1676493d21784208c36041"
    }
  },
  "script": {
    "source": "ctx._source = params",
    "params": {
       "dataSource": "ELASTIC",
       "entity": "vertices",
       "label": "vertices"
    }
  }
}

UPDATE: Using the Java API

Map<String, String> params = new HashMap<>();
params.put("dataSource", "ELASTIC");
params.put("entity", "vertices");
params.put("label", "vertices");

UpdateByQueryRequestBuilder updateByQuery = UpdateByQueryAction.INSTANCE.newRequestBuilder(client);
updateByQuery.source("index")
    .filter(QueryBuilders.matchQuery("id", "5b1676493d21784208c36041"))
    .size(1000)
    .script(new Script(ScriptType.INLINE, "painless", "ctx._source.putAll(params)", params));
BulkByScrollResponse response = updateByQuery.get();

More details on using the UpdateByQuery Java API and Java high level rest client

Upvotes: 6

Related Questions