0

I have a document in the form of:

curl -XPOST localhost:9200/books/book/1 -d '{
    "user_id": 1,
    "pages": [ {"page_id": 1, "count": 1}, {"page_id": 2, "count": 3}]
}

Now lets say the user reads page 1 again, so I want to increment the count. The document should become:

{
   "user_id": 1,
   "pages": [ {"page_id": 1, "count": 2}, {"page_id": 2, "count": 3}]
}

But how do you do this update of an element of a list using an if variable?

An example of a simple update in Elasticsearch is as follows:

curl -XPOST localhost:9200/books/book/2 -d '{
   "user_id": 1,
   "pages": { 
   "page_1": 1,
   "page_2": 2
   }
}'

curl -XPOST localhost:9200/books/book/2/_update -d '
{ 
    "script": "ctx._source.pages.page_1+=1"
}'

The document now becomes:

{
  "user_id": 1,
  "pages": { 
      "page_1": 1,
      "page_2": 2
}

However this more simple format of a doc looses stating the page_id as a field, so the id itself acts as the field. Similarly the value associated to the field has no real definition. Thus this isn't a great solution.

Anyway, would be great to have any ideas on how to update the array accordingly or any ideas on structuring of the data.

Note: Using ES 1.4.4, You also need to add script.disable_dynamic: false to your elasticsearch.yml file.

1 Answer 1

1

Assuming I'm understanding your problem correctly, I would probably use a parent/child relationship.

To test it, I set up an index with a "user" parent and "page" child, as follows:

PUT /test_index
{
   "settings": {
      "number_of_shards": 1
   },
   "mappings": {
      "user": {
         "_id": {
            "path": "user_id"
         },
         "properties": {
            "user_id": {
               "type": "integer"
            }
         }
      },
      "page": {
         "_parent": {
            "type": "user"
         },
         "_id": {
            "path": "page_id"
         },
         "properties": {
            "page_id": {
               "type": "integer"
            },
            "count": {
               "type": "integer"
            }
         }
      }
   }
}

(I used the "path" parameter in the "_id"s because it makes the indexing less redundant; the ES docs say that path is deprecated in ES 1.5, but they don't say what it's being replaced with.)

Then indexed a few docs:

POST /test_index/_bulk
{"index":{"_type":"user"}}
{"user_id":1}
{"index":{"_type":"page","_parent":1}}
{"page_id":1,"count":1}
{"index":{"_type":"page","_parent":1}}
{"page_id":2,"count":1}

Now I can use a scripted partial update to increment the "count" field of a page. Because of the parent/child relationship, I have to use the parent parameter to tell ES how to route the request.

POST /test_index/page/2/_update?parent=1
{
   "script": "ctx._source.count+=1"
}

Now if I search for that document, I will see that it was updated as expected:

POST /test_index/page/_search
{
    "query": {
        "term": {
           "page_id": {
              "value": "2"
           }
        }
    }
}
...
{
   "took": 3,
   "timed_out": false,
   "_shards": {
      "total": 1,
      "successful": 1,
      "failed": 0
   },
   "hits": {
      "total": 1,
      "max_score": 1,
      "hits": [
         {
            "_index": "test_index",
            "_type": "page",
            "_id": "2",
            "_score": 1,
            "_source": {
               "page_id": 2,
               "count": 2
            }
         }
      ]
   }
}

Here is the code all in one place:

http://sense.qbox.io/gist/9c977f15b514ec251aef8e84e9510d3de43aef8a

Sign up to request clarification or add additional context in comments.

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.