Skip to content

Commit 796dec3

Browse files
authored
Add several context examples for Painless date documentation (#44985)
1 parent 9e7af14 commit 796dec3

File tree

5 files changed

+203
-9
lines changed

5 files changed

+203
-9
lines changed

docs/painless/painless-guide/painless-datetime.asciidoc

Lines changed: 190 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,7 @@ document is most commonly accessible through an input called `doc`.
541541
+
542542
[source,Painless]
543543
----
544-
def input = doc['input_datetime'].value;
544+
ZonedDateTime input = doc['input_datetime'].value;
545545
String output = input.format(DateTimeFormatter.ISO_INSTANT); <1>
546546
----
547547
<1> Note the use of a built-in DateTimeFormatter.
@@ -584,8 +584,8 @@ if (doc.containsKey('start') && doc.containsKey('end')) { <1>
584584
585585
if (doc['start'].size() > 0 && doc['end'].size() > 0) { <2>
586586
587-
def start = doc['start'].value;
588-
def end = doc['end'].value;
587+
ZonedDateTime start = doc['start'].value;
588+
ZonedDateTime end = doc['end'].value;
589589
long differenceInMillis = ChronoUnit.MILLIS.between(start, end);
590590
591591
// handle difference in times
@@ -660,7 +660,7 @@ preferred as there is no need to parse it for comparision.
660660
[source,Painless]
661661
----
662662
long now = params['now'];
663-
def inputDateTime = doc['input_datetime'];
663+
ZonedDateTime inputDateTime = doc['input_datetime'];
664664
long millisDateTime = zdt.toInstant().toEpochMilli();
665665
long elapsedTime = now - millisDateTime;
666666
----
@@ -712,9 +712,194 @@ long elapsedTime = now - millisDateTime;
712712
String nowString = params['now'];
713713
ZonedDateTime nowZdt = ZonedDateTime.parse(datetime); <1>
714714
long now = ZonedDateTime.toInstant().toEpochMilli();
715-
def inputDateTime = doc['input_datetime'];
715+
ZonedDateTime inputDateTime = doc['input_datetime'];
716716
long millisDateTime = zdt.toInstant().toEpochMilli();
717717
long elapsedTime = now - millisDateTime;
718718
----
719719
<1> Note this parses the same string datetime every time the script runs. Use a
720720
numeric datetime to avoid a significant performance hit.
721+
722+
==== Datetime Examples in Contexts
723+
724+
===== Load the Example Data
725+
726+
Run the following curl commands to load the data necessary for the context
727+
examples into an Elasticsearch cluster:
728+
729+
. Create {ref}/mapping.html[mappings] for the sample data.
730+
+
731+
[source,js]
732+
----
733+
PUT /messages
734+
{
735+
"mappings": {
736+
"properties": {
737+
"priority": {
738+
"type": "integer"
739+
},
740+
"datetime": {
741+
"type": "date"
742+
},
743+
"message": {
744+
"type": "text"
745+
}
746+
}
747+
}
748+
}
749+
----
750+
+
751+
// CONSOLE
752+
+
753+
. Load the sample data.
754+
+
755+
[source,js]
756+
----
757+
POST /_bulk
758+
{ "index" : { "_index" : "messages", "_id" : "1" } }
759+
{ "priority": 1, "datetime": "2019-07-17T12:13:14Z", "message": "m1" }
760+
{ "index" : { "_index" : "messages", "_id" : "2" } }
761+
{ "priority": 1, "datetime": "2019-07-24T01:14:59Z", "message": "m2" }
762+
{ "index" : { "_index" : "messages", "_id" : "3" } }
763+
{ "priority": 2, "datetime": "1983-10-14T00:36:42Z", "message": "m3" }
764+
{ "index" : { "_index" : "messages", "_id" : "4" } }
765+
{ "priority": 3, "datetime": "1983-10-10T02:15:15Z", "message": "m4" }
766+
{ "index" : { "_index" : "messages", "_id" : "5" } }
767+
{ "priority": 3, "datetime": "1983-10-10T17:18:19Z", "message": "m5" }
768+
{ "index" : { "_index" : "messages", "_id" : "6" } }
769+
{ "priority": 1, "datetime": "2019-08-03T17:19:31Z", "message": "m6" }
770+
{ "index" : { "_index" : "messages", "_id" : "7" } }
771+
{ "priority": 3, "datetime": "2019-08-04T17:20:00Z", "message": "m7" }
772+
{ "index" : { "_index" : "messages", "_id" : "8" } }
773+
{ "priority": 2, "datetime": "2019-08-04T18:01:01Z", "message": "m8" }
774+
{ "index" : { "_index" : "messages", "_id" : "9" } }
775+
{ "priority": 3, "datetime": "1983-10-10T19:00:45Z", "message": "m9" }
776+
{ "index" : { "_index" : "messages", "_id" : "10" } }
777+
{ "priority": 2, "datetime": "2019-07-23T23:39:54Z", "message": "m10" }
778+
----
779+
+
780+
// CONSOLE
781+
// TEST[continued]
782+
783+
===== Day-of-the-Week Bucket Aggregation Example
784+
785+
The following example uses a
786+
{ref}/search-aggregations-bucket-terms-aggregation.html#search-aggregations-bucket-terms-aggregation-script[terms aggregation]
787+
as part of the
788+
<<painless-bucket-script-agg-context, bucket script aggregation context>> to
789+
display the number of messages from each day-of-the-week.
790+
791+
[source,js]
792+
----
793+
GET /messages/_search?pretty=true
794+
{
795+
"aggs": {
796+
"day-of-week-count": {
797+
"terms": {
798+
"script": "return doc[\"datetime\"].value.getDayOfWeekEnum();"
799+
}
800+
}
801+
}
802+
}
803+
----
804+
// CONSOLE
805+
// TEST[continued]
806+
807+
===== Morning/Evening Bucket Aggregation Example
808+
809+
The following example uses a
810+
{ref}/search-aggregations-bucket-terms-aggregation.html#search-aggregations-bucket-terms-aggregation-script[terms aggregation]
811+
as part of the
812+
<<painless-bucket-script-agg-context, bucket script aggregation context>> to
813+
display the number of messages received in the morning versus the evening.
814+
815+
[source,js]
816+
----
817+
GET /messages/_search?pretty=true
818+
{
819+
"aggs": {
820+
"am-pm-count": {
821+
"terms": {
822+
"script": "return doc[\"datetime\"].value.getHour() < 12 ? \"AM\" : \"PM\";"
823+
}
824+
}
825+
}
826+
}
827+
----
828+
// CONSOLE
829+
// TEST[continued]
830+
831+
===== Age of a Message Script Field Example
832+
833+
The following example uses a
834+
{ref}/search-request-script-fields.html[script field] as part of the
835+
<<painless-field-context, field context>> to display the elapsed time between
836+
"now" and when a message was received.
837+
838+
[source,js]
839+
----
840+
GET /_search?pretty=true
841+
{
842+
"query" : {
843+
"match_all": {}
844+
},
845+
"script_fields" : {
846+
"message_age" : {
847+
"script" : {
848+
"source": "ZonedDateTime now = ZonedDateTime.ofInstant(Instant.ofEpochMilli(params[\"now\"]), ZoneId.of(\"Z\")); ZonedDateTime mdt = doc[\"datetime\"].value; String age; long years = mdt.until(now, ChronoUnit.YEARS); age = years + \"Y \"; mdt = mdt.plusYears(years); long months = mdt.until(now, ChronoUnit.MONTHS); age += months + \"M \"; mdt = mdt.plusMonths(months); long days = mdt.until(now, ChronoUnit.DAYS); age += days + \"D \"; mdt = mdt.plusDays(days); long hours = mdt.until(now, ChronoUnit.HOURS); age += hours + \"h \"; mdt = mdt.plusHours(hours); long minutes = mdt.until(now, ChronoUnit.MINUTES); age += minutes + \"m \"; mdt = mdt.plusMinutes(minutes); long seconds = mdt.until(now, ChronoUnit.SECONDS); age += hours + \"s\"; return age;",
849+
"params": {
850+
"now": 1574005645830
851+
}
852+
}
853+
}
854+
}
855+
}
856+
----
857+
// CONSOLE
858+
// TEST[continued]
859+
860+
The following shows the script broken into multiple lines:
861+
862+
[source,Painless]
863+
----
864+
ZonedDateTime now = ZonedDateTime.ofInstant(
865+
Instant.ofEpochMilli(params['now']), ZoneId.of('Z')); <1>
866+
ZonedDateTime mdt = doc['datetime'].value; <2>
867+
868+
String age;
869+
870+
long years = mdt.until(now, ChronoUnit.YEARS); <3>
871+
age = years + 'Y '; <4>
872+
mdt = mdt.plusYears(years); <5>
873+
874+
long months = mdt.until(now, ChronoUnit.MONTHS);
875+
age += months + 'M ';
876+
mdt = mdt.plusMonths(months);
877+
878+
long days = mdt.until(now, ChronoUnit.DAYS);
879+
age += days + 'D ';
880+
mdt = mdt.plusDays(days);
881+
882+
long hours = mdt.until(now, ChronoUnit.HOURS);
883+
age += hours + 'h ';
884+
mdt = mdt.plusHours(hours);
885+
886+
long minutes = mdt.until(now, ChronoUnit.MINUTES);
887+
age += minutes + 'm ';
888+
mdt = mdt.plusMinutes(minutes);
889+
890+
long seconds = mdt.until(now, ChronoUnit.SECONDS);
891+
age += hours + 's';
892+
893+
return age; <6>
894+
----
895+
<1> Parse the datetime "now" as input from the user-defined params.
896+
<2> Store the datetime the message was received as a `ZonedDateTime`.
897+
<3> Find the difference in years between "now" and the datetime the message was
898+
received.
899+
<4> Add the difference in years later returned in the format
900+
`Y <years> ...` for the age of a message.
901+
<5> Add the years so only the remainder of the months, days, etc. remain as the
902+
difference between "now" and the datetime the message was received. Repeat this
903+
pattern until the desired granularity is reached (seconds in this example).
904+
<6> Return the age of the message in the format
905+
`Y <years> M <months> D <days> h <hours> m <minutes> s <seconds>`.

modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubDefArray.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.elasticsearch.painless.lookup.def;
2828
import org.objectweb.asm.Type;
2929

30+
import java.time.ZonedDateTime;
3031
import java.util.Objects;
3132
import java.util.Set;
3233

@@ -53,7 +54,8 @@ void analyze(Locals locals) {
5354
index.expected = index.actual;
5455
index = index.cast(locals);
5556

56-
actual = expected == null || explicit ? def.class : expected;
57+
// TODO: remove ZonedDateTime exception when JodaCompatibleDateTime is removed
58+
actual = expected == null || expected == ZonedDateTime.class || explicit ? def.class : expected;
5759
}
5860

5961
@Override

modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubDefCall.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.elasticsearch.painless.lookup.def;
2828
import org.objectweb.asm.Type;
2929

30+
import java.time.ZonedDateTime;
3031
import java.util.ArrayList;
3132
import java.util.Collections;
3233
import java.util.List;
@@ -84,7 +85,8 @@ void analyze(Locals locals) {
8485
arguments.set(argument, expression.cast(locals));
8586
}
8687

87-
actual = expected == null || explicit ? def.class : expected;
88+
// TODO: remove ZonedDateTime exception when JodaCompatibleDateTime is removed
89+
actual = expected == null || expected == ZonedDateTime.class || explicit ? def.class : expected;
8890
}
8991

9092
@Override

modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubDefField.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.elasticsearch.painless.MethodWriter;
2727
import org.elasticsearch.painless.lookup.def;
2828

29+
import java.time.ZonedDateTime;
2930
import java.util.Objects;
3031
import java.util.Set;
3132

@@ -49,7 +50,8 @@ void extractVariables(Set<String> variables) {
4950

5051
@Override
5152
void analyze(Locals locals) {
52-
actual = expected == null || explicit ? def.class : expected;
53+
// TODO: remove ZonedDateTime exception when JodaCompatibleDateTime is removed
54+
actual = expected == null || expected == ZonedDateTime.class || explicit ? def.class : expected;
5355
}
5456

5557
@Override

modules/lang-painless/src/test/java/org/elasticsearch/painless/DefCastTests.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,10 @@ public void testdefToZonedDateTime() {
689689
assertEquals(0L, exec(
690690
"Instant instant = Instant.ofEpochMilli(434931330000L);" +
691691
"def d = new JodaCompatibleZonedDateTime(instant, ZoneId.of('Z'));" +
692-
"ZonedDateTime t = d;" +
692+
"def x = new HashMap(); x.put('dt', d);" +
693+
"ZonedDateTime t = x['dt'];" +
694+
"def y = t;" +
695+
"t = y;" +
693696
"return ChronoUnit.MILLIS.between(d, t);"
694697
));
695698
}

0 commit comments

Comments
 (0)