Elasticsearch 基于可用性的无痛脚本搜索文档



我使用的是 ES 7.0 版。我有一个以 UTC 时间表示可用性(开盘和关门时间(的商店索引。我将时间存储在 Integer 中,以便可以轻松地与无痛脚本中的当前时间匹配。

下面是一个示例文档:

{
"availability" : {
"thu" : [
{
"start" : 0,
"end" : 400
},
{
"start" : 1300,
"end" : 2400
}
],
"tue" : [
{
"start" : 1300,
"end" : 2400
},
{
"start" : 0,
"end" : 400
}
],
"wed" : [
{
"start" : 0,
"end" : 400
},
{
"start" : 1300,
"end" : 2400
}
],
"sat" : [
{
"start" : 1400,
"end" : 2400
},
{
"start" : 0,
"end" : 500
}
],
"fri" : [
{
"start" : 0,
"end" : 400
},
{
"start" : 1300,
"end" : 2400
}
],
"mon" : [
{
"start" : 0,
"end" : 200
},
{
"start" : 1300,
"end" : 2400
}
],
"sun" : [
{
"start" : 1400,
"end" : 2400
},
{
"start" : 0,
"end" : 200
}
]
},
.
.
.
.
}

下面是使用无痛脚本的查询:

GET stores/_search
{
"query": {
"bool": {
"filter" : {
"script" : {
"script" : {
"source": "String d = params.day, start_key = 'availability.'+d+'.start', end_key = 'availability.'+d+'.end'; long t = params.time; if(doc[start_key].size() != 0 && doc[end_key].size() != 0){ long s =  doc[start_key].value; long e = doc[end_key].value; return (s <= t && e > t); }",
"lang": "painless",
"params" : {
"day" : "wed",
"time" : 300
}
}
}
}
}
}
}

上面的查询适用于周三的时间 300,并在结果中给出上述文档,但在周三的时间 1400 不起作用。看起来脚本始终与可用性数组中的第一个值匹配。

我还尝试循环查看可用性的当天,但这给了我没有找到字段错误。

GET stores/_search
{
"query": {
"bool": {
"filter" : {
"script" : {
"script" : {
"source": "String d = params.day, start_key = 'availability.'+d+'.start', end_key = 'availability.'+d+'.end'; long t = params.time; if(doc[start_key].size() != 0 && doc[start_key].size() != 0){ for(item in doc['availability.'+d]){ long s =  item['start'].value; long e = item['end'].value; if (t >= s && t < e){ return true; } }}",
"lang": "painless",
"params" : {
"day" : "wed",
"time" : 300
}
}
}
}
}
}
}

上面的查询返回以下错误

{ ....
"reason": {
"type": "script_exception",
"reason": "runtime error",
"script_stack": [
"org.elasticsearch.search.lookup.LeafDocLookup.get(LeafDocLookup.java:94)",
"org.elasticsearch.search.lookup.LeafDocLookup.get(LeafDocLookup.java:41)",
"for(item in doc['availability.'+d]){ long ",
"                ^---- HERE"
],
"script": "String d = params.day, start_key = 'availability.'+d+'.start', end_key = 'availability.'+d+'.end'; long t = params.time; if(doc[start_key].size() != 0 && doc[start_key].size() != 0){ for(item in doc['availability.'+d]){ long s =  item['start'].value; long e = item['end'].value; if (t >= s && t < e){ return true; } }}",
"lang": "painless",
"caused_by": {
"type": "illegal_argument_exception",
"reason": "No field found for [availability.wed] in mapping with types []"
}
}
..... }

使用doc['availability']['wed']时也出现错误

我在这里错过了什么吗?

如果 availability.wed 的类型是对象,请使用下面

{
"query": {
"script": {
"script": {
"source": "String d = params.day; for(int i=0; i<doc['availability.'+params.day+'.start'].length;i++){ long start =doc['availability.'+params.day+'.start'][i]; long end = doc['availability.'+params.day+'.end'][i]; if(start <= params.time && end > params.time){ return true;}} ",
"lang": "painless",
"params": {
"day": "wed",
"time": 2300
}
}
}
}
}

如果 availability.wed 属于嵌套类型,请使用下面

映射:

PUT testindex10/_mappings
{
"properties": {
"name":{
"type": "text"
},
"availability": {
"type": "object",
"properties": {
"mon": {
"type": "nested",
"properties": {
"start": {
"type": "integer"
}
}
},
"tue": {
"type": "nested",
"properties": {
"start": {
"type": "integer"
}
}
},
"wed": {
"type": "nested",
"properties": {
"start": {
"type": "integer"
}
}
},
"thu": {
"type": "nested",
"properties": {
"start": {
"type": "integer"
}
}
},
"fri": {
"type": "nested",
"properties": {
"start": {
"type": "integer"
}
}
},
"sat": {
"type": "nested",
"properties": {
"start": {
"type": "integer"
}
}
}
}
}
}
}

数据:

[
{
"_index" : "testindex10",
"_type" : "_doc",
"_id" : "snyiPm0ButCCF6l_WyTl",
"_score" : 1.0,
"_source" : {
"name" : "store1",
"availability" : {
"mon" : [
{
"start" : 0,
"end" : 400
},
{
"start" : 1300,
"end" : 2400
}
],
"tue" : [
{
"start" : 1300,
"end" : 2400
},
{
"start" : 0,
"end" : 400
}
],
"wed" : [
{
"start" : 0,
"end" : 200
},
{
"start" : 1300,
"end" : 2400
}
],
"thu" : [
{
"start" : 1400,
"end" : 2400
},
{
"start" : 0,
"end" : 500
}
],
"fri" : [
{
"start" : 0,
"end" : 400
},
{
"start" : 1300,
"end" : 2400
}
]
}
}
},
{
"_index" : "testindex10",
"_type" : "_doc",
"_id" : "s3yiPm0ButCCF6l_liQq",
"_score" : 1.0,
"_source" : {
"name" : "store2",
"availability" : {
"mon" : [
{
"start" : 0,
"end" : 400
},
{
"start" : 1300,
"end" : 2400
}
],
"tue" : [
{
"start" : 1300,
"end" : 2400
},
{
"start" : 0,
"end" : 400
}
],
"wed" : [
{
"start" : 0,
"end" : 500
},
{
"start" : 1300,
"end" : 2400
}
],
"thu" : [
{
"start" : 1400,
"end" : 2400
},
{
"start" : 0,
"end" : 500
}
],
"fri" : [
{
"start" : 0,
"end" : 400
},
{
"start" : 1300,
"end" : 2400
}
]
}
}
}
]

查询

GET testindex10/_search
{
"query": {
"bool": {
"filter": {
"nested": {
"path": "availability.wed",
"query": {
"script": {
"script": {
"source": "String d = params.day; long start =doc['availability.'+params.day+'.start'].value; long end = doc['availability.'+params.day+'.end'].value; if(start <= params.time && end > params.time){ return true;}  ",
"lang": "painless",
"params": {
"day": "wed",
"time": 400
}
}
}
}
}
}
}
}
}

结果:

[
{
"_index" : "testindex10",
"_type" : "_doc",
"_id" : "s3yiPm0ButCCF6l_liQq",
"_score" : 0.0,
"_source" : {
"name" : "store2",
"availability" : {
"mon" : [
{
"start" : 0,
"end" : 400
},
{
"start" : 1300,
"end" : 2400
}
],
"tue" : [
{
"start" : 1300,
"end" : 2400
},
{
"start" : 0,
"end" : 400
}
],
"wed" : [
{
"start" : 0,
"end" : 500
},
{
"start" : 1300,
"end" : 2400
}
],
"thu" : [
{
"start" : 1400,
"end" : 2400
},
{
"start" : 0,
"end" : 500
}
],
"fri" : [
{
"start" : 0,
"end" : 400
},
{
"start" : 1300,
"end" : 2400
}
]
}
}
}
]

在没有脚本的情况下解决相同问题的另一种方法是(更好的性能(是

{
"query": {
"bool": {
"filter": {
"nested": {
"path": "availability.wed",
"query": {
"bool": {
"must": [
{
"range": {
"availability.wed.start": {
"lte": 400
}
}
},
{
"range": {
"availability.wed.end": {
"gte": 400
}
}
}
]
}
}
}
}
}
}
}

最新更新