如何在脚本更新中使用_timestamp

时间:2015-02-15 20:26:19

标签: elasticsearch timestamp updates

我试图为this question提出一个优雅的答案,并遇到一个意想不到的问题。基本思想是根据文档的当前时间戳更新文档。看起来很简单,但我似乎错过了一些东西。在Update API页面的底部,ES文档说:

  

它还允许使用ctx._ttl更新文档的ttl,使用ctx._timestamp更新时间戳。请注意,如果时间戳未更新且未从_source中提取,则会将其设置为更新日期。

ES文档通常是神秘的,特别是在编写脚本时,但我认为这意味着我可以在更新脚本中使用_timestamp字段。

所以我设置了一个带时间戳的简单索引:

PUT /test_index
{
   "settings": {
      "number_of_shards": 1,
      "number_of_replicas": 0
   },
   "mappings": {
      "doc": {
         "_timestamp": {
            "enabled": true,
            "store": true,
            "path": "doc_date",
            "format" : "YYYY-MM-dd"
         },
         "properties": {
            "doc_date": {
               "type": "date",
                "format" : "YYYY-MM-dd"
            },
            "doc_text": {
               "type": "string"
            }
         }
      }
   }
}

并添加了一些文档:

POST /test_index/_bulk
{"index":{"_index":"test_index","_type":"doc","_id":1}}
{"doc_text":"doc1", "doc_date":"2015-2-5"}
{"index":{"_index":"test_index","_type":"doc","_id":2}}
{"doc_text":"doc2", "doc_date":"2015-2-10"}
{"index":{"_index":"test_index","_type":"doc","_id":3}}
{"doc_text":"doc3", "doc_date":"2015-2-15"}

如果我查询第一个文档,我会回到我的期望:

POST /test_index/_search
{
    "query": {
        "match": {
           "doc_text": "doc1"
        }
    }, 
    "fields": [
       "_timestamp",
       "_source"
    ]
}
...
{
   "took": 2,
   "timed_out": false,
   "_shards": {
      "total": 1,
      "successful": 1,
      "failed": 0
   },
   "hits": {
      "total": 1,
      "max_score": 1.4054651,
      "hits": [
         {
            "_index": "test_index",
            "_type": "doc",
            "_id": "1",
            "_score": 1.4054651,
            "_source": {
               "doc_text": "doc1",
               "doc_date": "2015-2-5"
            },
            "fields": {
               "_timestamp": 1423094400000
            }
         }
      ]
   }
}

到目前为止一切顺利。现在我想基于其时间戳有条件地更新第一个doc。首先我尝试了这个,但得到了一个错误:

POST /test_index/doc/1/_update
{
   "script": "if(ctx._timestamp < new_ts){ctx._source.doc_date=new_date;ctx._source.doc_text=new_text}",
   "params": {
      "new_ts": 1423526400000,
      "new_date": "2015-2-10",
      "new_text": "doc1-updated"
   }
}
...
{
   "error": "ElasticsearchIllegalArgumentException[failed to execute script]; nested: PropertyAccessException[[Error: could not access: _timestamp; in class: java.util.HashMap]\n[Near : {... if(ctx._timestamp < new_ts){ctx._ ....}]\n                ^\n[Line: 1, Column: 4]]; ",
   "status": 400
}

然后我尝试了这个:

POST /test_index/doc/1/_update
{
   "script": "if(ctx[\"_timestamp\"] < new_ts){ctx._source.doc_date=new_date;ctx._source.doc_text=new_text}",
   "params": {
      "new_ts": 1423526400000,
      "new_date": "2015-2-10",
      "new_text": "doc1-updated"
   }
}
...
{
   "_index": "test_index",
   "_type": "doc",
   "_id": "1",
   "_version": 2
}

我没有收到错误,但更新没有发生:

POST /test_index/_search
{
    "query": {
        "match": {
           "doc_text": "doc1"
        }
    }, 
    "fields": [
       "_timestamp",
       "_source"
    ]
}
...
{
   "took": 2,
   "timed_out": false,
   "_shards": {
      "total": 1,
      "successful": 1,
      "failed": 0
   },
   "hits": {
      "total": 1,
      "max_score": 1.287682,
      "hits": [
         {
            "_index": "test_index",
            "_type": "doc",
            "_id": "1",
            "_score": 1.287682,
            "_source": {
               "doc_text": "doc1",
               "doc_date": "2015-2-5"
            },
            "fields": {
               "_timestamp": 1423094400000
            }
         }
      ]
   }
}

出于好奇,我颠倒了条件:

POST /test_index/doc/1/_update
{
   "script": "if(ctx[\"_timestamp\"] > new_ts){ctx._source.doc_date=new_date;ctx._source.doc_text=new_text}",
   "params": {
      "new_ts": 1423526400000,
      "new_date": "2015-2-10",
      "new_text": "doc1-updated"
   }
}

具有相同的结果:没有更新。

好的,所以作为一个完整性检查我试图设置时间戳,并得到一个错误:

POST /test_index/doc/1/_update
{
   "script": "ctx._source.doc_date=new_date;ctx._source.doc_text=new_text;ctx._timestamp=new_ts",
   "params": {
      "new_ts": 1423526400000,
      "new_date": "2015-2-10",
      "new_text": "doc1-updated"
   }
}
...
{
   "error": "ClassCastException[java.lang.Long cannot be cast to java.lang.String]",
   "status": 500
}

我也尝试了"ctx[\"_timestamp\"]=new_ts;",并得到了同样的错误。

所以看起来_timestamp字段不可用于脚本,即使文档说它是。我做错了什么?

我也尝试在没有条件或更新时间戳的情况下进行更新,并且按预期工作。

我在Ubuntu 12虚拟机上运行了Elasticsearch 1.3.4版(显然启用了动态脚本)。

以下是我用来设置它的代码:

http://sense.qbox.io/gist/ca2b3c6b84572e5f87d57d22f8c38252fa4ee216

0 个答案:

没有答案