Elasticsearch.Nest 教程系列 10-3 常用类型:Date math expressions | 日期数字表达式


在“查询/过滤”的时候支持使用日期类型的数学表达式。表达式以“锚定”日期开始,该日期可以是现在,也可以是以“||”结尾的日期字符串。它后面可以是一个数学表达式,支持 +,- 和 /(四舍五入)。支持的单位有:

  • y (year)
  • M (month)
  • w (week)
  • d (day)
  • h (hour)
  • m (minute)
  • s (second)

ES 中的 Date Math

常规示例

使用 DateMath 提供的静态方法:

Expect("2020-01-23T00:00:00").WhenSerializing(Nest.DateMath.Anchored(new DateTime(2020,01, 23)));

字符串则会隐式转换为 DateMath

Expect("now").WhenSerializing<Nest.DateMath>("now");

DateMath 对于入参会进行一定程度的容错:

var nonsense = "now||*asdaqwe";  //“*asdaqwe”不是一个有效的时间表达式
Expect(nonsense)
    .WhenSerializing<Nest.DateMath>(nonsense)
    .AssertSubject(dateMath => ((IDateMath)dateMath)
        .Anchor.Match(
            d => d.Should().NotBe(default(DateTime)),
            s => s.Should().Be(nonsense)
        )
    );

DateTime 还会隐式转换为简单的日期数学表达式

  • 哪怕经过序列化/反序列化后,所得的Anchor 仍将是实际的 DateTime。

var date = new DateTime(2020,1, 23); //将会倍序列化为 "2020-01-23T00:00:00"

//UTC 示例
var utcDate = new DateTime(2020, 1, 23, 0, 0, 0, DateTimeKind.Utc); //将会倍序列化为 "2020-01-23T00:00:00Z"

复杂示例

范围可以链接到简单表达式

Expect("now+1d").WhenSerializing(Nest.DateMath.Now.Add("1d"));

Expect("now+1d-1m").WhenSerializing(
    Nest.DateMath.Now.Add("1d").Subtract(TimeSpan.FromMinutes(1)));

经过四舍五入的表达式,之后不能再使用范围进行链接,如下:

Expect("now+1d-1m/d").WhenSerializing(
    Nest.DateMath.Now.Add("1d")
        .Subtract(TimeSpan.FromMinutes(1))
        .RoundTo(DateMathTimeUnit.Day));

锚定日期时,需要在锚点和范围之间使用分隔符 “||”。同样,可以链接多个范围

Expect("2020-01-23T00:00:00||+1d-1m").WhenSerializing(
    Nest.DateMath.Anchored(new DateTime(2020,1,23))
        .Add("1d")
        .Subtract(TimeSpan.FromMinutes(1)));

含有小数时间

Elasticsearch中的日期数学表达式不支持小数。

为了在 NEST 中更轻松地使用日期数学,从字符串,TimeSpan 和 double 转换而来的值会将小数部分转换为最大的整数值和单位,四舍五入到最接近的秒数。

Expect("now+1w").WhenSerializing(Nest.DateMath.Now.Add(TimeSpan.FromDays(7)));

Expect("now+1w").WhenSerializing(Nest.DateMath.Now.Add("1w"));

Expect("now+1w").WhenSerializing(Nest.DateMath.Now.Add(604800000));

Expect("now+7d").WhenSerializing(Nest.DateMath.Now.Add("7d"));

Expect("now+30h").WhenSerializing(Nest.DateMath.Now.Add(TimeSpan.FromHours(30)));

Expect("now+30h").WhenSerializing(Nest.DateMath.Now.Add("1.25d"));

Expect("now+90001s").WhenSerializing(
    Nest.DateMath.Now.Add(TimeSpan.FromHours(25).Add(TimeSpan.FromSeconds(1))));

Expect("now+90000s").WhenSerializing(
    Nest.DateMath.Now.Add(TimeSpan.FromHours(25).Add(TimeSpan.FromMilliseconds(1))));

Expect("now+1y").WhenSerializing(Nest.DateMath.Now.Add("1y"));

Expect("now+12M").WhenSerializing(Nest.DateMath.Now.Add("12M"));

Expect("now+18M").WhenSerializing(Nest.DateMath.Now.Add("1.5y"));

Expect("now+52w").WhenSerializing(Nest.DateMath.Now.Add(TimeSpan.FromDays(7 * 52)));

舍入

可以使用构造函数控制舍入,并传递一个值进行舍入:

Expect("now+2s").WhenSerializing(
    Nest.DateMath.Now.Add(new DateMathTime("2.5s", MidpointRounding.ToEven)));

Expect("now+3s").WhenSerializing(
    Nest.DateMath.Now.Add(new DateMathTime("2.5s", MidpointRounding.AwayFromZero)));

Expect("now+0s").WhenSerializing(
    Nest.DateMath.Now.Add(new DateMathTime(500, MidpointRounding.ToEven)));

Expect("now+1s").WhenSerializing(
    Nest.DateMath.Now.Add(new DateMathTime(500, MidpointRounding.AwayFromZero)));

相等性比较

DateMathTime 实现了 equality 和 comparison:

DateMathTime twoSeconds = new DateMathTime(2, DateMathTimeUnit.Second);
DateMathTime twoSecondsFromString = "2s";
DateMathTime twoSecondsFromTimeSpan = TimeSpan.FromSeconds(2);
DateMathTime twoSecondsFromDouble = 2000;

twoSeconds.Should().Be(twoSecondsFromString);
twoSeconds.Should().Be(twoSecondsFromTimeSpan);
twoSeconds.Should().Be(twoSecondsFromDouble);

DateMathTime threeSecondsFromString = "3s";
DateMathTime oneMinuteFromTimeSpan = TimeSpan.FromMinutes(1);

(threeSecondsFromString > twoSecondsFromString).Should().BeTrue();
(oneMinuteFromTimeSpan > threeSecondsFromString).Should().BeTrue();

年份和月份不包含确切值:

  • 一年大约365天
  • 一个月大约(365/12)天

DateMathTime oneYear = new DateMathTime(1, DateMathTimeUnit.Year);
DateMathTime oneYearFromString = "1y";
DateMathTime twelveMonths = new DateMathTime(12, DateMathTimeUnit.Month);
DateMathTime twelveMonthsFromString = "12M";

oneYear.Should().Be(oneYearFromString);
oneYear.Should().Be(twelveMonths);
twelveMonths.Should().Be(twelveMonthsFromString);

DateMathTime thirteenMonths = new DateMathTime(13, DateMathTimeUnit.Month);
DateMathTime thirteenMonthsFromString = "13M";
DateMathTime fiftyTwoWeeks = "52w";

(oneYear < thirteenMonths).Should().BeTrue();
(oneYear < thirteenMonthsFromString).Should().BeTrue();
(twelveMonths > fiftyTwoWeeks).Should().BeTrue();
(oneYear > fiftyTwoWeeks).Should().BeTrue();