blob: 57e6973f0a05f2911c7faa0490059573816dc230 [file] [log] [blame]
Akron4335bdc2018-03-08 17:15:33 +01001package Krawfish::Koral::Corpus::Field::Date;
Akron4335bdc2018-03-08 17:15:33 +01002use strict;
3use warnings;
Akroncb0bac12018-04-12 20:28:21 +02004use Krawfish::Util::Constants qw/:PREFIX :RANGE/;
5use Krawfish::Log;
6use Krawfish::Koral::Corpus::DateRange;
7use Role::Tiny::With;
Akron4335bdc2018-03-08 17:15:33 +01008
Akron62748022018-03-15 19:40:45 +01009with 'Krawfish::Koral::Corpus::Field::Relational';
Akronbfa1cbd2018-04-10 12:43:25 +020010with 'Krawfish::Koral::Corpus::Field';
Akron4335bdc2018-03-08 17:15:33 +010011with 'Krawfish::Koral::Corpus';
12
Akroncb0bac12018-04-12 20:28:21 +020013# This supports range queries on special
14# date and int fields in the dictionary.
15
16# The implementation for dates has to recognize
17# that not only ranges are required to be queried, but also
18# stored.
19
20# RESTRICTION:
21# - Currently this is restricted to dates!
22# - currently this finds all intersecting dates!
Akron320c67c2018-04-09 17:26:04 +020023
24# TODO:
25# Convert the strings to RFC3339, as this is a sortable
26# date format.
27
Akron74035922018-03-27 20:33:13 +020028use constant DEBUG => 0;
Akron4335bdc2018-03-08 17:15:33 +010029
Akron99da8692018-04-08 15:33:19 +020030# TODO:
31# A date should probably have a different prefix
Akron4335bdc2018-03-08 17:15:33 +010032
Akrone984d642018-03-14 15:05:43 +010033# TODO:
34# Compare with de.ids_mannheim.korap.util.KrillDate
35
Akron1bbe60e2018-03-25 09:28:37 +020036# Construct new date field object
Akron4335bdc2018-03-08 17:15:33 +010037sub new {
38 my $class = shift;
39 bless {
Akrone984d642018-03-14 15:05:43 +010040 key => shift,
41 parsed => undef,
42 year => undef,
43 month => undef,
44 day => undef
Akron4335bdc2018-03-08 17:15:33 +010045 }, $class;
46};
47
Akron4335bdc2018-03-08 17:15:33 +010048sub key_type {
49 'date';
50};
51
Akrone984d642018-03-14 15:05:43 +010052sub year {
Akron1bbe60e2018-03-25 09:28:37 +020053 $_[0]->{year};
Akrone984d642018-03-14 15:05:43 +010054};
55
56sub month {
Akron1bbe60e2018-03-25 09:28:37 +020057 $_[0]->{month} // 0;
Akrone984d642018-03-14 15:05:43 +010058};
59
60sub day {
Akron1bbe60e2018-03-25 09:28:37 +020061 $_[0]->{day} // 0;
Akrone984d642018-03-14 15:05:43 +010062};
63
Akronbfa1cbd2018-04-10 12:43:25 +020064# Compare against another field value
65sub value_eq {
66 my ($self, $other) = @_;
67 if ($self->year == $other->year &&
68 $self->month == $other->month &&
69 $self->day == $other->day) {
70 return 1;
71 };
72 return 0;
73};
74
75
76sub value_gt {
Akrone984d642018-03-14 15:05:43 +010077 my ($self, $other) = @_;
78 if ($self->year > $other->year) {
79 return 1;
80 }
81 elsif ($self->year < $other->year) {
82 return 0;
83 }
Akronbfa1cbd2018-04-10 12:43:25 +020084 elsif (!$self->month && !$other->month) {
85 return 0; # It's equal
86 }
87 elsif ($self->month && !$other->month) {
Akrone984d642018-03-14 15:05:43 +010088 return 1;
89 }
Akronbfa1cbd2018-04-10 12:43:25 +020090 elsif (!$self->month && $other->month) {
91 return 0;
92 }
Akrone984d642018-03-14 15:05:43 +010093 elsif ($self->month > $other->month) {
94 return 1;
95 }
96 elsif ($self->month < $other->month) {
97 return 0;
98 }
Akronbfa1cbd2018-04-10 12:43:25 +020099 elsif (!$self->day && !$other->day) {
100 return 0; # It's equal
101 }
102 elsif ($self->day && !$other->day) {
Akrone984d642018-03-14 15:05:43 +0100103 return 1;
104 }
Akronbfa1cbd2018-04-10 12:43:25 +0200105 elsif (!$self->day && $other->day) {
106 return 0;
107 }
Akrone984d642018-03-14 15:05:43 +0100108 elsif ($self->day > $other->day) {
109 return 1;
110 };
111 return 0;
112};
113
114
Akronbfa1cbd2018-04-10 12:43:25 +0200115sub value_lt {
Akron320c67c2018-04-09 17:26:04 +0200116 my ($self, $other) = @_;
117 if ($self->year < $other->year) {
118 return 1;
119 }
120 elsif ($self->year > $other->year) {
121 return 0;
122 }
Akronbfa1cbd2018-04-10 12:43:25 +0200123 elsif (!$self->month && !$other->month) {
124 return 0; # It's equal
125 }
126 elsif ($self->month && !$other->month) {
Akron320c67c2018-04-09 17:26:04 +0200127 return 0;
128 }
Akronbfa1cbd2018-04-10 12:43:25 +0200129 elsif (!$self->month && $other->month) {
130 return 1;
131 }
Akron320c67c2018-04-09 17:26:04 +0200132 elsif ($self->month < $other->month) {
133 return 1;
134 }
135 elsif ($self->month > $other->month) {
136 return 0;
137 }
Akronbfa1cbd2018-04-10 12:43:25 +0200138 elsif (!$self->day && !$other->day) {
139 return 0; # It's equal
140 }
141 elsif ($self->day && !$other->day) {
Akron320c67c2018-04-09 17:26:04 +0200142 return 0;
143 }
Akronbfa1cbd2018-04-10 12:43:25 +0200144 elsif (!$self->day && $other->day) {
145 return 1;
146 }
Akron320c67c2018-04-09 17:26:04 +0200147 elsif ($self->day < $other->day) {
148 return 1;
149 };
150 return 0;
151};
152
153
154
Akron3ee72ec2018-03-26 22:49:36 +0200155# Translate all terms to term ids
156sub identify {
157 my ($self, $dict) = @_;
158
159 if ($self->match_short ne '=') {
160 warn 'Relational matches not supported yet';
161 return;
162 };
163
164 my $term = $self->to_term;
165
166 print_log('kq_date', "Translate term $term to term_id") if DEBUG;
167
168 my $term_id = $dict->term_id_by_term(DATE_FIELD_PREF . $term);
169
170 return $self->builder->nowhere unless defined $term_id;
171
172 return Krawfish::Koral::Corpus::FieldID->new($term_id);
173};
174
175
Akron1bbe60e2018-03-25 09:28:37 +0200176sub value {
Akrone984d642018-03-14 15:05:43 +0100177 my $self = shift;
Akron1bbe60e2018-03-25 09:28:37 +0200178 if (@_) {
179 $self->{value} = shift;
180 if ($self->{value} =~ /^(\d{4})(?:-?(\d{2})(?:-?(\d{2}))?)?$/) {
181 $self->{year} = ($1 + 0) if $1;
182 $self->{month} = ($2 + 0) if $2;
183 $self->{day} = ($3 + 0) if $3;
184 return $self;
185 };
186 return;
187 };
188 return $self->{value};
Akrone984d642018-03-14 15:05:43 +0100189};
190
Akron1bbe60e2018-03-25 09:28:37 +0200191
Akroncb0bac12018-04-12 20:28:21 +0200192# Serialize the value string
193# Accepts an optional granularity value
194# with: 0 = all
195# 1 = till month
196# 2 = till year
Akron3c896682018-03-24 12:00:55 +0100197sub value_string {
Akroncb0bac12018-04-12 20:28:21 +0200198 my ($self, $granularity) = @_;
199 $granularity //= 0;
Akron1bbe60e2018-03-25 09:28:37 +0200200 my $str = '';
201 $str .= $self->year;
Akroncb0bac12018-04-12 20:28:21 +0200202 if ($self->month && $granularity <= 1) {
Akron3c896682018-03-24 12:00:55 +0100203 $str .= '-' . _zero($self->month);
Akroncb0bac12018-04-12 20:28:21 +0200204 if ($self->day && $granularity <= 0) {
Akron3c896682018-03-24 12:00:55 +0100205 $str .= '-' . _zero($self->day);
206 };
207 }
208 return $str;
209};
210
Akroncb0bac12018-04-12 20:28:21 +0200211
Akron3c896682018-03-24 12:00:55 +0100212sub _zero {
213 if ($_[0] < 10) {
214 return '0' . $_[0]
215 };
216 return $_[0];
217};
218
219
Akron1bbe60e2018-03-25 09:28:37 +0200220# Stringification for sorting
221# TODO:
222# This may fail in case key_type and/or
223# value may contain ':' - so this should be
224# ensured!
225sub to_sort_string {
226 my $self = shift;
227 return 0 if $self->is_null;
228
229 my $str = $self->key_type . ':';
230 $str .= $self->key . ':';
231 $str .= ($self->value_string // '') . ':';
232 $str .= $self->match_short;
233 return $str;
234};
235
236
Akroncb0bac12018-04-12 20:28:21 +0200237# Convert date to query term
238# This will represent an intersection
239# with all dates or dateranges intersecting
240# with the current date
241sub to_intersecting_terms {
242 my $self = shift;
243 my @terms;
244
245 # Match the whole granularity subtree
246 # Either the day, the month or the year
247 # e.g. 2015], 2015-11], 2015-11-14]
248 if ($self->day) {
249 push @terms,
250 $self->builder->string($self->key)->eq(
251 $self->value_string(0) . RANGE_ALL_POST
252 );
253 };
254
255 if ($self->month) {
256 push @terms,
257 $self->builder->string($self->key)->eq(
258 $self->value_string(1) . RANGE_ALL_POST
259 );
260 };
261
262 push @terms,
263 $self->builder->string($self->key)->eq(
264 $self->value_string(2) . RANGE_ALL_POST
265 );
266
267 return @terms;
268};
269
270
271# Spawn an intersecting date range query
272sub intersect {
273 my $self = shift;
274 my ($first, $second) = @_;
275
276 # Make this a DateRange query
277 if ($second) {
278 my $cb = $self->builder;
279
280 return Krawfish::Koral::Corpus::DateRange->new(
281 $cb->date($self->key)->geq($first),
282 $cb->date($self->key)->leq($second)
283 );
284 };
285
286 $self->{match} = 'intersect';
287 $self->value(shift) or return;
288
289 return $self;
290};
291
292
293
294
Akron4335bdc2018-03-08 17:15:33 +01002951;