| Akron | 8a67933 | 2016-11-01 16:18:55 +0100 | [diff] [blame] | 1 | package Krawfish::Koral::Corpus::Field; |
| Akron | c3ebcbb | 2018-04-02 16:30:18 +0200 | [diff] [blame] | 2 | use Krawfish::Util::String qw/normalize_nfkc/; |
| Akron | 7dc2a64 | 2017-08-02 15:39:49 +0200 | [diff] [blame] | 3 | use Krawfish::Koral::Corpus::FieldID; |
| Akron | 4335bdc | 2018-03-08 17:15:33 +0100 | [diff] [blame] | 4 | use Role::Tiny; |
| Akron | 8a67933 | 2016-11-01 16:18:55 +0100 | [diff] [blame] | 5 | use strict; |
| 6 | use warnings; | ||||
| 7 | |||||
| Akron | bc7dd43 | 2017-07-18 14:21:51 +0200 | [diff] [blame] | 8 | use constant DEBUG => 0; |
| Akron | 8a27142 | 2017-06-08 01:58:32 +0200 | [diff] [blame] | 9 | |
| Akron | 61e8bce | 2017-05-24 15:55:27 +0200 | [diff] [blame] | 10 | # TODO: |
| 11 | # - Check for valid parameters | ||||
| 12 | # - Only support positive terms | ||||
| 13 | # - Wrap in negative field! | ||||
| Akron | 8a67933 | 2016-11-01 16:18:55 +0100 | [diff] [blame] | 14 | |
| Akron | 93271d8 | 2016-11-24 09:18:41 +0100 | [diff] [blame] | 15 | sub type { |
| 16 | 'field'; | ||||
| 17 | }; | ||||
| 18 | |||||
| Akron | 2c6c716 | 2017-05-15 18:15:33 +0200 | [diff] [blame] | 19 | sub is_leaf { 1 }; |
| 20 | |||||
| Akron | 61e8bce | 2017-05-24 15:55:27 +0200 | [diff] [blame] | 21 | # Equal |
| Akron | 8a67933 | 2016-11-01 16:18:55 +0100 | [diff] [blame] | 22 | sub eq { |
| 23 | my $self = shift; | ||||
| 24 | $self->{match} = 'eq'; | ||||
| Akron | 1bbe60e | 2018-03-25 09:28:37 +0200 | [diff] [blame] | 25 | $self->value(shift) or return; |
| Akron | 8a67933 | 2016-11-01 16:18:55 +0100 | [diff] [blame] | 26 | return $self; |
| 27 | }; | ||||
| 28 | |||||
| Akron | 61e8bce | 2017-05-24 15:55:27 +0200 | [diff] [blame] | 29 | |
| 30 | # Not equal | ||||
| Akron | 8a67933 | 2016-11-01 16:18:55 +0100 | [diff] [blame] | 31 | sub ne { |
| 32 | my $self = shift; | ||||
| Akron | d3355ba | 2017-05-17 21:16:35 +0200 | [diff] [blame] | 33 | $self->{match} = 'eq'; |
| Akron | 373df82 | 2016-12-28 15:25:14 +0100 | [diff] [blame] | 34 | $self->is_negative(1); |
| Akron | 1bbe60e | 2018-03-25 09:28:37 +0200 | [diff] [blame] | 35 | $self->value(shift) or return; |
| Akron | 8a67933 | 2016-11-01 16:18:55 +0100 | [diff] [blame] | 36 | return $self; |
| 37 | }; | ||||
| 38 | |||||
| Akron | 61e8bce | 2017-05-24 15:55:27 +0200 | [diff] [blame] | 39 | |
| 40 | # Check for negativity | ||||
| Akron | d3355ba | 2017-05-17 21:16:35 +0200 | [diff] [blame] | 41 | sub is_negative { |
| 42 | my $self = shift; | ||||
| 43 | if (scalar @_ == 1) { | ||||
| 44 | $self->{negative} = shift; | ||||
| 45 | |||||
| 46 | my $op = $self->match; | ||||
| 47 | if ($self->{negative}) { | ||||
| 48 | |||||
| 49 | # Reverse operation | ||||
| 50 | if ($op eq 'eq') { | ||||
| 51 | $self->{match} = 'ne'; | ||||
| 52 | } | ||||
| 53 | elsif ($op eq 'contains') { | ||||
| 54 | $self->{match} = 'excludes' | ||||
| 55 | }; | ||||
| 56 | } | ||||
| 57 | |||||
| 58 | else { | ||||
| 59 | |||||
| 60 | # Reverse operation | ||||
| 61 | if ($op eq 'ne') { | ||||
| 62 | $self->{match} = 'eq'; | ||||
| 63 | } | ||||
| 64 | elsif ($op eq 'excludes') { | ||||
| 65 | $self->{match} = 'contains' | ||||
| 66 | }; | ||||
| 67 | }; | ||||
| 68 | }; | ||||
| 69 | return $self->{negative} // 0; | ||||
| 70 | }; | ||||
| 71 | |||||
| Akron | 4335bdc | 2018-03-08 17:15:33 +0100 | [diff] [blame] | 72 | |
| Akron | 76caadc | 2018-03-27 18:41:01 +0200 | [diff] [blame] | 73 | # Toggle negativity |
| 74 | sub toggle_negative { | ||||
| 75 | my $self = shift; | ||||
| 76 | my $op = $self->match; | ||||
| 77 | |||||
| 78 | # Reverse operation | ||||
| 79 | if ($op eq 'eq') { | ||||
| 80 | $self->{match} = 'ne'; | ||||
| 81 | $self->is_negative(1); | ||||
| 82 | } | ||||
| 83 | elsif ($op eq 'ne') { | ||||
| 84 | $self->{match} = 'eq'; | ||||
| 85 | $self->is_negative(0); | ||||
| 86 | } | ||||
| 87 | elsif ($op eq 'contains') { | ||||
| 88 | $self->{match} = 'excludes'; | ||||
| 89 | $self->is_negative(1); | ||||
| 90 | } | ||||
| 91 | elsif ($op eq 'excludes') { | ||||
| 92 | $self->{match} = 'contains'; | ||||
| 93 | $self->is_negative(0); | ||||
| 94 | } | ||||
| 95 | else { | ||||
| 96 | warn 'Unknown operation'; | ||||
| 97 | }; | ||||
| 98 | |||||
| 99 | return $self; | ||||
| 100 | }; | ||||
| 101 | |||||
| 102 | |||||
| Akron | 61e8bce | 2017-05-24 15:55:27 +0200 | [diff] [blame] | 103 | # Contains the value in multi-token field |
| Akron | 8a67933 | 2016-11-01 16:18:55 +0100 | [diff] [blame] | 104 | sub contains { |
| 105 | my $self = shift; | ||||
| 106 | $self->{match} = 'contains'; | ||||
| Akron | 1bbe60e | 2018-03-25 09:28:37 +0200 | [diff] [blame] | 107 | $self->value(shift) or return; |
| Akron | 8a67933 | 2016-11-01 16:18:55 +0100 | [diff] [blame] | 108 | return $self; |
| 109 | }; | ||||
| 110 | |||||
| Akron | 61e8bce | 2017-05-24 15:55:27 +0200 | [diff] [blame] | 111 | |
| 112 | # Does not contain the value in multi-token field | ||||
| Akron | 8a67933 | 2016-11-01 16:18:55 +0100 | [diff] [blame] | 113 | sub excludes { |
| 114 | my $self = shift; | ||||
| 115 | $self->{match} = 'excludes'; | ||||
| Akron | 1bbe60e | 2018-03-25 09:28:37 +0200 | [diff] [blame] | 116 | $self->value(shift) or return; |
| Akron | 8a67933 | 2016-11-01 16:18:55 +0100 | [diff] [blame] | 117 | return $self; |
| 118 | }; | ||||
| 119 | |||||
| Akron | 290f59f | 2017-08-17 21:55:07 +0200 | [diff] [blame] | 120 | sub can_toggle_negativity { |
| 121 | ... | ||||
| 122 | }; | ||||
| Akron | 61e8bce | 2017-05-24 15:55:27 +0200 | [diff] [blame] | 123 | |
| Akron | c3ebcbb | 2018-04-02 16:30:18 +0200 | [diff] [blame] | 124 | sub normalize { |
| 125 | my $self = shift; | ||||
| 126 | $self->{value} = normalize_nfkc($self->value) if $self->value; | ||||
| 127 | return $self; | ||||
| 128 | }; | ||||
| Akron | bc7dd43 | 2017-07-18 14:21:51 +0200 | [diff] [blame] | 129 | |
| Akron | 4335bdc | 2018-03-08 17:15:33 +0100 | [diff] [blame] | 130 | sub optimize { |
| 131 | 'Irrelevant'; | ||||
| 132 | }; | ||||
| 133 | |||||
| Akron | 7dc2a64 | 2017-08-02 15:39:49 +0200 | [diff] [blame] | 134 | |
| 135 | sub operands { | ||||
| Akron | 3ee72ec | 2018-03-26 22:49:36 +0200 | [diff] [blame] | 136 | warn 'operands() called in leaf node'; |
| Akron | 7dc2a64 | 2017-08-02 15:39:49 +0200 | [diff] [blame] | 137 | }; |
| 138 | |||||
| 139 | |||||
| Akron | 3ee72ec | 2018-03-26 22:49:36 +0200 | [diff] [blame] | 140 | sub identify { |
| 141 | warn 'Override'; | ||||
| 142 | }; | ||||
| 143 | |||||
| Akron | ca4cd54 | 2018-03-09 18:20:56 +0100 | [diff] [blame] | 144 | # TODO: Support existence |
| Akron | 8a67933 | 2016-11-01 16:18:55 +0100 | [diff] [blame] | 145 | sub match { |
| Akron | ca4cd54 | 2018-03-09 18:20:56 +0100 | [diff] [blame] | 146 | my $self = shift; |
| 147 | if (@_) { | ||||
| 148 | $self->{match} = shift; | ||||
| 149 | return $self; | ||||
| 150 | }; | ||||
| 151 | return ($self->{match} // 'eq'); | ||||
| Akron | 8a67933 | 2016-11-01 16:18:55 +0100 | [diff] [blame] | 152 | }; |
| 153 | |||||
| Akron | d3355ba | 2017-05-17 21:16:35 +0200 | [diff] [blame] | 154 | |
| Akron | 8a67933 | 2016-11-01 16:18:55 +0100 | [diff] [blame] | 155 | sub key { |
| 156 | $_[0]->{key}; | ||||
| 157 | }; | ||||
| 158 | |||||
| 159 | |||||
| 160 | sub value { | ||||
| Akron | 1bbe60e | 2018-03-25 09:28:37 +0200 | [diff] [blame] | 161 | my $self = shift; |
| 162 | if (@_) { | ||||
| 163 | $self->{value} = shift; | ||||
| 164 | return $self; | ||||
| 165 | }; | ||||
| 166 | return $self->{value}; | ||||
| Akron | 8a67933 | 2016-11-01 16:18:55 +0100 | [diff] [blame] | 167 | }; |
| 168 | |||||
| Akron | d3355ba | 2017-05-17 21:16:35 +0200 | [diff] [blame] | 169 | |
| Akron | 8a67933 | 2016-11-01 16:18:55 +0100 | [diff] [blame] | 170 | sub to_koral_fragment { |
| 171 | my $self = shift; | ||||
| 172 | |||||
| 173 | my $field = { | ||||
| 174 | '@type' => 'koral:field', | ||||
| 175 | key => $self->key, | ||||
| 176 | match => 'match:' . $self->match, | ||||
| Akron | 93271d8 | 2016-11-24 09:18:41 +0100 | [diff] [blame] | 177 | type => 'type:' . $self->key_type |
| Akron | 8a67933 | 2016-11-01 16:18:55 +0100 | [diff] [blame] | 178 | }; |
| 179 | |||||
| 180 | # No value defined | ||||
| 181 | unless ($self->value) { | ||||
| 182 | |||||
| 183 | # Check for existence | ||||
| 184 | if ($field->{match} ne 'match:contains' || | ||||
| 185 | $field->{match} ne 'match:excludes') { | ||||
| 186 | |||||
| 187 | # Set to existence default | ||||
| 188 | $field->{match} = 'match:contains'; | ||||
| 189 | }; | ||||
| 190 | } | ||||
| 191 | |||||
| 192 | # Set value | ||||
| 193 | else { | ||||
| 194 | $field->{value} = $self->value; | ||||
| 195 | }; | ||||
| 196 | |||||
| 197 | return $field; | ||||
| 198 | }; | ||||
| 199 | |||||
| 200 | |||||
| Akron | e21cb60 | 2018-03-10 11:35:52 +0100 | [diff] [blame] | 201 | # Stringification |
| Akron | ded01ae | 2016-11-23 13:43:54 +0100 | [diff] [blame] | 202 | sub to_string { |
| 203 | my $self = shift; | ||||
| Akron | 2c6c716 | 2017-05-15 18:15:33 +0200 | [diff] [blame] | 204 | |
| 205 | return 0 if $self->is_null; | ||||
| 206 | |||||
| Akron | 38ec305 | 2018-03-08 19:50:18 +0100 | [diff] [blame] | 207 | my $str = ''; # $self->key_type . ':'; |
| 208 | $str .= $self->{key}; | ||||
| Akron | ded01ae | 2016-11-23 13:43:54 +0100 | [diff] [blame] | 209 | my $op = $self->match; |
| Akron | 22b6858 | 2017-01-19 12:05:21 +0100 | [diff] [blame] | 210 | |
| 211 | unless ($self->{value}) { | ||||
| 212 | return $str unless $op eq 'excludes'; | ||||
| Akron | 38ec305 | 2018-03-08 19:50:18 +0100 | [diff] [blame] | 213 | return $str; # KEY_PREF . $str; |
| Akron | 22b6858 | 2017-01-19 12:05:21 +0100 | [diff] [blame] | 214 | }; |
| 215 | |||||
| Akron | e21cb60 | 2018-03-10 11:35:52 +0100 | [diff] [blame] | 216 | $str .= $self->match_short; |
| 217 | |||||
| Akron | 3c89668 | 2018-03-24 12:00:55 +0100 | [diff] [blame] | 218 | return $str . $self->value_string; |
| Akron | e21cb60 | 2018-03-10 11:35:52 +0100 | [diff] [blame] | 219 | }; |
| 220 | |||||
| 221 | |||||
| Akron | 3c89668 | 2018-03-24 12:00:55 +0100 | [diff] [blame] | 222 | sub value_string { |
| 223 | $_[0]->{value}; | ||||
| 224 | }; | ||||
| 225 | |||||
| Akron | e21cb60 | 2018-03-10 11:35:52 +0100 | [diff] [blame] | 226 | sub match_short { |
| 227 | my $self = shift; | ||||
| 228 | my $op = $self->match; | ||||
| Akron | ded01ae | 2016-11-23 13:43:54 +0100 | [diff] [blame] | 229 | if ($op eq 'eq') { |
| Akron | e21cb60 | 2018-03-10 11:35:52 +0100 | [diff] [blame] | 230 | return '='; |
| Akron | ded01ae | 2016-11-23 13:43:54 +0100 | [diff] [blame] | 231 | } |
| 232 | elsif ($op eq 'ne') { | ||||
| Akron | e21cb60 | 2018-03-10 11:35:52 +0100 | [diff] [blame] | 233 | return '!='; |
| Akron | ded01ae | 2016-11-23 13:43:54 +0100 | [diff] [blame] | 234 | } |
| 235 | elsif ($op eq 'geq') { | ||||
| Akron | e21cb60 | 2018-03-10 11:35:52 +0100 | [diff] [blame] | 236 | return '>='; |
| Akron | ded01ae | 2016-11-23 13:43:54 +0100 | [diff] [blame] | 237 | } |
| 238 | elsif ($op eq 'leq') { | ||||
| Akron | e21cb60 | 2018-03-10 11:35:52 +0100 | [diff] [blame] | 239 | return '<='; |
| Akron | ded01ae | 2016-11-23 13:43:54 +0100 | [diff] [blame] | 240 | } |
| 241 | elsif ($op eq 'contains') { | ||||
| Akron | e21cb60 | 2018-03-10 11:35:52 +0100 | [diff] [blame] | 242 | return '~' |
| Akron | ded01ae | 2016-11-23 13:43:54 +0100 | [diff] [blame] | 243 | } |
| 244 | elsif ($op eq 'excludes') { | ||||
| Akron | e21cb60 | 2018-03-10 11:35:52 +0100 | [diff] [blame] | 245 | return '!='; |
| Akron | ded01ae | 2016-11-23 13:43:54 +0100 | [diff] [blame] | 246 | }; |
| Akron | e21cb60 | 2018-03-10 11:35:52 +0100 | [diff] [blame] | 247 | return '?'; |
| 248 | }; | ||||
| 249 | |||||
| 250 | |||||
| 251 | # Stringification for sorting | ||||
| 252 | sub to_sort_string { | ||||
| Akron | 1bbe60e | 2018-03-25 09:28:37 +0200 | [diff] [blame] | 253 | # TODO: |
| 254 | # Maybe date, string etc. implementations are generalizable! | ||||
| Akron | e21cb60 | 2018-03-10 11:35:52 +0100 | [diff] [blame] | 255 | return $_[0]->to_string; |
| Akron | ded01ae | 2016-11-23 13:43:54 +0100 | [diff] [blame] | 256 | }; |
| 257 | |||||
| Akron | ce7f228 | 2018-03-12 13:46:01 +0100 | [diff] [blame] | 258 | sub is_relational { |
| Akron | ce7f228 | 2018-03-12 13:46:01 +0100 | [diff] [blame] | 259 | return 0; |
| 260 | }; | ||||
| 261 | |||||
| Akron | ded01ae | 2016-11-23 13:43:54 +0100 | [diff] [blame] | 262 | sub to_term { |
| 263 | my $self = shift; | ||||
| 264 | my $term = $self->to_string; | ||||
| 265 | $term =~ s/^([^=!><~\?]+?)(?:[!<>]?[=~\?])/$1:/; | ||||
| 266 | return $term; | ||||
| 267 | }; | ||||
| 268 | |||||
| Akron | 5756955 | 2017-06-20 13:31:25 +0200 | [diff] [blame] | 269 | |
| Akron | acde0ba | 2017-12-08 14:05:13 +0100 | [diff] [blame] | 270 | sub from_koral { |
| 271 | ... | ||||
| 272 | }; | ||||
| 273 | |||||
| Akron | 5756955 | 2017-06-20 13:31:25 +0200 | [diff] [blame] | 274 | sub to_neutral { |
| 275 | $_[0]->to_term; | ||||
| 276 | }; | ||||
| 277 | |||||
| 278 | |||||
| Akron | 4335bdc | 2018-03-08 17:15:33 +0100 | [diff] [blame] | 279 | |
| Akron | 8a67933 | 2016-11-01 16:18:55 +0100 | [diff] [blame] | 280 | 1; |