summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2017-02-06 20:58:37 -0500
committerKelly Rauchenberger <fefferburbia@gmail.com>2017-02-06 20:58:37 -0500
commitf1f67e62cebb4144f0599196263cd93b41fa972e (patch)
tree5d0f8fceeec63f47fb85846f5568bcb36f4a975f /lib
parent6cc23ba387d0813b801ba094709673a61bac889c (diff)
downloadverbly-f1f67e62cebb4144f0599196263cd93b41fa972e.tar.gz
verbly-f1f67e62cebb4144f0599196263cd93b41fa972e.tar.bz2
verbly-f1f67e62cebb4144f0599196263cd93b41fa972e.zip
Made pronunciation::rhymes join dynamic
This involved adding a new type of filter; one that compares (currently
only equality and inequality) a field with another field located in an
enclosing join context.

In the process, it was discovered that simplifying the lemma::forms join
field earlier actually made some queries return inaccurate results
because the inflection of the form was being ignored and anything in the
lemma would be used because of the inner join. Because the existing
condition join did not allow for the condition field to be on the from
side of the join, two things were done: a condition version of
joinThrough was made, and lemma was finally eliminated as a top-level
object, replaced instead with a condition join between word and form
through lemmas_forms.

Queries are also now grouped by the first select field (assumed to be
the primary ID) of the top table, in order to eliminate duplicates
created by inner joins, so that there is a uniform distribution between
results for random queries.

Created a database index on pronunciations(rhyme) which decreases query
time for rhyming filters. The new database version is
backwards-compatible because no data or structure changed.
Diffstat (limited to 'lib')
-rw-r--r--lib/binding.cpp71
-rw-r--r--lib/binding.h14
-rw-r--r--lib/database.cpp5
-rw-r--r--lib/database.h3
-rw-r--r--lib/enums.h5
-rw-r--r--lib/field.h61
-rw-r--r--lib/filter.cpp201
-rw-r--r--lib/filter.h8
-rw-r--r--lib/form.cpp6
-rw-r--r--lib/form.h2
-rw-r--r--lib/frame.cpp2
-rw-r--r--lib/lemma.cpp67
-rw-r--r--lib/lemma.h97
-rw-r--r--lib/pronunciation.cpp37
-rw-r--r--lib/pronunciation.h23
-rw-r--r--lib/query.h5
-rw-r--r--lib/statement.cpp232
-rw-r--r--lib/statement.h9
-rw-r--r--lib/token.cpp2
-rw-r--r--lib/verbly.h1
-rw-r--r--lib/word.cpp65
-rw-r--r--lib/word.h19
22 files changed, 571 insertions, 364 deletions
diff --git a/lib/binding.cpp b/lib/binding.cpp index 349cd6f..0b58785 100644 --- a/lib/binding.cpp +++ b/lib/binding.cpp
@@ -24,6 +24,14 @@ namespace verbly {
24 break; 24 break;
25 } 25 }
26 26
27 case type::field:
28 {
29 new(&field_.table_) std::string(other.field_.table_);
30 new(&field_.column_) std::string(other.field_.column_);
31
32 break;
33 }
34
27 case type::invalid: 35 case type::invalid:
28 { 36 {
29 break; 37 break;
@@ -50,6 +58,8 @@ namespace verbly {
50 type tempType = first.type_; 58 type tempType = first.type_;
51 int tempInteger; 59 int tempInteger;
52 std::string tempString; 60 std::string tempString;
61 std::string tempTable;
62 std::string tempColumn;
53 63
54 switch (first.type_) 64 switch (first.type_)
55 { 65 {
@@ -67,6 +77,14 @@ namespace verbly {
67 break; 77 break;
68 } 78 }
69 79
80 case type::field:
81 {
82 tempTable = std::move(first.field_.table_);
83 tempColumn = std::move(first.field_.column_);
84
85 break;
86 }
87
70 case type::invalid: 88 case type::invalid:
71 { 89 {
72 break; 90 break;
@@ -93,6 +111,14 @@ namespace verbly {
93 break; 111 break;
94 } 112 }
95 113
114 case type::field:
115 {
116 new(&first.field_.table_) std::string(std::move(second.field_.table_));
117 new(&first.field_.column_) std::string(std::move(second.field_.column_));
118
119 break;
120 }
121
96 case type::invalid: 122 case type::invalid:
97 { 123 {
98 break; 124 break;
@@ -119,6 +145,14 @@ namespace verbly {
119 break; 145 break;
120 } 146 }
121 147
148 case type::field:
149 {
150 new(&first.field_.table_) std::string(std::move(tempTable));
151 new(&first.field_.column_) std::string(std::move(tempColumn));
152
153 break;
154 }
155
122 case type::invalid: 156 case type::invalid:
123 { 157 {
124 break; 158 break;
@@ -138,6 +172,15 @@ namespace verbly {
138 break; 172 break;
139 } 173 }
140 174
175 case type::field:
176 {
177 using string_type = std::string;
178 field_.table_.~string_type();
179 field_.column_.~string_type();
180
181 break;
182 }
183
141 case type::integer: 184 case type::integer:
142 case type::invalid: 185 case type::invalid:
143 { 186 {
@@ -164,7 +207,7 @@ namespace verbly {
164 207
165 binding::binding(std::string arg) : type_(type::string) 208 binding::binding(std::string arg) : type_(type::string)
166 { 209 {
167 new(&string_) std::string(arg); 210 new(&string_) std::string(std::move(arg));
168 } 211 }
169 212
170 std::string binding::getString() const 213 std::string binding::getString() const
@@ -177,4 +220,30 @@ namespace verbly {
177 return string_; 220 return string_;
178 } 221 }
179 222
223 binding::binding(std::string table, std::string column) : type_(type::field)
224 {
225 new(&field_.table_) std::string(std::move(table));
226 new(&field_.column_) std::string(std::move(column));
227 }
228
229 std::string binding::getTable() const
230 {
231 if (type_ != type::field)
232 {
233 throw std::domain_error("binding::getTable called on non-field binding");
234 }
235
236 return field_.table_;
237 }
238
239 std::string binding::getColumn() const
240 {
241 if (type_ != type::field)
242 {
243 throw std::domain_error("binding::getColumn called on non-field binding");
244 }
245
246 return field_.column_;
247 }
248
180}; 249};
diff --git a/lib/binding.h b/lib/binding.h index 5578a09..5da1e71 100644 --- a/lib/binding.h +++ b/lib/binding.h
@@ -10,7 +10,8 @@ namespace verbly {
10 enum class type { 10 enum class type {
11 invalid, 11 invalid,
12 integer, 12 integer,
13 string 13 string,
14 field
14 }; 15 };
15 16
16 // Default constructor 17 // Default constructor
@@ -55,11 +56,22 @@ namespace verbly {
55 56
56 std::string getString() const; 57 std::string getString() const;
57 58
59 // Field
60
61 binding(std::string table, std::string column);
62
63 std::string getTable() const;
64 std::string getColumn() const;
65
58 private: 66 private:
59 67
60 union { 68 union {
61 int integer_; 69 int integer_;
62 std::string string_; 70 std::string string_;
71 struct {
72 std::string table_;
73 std::string column_;
74 } field_;
63 }; 75 };
64 76
65 type type_ = type::invalid; 77 type type_ = type::invalid;
diff --git a/lib/database.cpp b/lib/database.cpp index c7b37ec..a4d056d 100644 --- a/lib/database.cpp +++ b/lib/database.cpp
@@ -61,11 +61,6 @@ namespace verbly {
61 return query<part>(*this, ppdb_, std::move(where), std::move(sortOrder), limit); 61 return query<part>(*this, ppdb_, std::move(where), std::move(sortOrder), limit);
62 } 62 }
63 63
64 query<lemma> database::lemmas(filter where, order sortOrder, int limit) const
65 {
66 return query<lemma>(*this, ppdb_, std::move(where), std::move(sortOrder), limit);
67 }
68
69 query<form> database::forms(filter where, order sortOrder, int limit) const 64 query<form> database::forms(filter where, order sortOrder, int limit) const
70 { 65 {
71 return query<form>(*this, ppdb_, std::move(where), std::move(sortOrder), limit); 66 return query<form>(*this, ppdb_, std::move(where), std::move(sortOrder), limit);
diff --git a/lib/database.h b/lib/database.h index 5567061..ef5ff87 100644 --- a/lib/database.h +++ b/lib/database.h
@@ -9,7 +9,6 @@
9#include "word.h" 9#include "word.h"
10#include "frame.h" 10#include "frame.h"
11#include "part.h" 11#include "part.h"
12#include "lemma.h"
13#include "form.h" 12#include "form.h"
14#include "pronunciation.h" 13#include "pronunciation.h"
15#include "order.h" 14#include "order.h"
@@ -56,8 +55,6 @@ namespace verbly {
56 55
57 query<part> parts(filter where, order sortOrder = {}, int limit = 1) const; 56 query<part> parts(filter where, order sortOrder = {}, int limit = 1) const;
58 57
59 query<lemma> lemmas(filter where, order sortOrder = {}, int limit = 1) const;
60
61 query<form> forms(filter where, order sortOrder = {}, int limit = 1) const; 58 query<form> forms(filter where, order sortOrder = {}, int limit = 1) const;
62 59
63 query<pronunciation> pronunciations(filter where, order sortOrder = {}, int limit = 1) const; 60 query<pronunciation> pronunciations(filter where, order sortOrder = {}, int limit = 1) const;
diff --git a/lib/enums.h b/lib/enums.h index 2646fa4..55a1bda 100644 --- a/lib/enums.h +++ b/lib/enums.h
@@ -35,9 +35,8 @@ namespace verbly {
35 word = 1, 35 word = 1,
36 frame = 2, 36 frame = 2,
37 part = 3, 37 part = 3,
38 lemma = 4, 38 form = 4,
39 form = 5, 39 pronunciation = 5
40 pronunciation = 6
41 }; 40 };
42 41
43 enum class part_type { 42 enum class part_type {
diff --git a/lib/field.h b/lib/field.h index bec0618..f799900 100644 --- a/lib/field.h +++ b/lib/field.h
@@ -19,6 +19,7 @@ namespace verbly {
19 join, 19 join,
20 join_where, 20 join_where,
21 join_through, 21 join_through,
22 join_through_where,
22 hierarchal_join 23 hierarchal_join
23 }; 24 };
24 25
@@ -100,11 +101,11 @@ namespace verbly {
100 object obj, 101 object obj,
101 const char* name, 102 const char* name,
102 object joinWith, 103 object joinWith,
103 const field& conditionField, 104 const char* conditionColumn,
104 int conditionValue, 105 int conditionValue,
105 bool nullable = false) 106 bool nullable = false)
106 { 107 {
107 return field(obj, type::join_where, name, nullable, 0, joinWith, 0, 0, 0, &conditionField, conditionValue); 108 return field(obj, type::join_where, name, nullable, 0, joinWith, 0, 0, 0, conditionColumn, conditionValue);
108 } 109 }
109 110
110 static field joinThrough( 111 static field joinThrough(
@@ -129,6 +130,19 @@ namespace verbly {
129 return field(obj, type::join_through, name, true, joinTable, joinWith, foreignColumn, joinColumn, foreignJoinColumn); 130 return field(obj, type::join_through, name, true, joinTable, joinWith, foreignColumn, joinColumn, foreignJoinColumn);
130 } 131 }
131 132
133 static field joinThroughWhere(
134 object obj,
135 const char* name,
136 object joinWith,
137 const char* joinTable,
138 const char* foreignColumn,
139 const char* conditionColumn,
140 int conditionValue,
141 bool nullable = false)
142 {
143 return field(obj, type::join_through_where, name, nullable, joinTable, joinWith, foreignColumn, name, foreignColumn, conditionColumn, conditionValue);
144 }
145
132 static field selfJoin( 146 static field selfJoin(
133 object obj, 147 object obj,
134 const char* name, 148 const char* name,
@@ -166,6 +180,7 @@ namespace verbly {
166 return ((type_ == type::join) 180 return ((type_ == type::join)
167 || (type_ == type::join_where) 181 || (type_ == type::join_where)
168 || (type_ == type::join_through) 182 || (type_ == type::join_through)
183 || (type_ == type::join_through_where)
169 || (type_ == type::hierarchal_join)); 184 || (type_ == type::hierarchal_join));
170 } 185 }
171 186
@@ -195,7 +210,7 @@ namespace verbly {
195 { 210 {
196 return (type_ == type::hierarchal_join) 211 return (type_ == type::hierarchal_join)
197 ? object_ 212 ? object_
198 : ((type_ == type::join) || (type_ == type::join_where) || (type_ == type::join_through)) 213 : ((type_ == type::join) || (type_ == type::join_where) || (type_ == type::join_through) || (type_ == type::join_through_where))
199 ? joinObject_ 214 ? joinObject_
200 : throw std::domain_error("Non-join fields don't have join objects"); 215 : throw std::domain_error("Non-join fields don't have join objects");
201 } 216 }
@@ -205,40 +220,40 @@ namespace verbly {
205 const char* getForeignColumn() const 220 const char* getForeignColumn() const
206 { 221 {
207 // We ignore hierarchal joins because they are always self joins. 222 // We ignore hierarchal joins because they are always self joins.
208 return (type_ == type::join_through) 223 return ((type_ == type::join_through) || (type_ == type::join_through_where))
209 ? foreignColumn_ 224 ? foreignColumn_
210 : throw std::domain_error("Only many-to-many join fields have a foreign column"); 225 : throw std::domain_error("Only many-to-many join fields have a foreign column");
211 } 226 }
212 227
213 const char* getJoinColumn() const 228 const char* getJoinColumn() const
214 { 229 {
215 return ((type_ == type::join_through) || (type_ == type::hierarchal_join)) 230 return ((type_ == type::join_through) || (type_ == type::join_through_where) || (type_ == type::hierarchal_join))
216 ? joinColumn_ 231 ? joinColumn_
217 : throw std::domain_error("Only many-to-many join fields have a join column"); 232 : throw std::domain_error("Only many-to-many join fields have a join column");
218 } 233 }
219 234
220 const char* getForeignJoinColumn() const 235 const char* getForeignJoinColumn() const
221 { 236 {
222 return ((type_ == type::join_through) || (type_ == type::hierarchal_join)) 237 return ((type_ == type::join_through) || (type_ == type::join_through_where) || (type_ == type::hierarchal_join))
223 ? foreignJoinColumn_ 238 ? foreignJoinColumn_
224 : throw std::domain_error("Only many-to-many join fields have a foreign join column"); 239 : throw std::domain_error("Only many-to-many join fields have a foreign join column");
225 } 240 }
226 241
227 // Condition joins 242 // Condition joins
228 243
229 const field& getConditionField() const 244 const char* getConditionColumn() const
230 { 245 {
231 if (type_ == type::join_where) 246 if ((type_ == type::join_where) || (type_ == type::join_through_where))
232 { 247 {
233 return *conditionField_; 248 return conditionColumn_;
234 } else { 249 } else {
235 throw std::domain_error("Only condition join fields have a condition field"); 250 throw std::domain_error("Only condition join fields have a condition column");
236 } 251 }
237 } 252 }
238 253
239 int getConditionValue() const 254 int getConditionValue() const
240 { 255 {
241 return (type_ == type::join_where) 256 return ((type_ == type::join_where) || (type_ == type::join_through_where))
242 ? conditionValue_ 257 ? conditionValue_
243 : throw std::domain_error("Only condition join fields have a condition value"); 258 : throw std::domain_error("Only condition join fields have a condition value");
244 } 259 }
@@ -254,13 +269,8 @@ namespace verbly {
254 // table (hypernymy); however, they have different join columns. For 269 // table (hypernymy); however, they have different join columns. For
255 // condition joins, the condition field and condition value are also 270 // condition joins, the condition field and condition value are also
256 // significant. 271 // significant.
257 if (conditionField_) 272 return std::tie(object_, column_, table_, joinColumn_, conditionColumn_, conditionValue_)
258 { 273 < std::tie(other.object_, other.column_, other.table_, other.joinColumn_, other.conditionColumn_, other.conditionValue_);
259 return std::tie(object_, column_, table_, joinColumn_, *conditionField_, conditionValue_)
260 < std::tie(other.object_, other.column_, other.table_, other.joinColumn_, *other.conditionField_, other.conditionValue_);
261 } else {
262 return std::tie(object_, column_, table_, joinColumn_) < std::tie(other.object_, other.column_, other.table_, other.joinColumn_);
263 }
264 } 274 }
265 275
266 // Equality 276 // Equality
@@ -268,13 +278,8 @@ namespace verbly {
268 bool operator==(const field& other) const 278 bool operator==(const field& other) const
269 { 279 {
270 // See operator<() for documentation. 280 // See operator<() for documentation.
271 if (conditionField_) 281 return std::tie(object_, column_, table_, joinColumn_, conditionColumn_, conditionValue_)
272 { 282 == std::tie(other.object_, other.column_, other.table_, other.joinColumn_, other.conditionColumn_, other.conditionValue_);
273 return std::tie(object_, column_, table_, joinColumn_, *conditionField_, conditionValue_)
274 == std::tie(other.object_, other.column_, other.table_, other.joinColumn_, *other.conditionField_, other.conditionValue_);
275 } else {
276 return std::tie(object_, column_, table_, joinColumn_) == std::tie(other.object_, other.column_, other.table_, other.joinColumn_);
277 }
278 } 283 }
279 284
280 // Filter construction 285 // Filter construction
@@ -325,7 +330,7 @@ namespace verbly {
325 const char* foreignColumn = 0, 330 const char* foreignColumn = 0,
326 const char* joinColumn = 0, 331 const char* joinColumn = 0,
327 const char* foreignJoinColumn = 0, 332 const char* foreignJoinColumn = 0,
328 const field* conditionField = 0, 333 const char* conditionColumn = 0,
329 int conditionValue = 0) : 334 int conditionValue = 0) :
330 object_(obj), 335 object_(obj),
331 type_(datatype), 336 type_(datatype),
@@ -336,7 +341,7 @@ namespace verbly {
336 foreignColumn_(foreignColumn), 341 foreignColumn_(foreignColumn),
337 joinColumn_(joinColumn), 342 joinColumn_(joinColumn),
338 foreignJoinColumn_(foreignJoinColumn), 343 foreignJoinColumn_(foreignJoinColumn),
339 conditionField_(conditionField), 344 conditionColumn_(conditionColumn),
340 conditionValue_(conditionValue) 345 conditionValue_(conditionValue)
341 { 346 {
342 } 347 }
@@ -359,7 +364,7 @@ namespace verbly {
359 const char* foreignJoinColumn_ = 0; 364 const char* foreignJoinColumn_ = 0;
360 365
361 // Condition joins 366 // Condition joins
362 const field* conditionField_ = 0; 367 const char* conditionColumn_ = 0;
363 int conditionValue_ = 0; 368 int conditionValue_ = 0;
364 369
365 }; 370 };
diff --git a/lib/filter.cpp b/lib/filter.cpp index ecff8ac..c201618 100644 --- a/lib/filter.cpp +++ b/lib/filter.cpp
@@ -5,7 +5,6 @@
5#include "word.h" 5#include "word.h"
6#include "frame.h" 6#include "frame.h"
7#include "part.h" 7#include "part.h"
8#include "lemma.h"
9#include "form.h" 8#include "form.h"
10#include "pronunciation.h" 9#include "pronunciation.h"
11 10
@@ -73,6 +72,14 @@ namespace verbly {
73 72
74 break; 73 break;
75 } 74 }
75
76 case comparison::field_equals:
77 case comparison::field_does_not_equal:
78 {
79 new(&singleton_.compareField) field(other.singleton_.compareField);
80
81 break;
82 }
76 } 83 }
77 84
78 break; 85 break;
@@ -112,6 +119,7 @@ namespace verbly {
112 std::string tempStringValue; 119 std::string tempStringValue;
113 int tempIntValue; 120 int tempIntValue;
114 bool tempBoolValue; 121 bool tempBoolValue;
122 field tempCompareField;
115 std::list<filter> tempChildren; 123 std::list<filter> tempChildren;
116 bool tempOrlogic; 124 bool tempOrlogic;
117 125
@@ -173,6 +181,14 @@ namespace verbly {
173 181
174 break; 182 break;
175 } 183 }
184
185 case comparison::field_equals:
186 case comparison::field_does_not_equal:
187 {
188 tempCompareField = std::move(first.singleton_.compareField);
189
190 break;
191 }
176 } 192 }
177 193
178 break; 194 break;
@@ -249,6 +265,14 @@ namespace verbly {
249 265
250 break; 266 break;
251 } 267 }
268
269 case comparison::field_equals:
270 case comparison::field_does_not_equal:
271 {
272 new(&first.singleton_.compareField) field(std::move(second.singleton_.compareField));
273
274 break;
275 }
252 } 276 }
253 277
254 break; 278 break;
@@ -325,6 +349,14 @@ namespace verbly {
325 349
326 break; 350 break;
327 } 351 }
352
353 case comparison::field_equals:
354 case comparison::field_does_not_equal:
355 {
356 new(&second.singleton_.compareField) field(std::move(tempCompareField));
357
358 break;
359 }
328 } 360 }
329 361
330 break; 362 break;
@@ -391,6 +423,14 @@ namespace verbly {
391 423
392 break; 424 break;
393 } 425 }
426
427 case comparison::field_equals:
428 case comparison::field_does_not_equal:
429 {
430 singleton_.compareField.~field();
431
432 break;
433 }
394 } 434 }
395 435
396 break; 436 break;
@@ -446,6 +486,8 @@ namespace verbly {
446 case comparison::does_not_match: 486 case comparison::does_not_match:
447 case comparison::hierarchally_matches: 487 case comparison::hierarchally_matches:
448 case comparison::does_not_hierarchally_match: 488 case comparison::does_not_hierarchally_match:
489 case comparison::field_equals:
490 case comparison::field_does_not_equal:
449 { 491 {
450 throw std::invalid_argument("Invalid comparison for integer field"); 492 throw std::invalid_argument("Invalid comparison for integer field");
451 } 493 }
@@ -490,6 +532,8 @@ namespace verbly {
490 case comparison::does_not_match: 532 case comparison::does_not_match:
491 case comparison::hierarchally_matches: 533 case comparison::hierarchally_matches:
492 case comparison::does_not_hierarchally_match: 534 case comparison::does_not_hierarchally_match:
535 case comparison::field_equals:
536 case comparison::field_does_not_equal:
493 { 537 {
494 throw std::invalid_argument("Invalid comparison for string field"); 538 throw std::invalid_argument("Invalid comparison for string field");
495 } 539 }
@@ -534,6 +578,8 @@ namespace verbly {
534 case comparison::does_not_match: 578 case comparison::does_not_match:
535 case comparison::hierarchally_matches: 579 case comparison::hierarchally_matches:
536 case comparison::does_not_hierarchally_match: 580 case comparison::does_not_hierarchally_match:
581 case comparison::field_equals:
582 case comparison::field_does_not_equal:
537 { 583 {
538 throw std::invalid_argument("Invalid comparison for boolean field"); 584 throw std::invalid_argument("Invalid comparison for boolean field");
539 } 585 }
@@ -576,6 +622,8 @@ namespace verbly {
576 case comparison::does_not_match: 622 case comparison::does_not_match:
577 case comparison::hierarchally_matches: 623 case comparison::hierarchally_matches:
578 case comparison::does_not_hierarchally_match: 624 case comparison::does_not_hierarchally_match:
625 case comparison::field_equals:
626 case comparison::field_does_not_equal:
579 { 627 {
580 throw std::invalid_argument("Incorrect constructor for given comparison"); 628 throw std::invalid_argument("Incorrect constructor for given comparison");
581 } 629 }
@@ -596,6 +644,7 @@ namespace verbly {
596 case field::type::join: 644 case field::type::join:
597 case field::type::join_where: 645 case field::type::join_where:
598 case field::type::join_through: 646 case field::type::join_through:
647 case field::type::join_through_where:
599 { 648 {
600 switch (filterType) 649 switch (filterType)
601 { 650 {
@@ -624,6 +673,8 @@ namespace verbly {
624 case comparison::is_not_null: 673 case comparison::is_not_null:
625 case comparison::hierarchally_matches: 674 case comparison::hierarchally_matches:
626 case comparison::does_not_hierarchally_match: 675 case comparison::does_not_hierarchally_match:
676 case comparison::field_equals:
677 case comparison::field_does_not_equal:
627 { 678 {
628 throw std::invalid_argument("Incorrect constructor for given comparison"); 679 throw std::invalid_argument("Incorrect constructor for given comparison");
629 } 680 }
@@ -661,6 +712,8 @@ namespace verbly {
661 case comparison::is_not_null: 712 case comparison::is_not_null:
662 case comparison::matches: 713 case comparison::matches:
663 case comparison::does_not_match: 714 case comparison::does_not_match:
715 case comparison::field_equals:
716 case comparison::field_does_not_equal:
664 { 717 {
665 throw std::invalid_argument("Incorrect constructor for given comparison"); 718 throw std::invalid_argument("Incorrect constructor for given comparison");
666 } 719 }
@@ -679,6 +732,57 @@ namespace verbly {
679 } 732 }
680 } 733 }
681 734
735 filter::filter(
736 field filterField,
737 comparison filterType,
738 field compareField) :
739 type_(type::singleton)
740 {
741 switch (filterType)
742 {
743 case comparison::field_equals:
744 case comparison::field_does_not_equal:
745 {
746 if (filterField.getType() != compareField.getType())
747 {
748 throw std::invalid_argument("Cannot compare two fields of different types");
749 }
750
751 if (filterField.isJoin())
752 {
753 throw std::domain_error("Cannot compare join fields");
754 }
755
756 new(&singleton_.filterField) field(std::move(filterField));
757 singleton_.filterType = filterType;
758 new(&singleton_.compareField) field(std::move(compareField));
759
760 break;
761 }
762
763 case comparison::int_equals:
764 case comparison::int_does_not_equal:
765 case comparison::int_is_at_least:
766 case comparison::int_is_greater_than:
767 case comparison::int_is_at_most:
768 case comparison::int_is_less_than:
769 case comparison::boolean_equals:
770 case comparison::string_equals:
771 case comparison::string_does_not_equal:
772 case comparison::string_is_like:
773 case comparison::string_is_not_like:
774 case comparison::is_null:
775 case comparison::is_not_null:
776 case comparison::matches:
777 case comparison::does_not_match:
778 case comparison::hierarchally_matches:
779 case comparison::does_not_hierarchally_match:
780 {
781 throw std::domain_error("Incorrect constructor for given comparison");
782 }
783 }
784 }
785
682 field filter::getField() const 786 field filter::getField() const
683 { 787 {
684 if (type_ == type::singleton) 788 if (type_ == type::singleton)
@@ -726,6 +830,8 @@ namespace verbly {
726 case comparison::boolean_equals: 830 case comparison::boolean_equals:
727 case comparison::is_null: 831 case comparison::is_null:
728 case comparison::is_not_null: 832 case comparison::is_not_null:
833 case comparison::field_equals:
834 case comparison::field_does_not_equal:
729 { 835 {
730 throw std::domain_error("This filter does not have a join condition"); 836 throw std::domain_error("This filter does not have a join condition");
731 } 837 }
@@ -762,6 +868,8 @@ namespace verbly {
762 case comparison::does_not_match: 868 case comparison::does_not_match:
763 case comparison::hierarchally_matches: 869 case comparison::hierarchally_matches:
764 case comparison::does_not_hierarchally_match: 870 case comparison::does_not_hierarchally_match:
871 case comparison::field_equals:
872 case comparison::field_does_not_equal:
765 { 873 {
766 throw std::domain_error("This filter does not have a string argument"); 874 throw std::domain_error("This filter does not have a string argument");
767 } 875 }
@@ -798,6 +906,8 @@ namespace verbly {
798 case comparison::does_not_match: 906 case comparison::does_not_match:
799 case comparison::hierarchally_matches: 907 case comparison::hierarchally_matches:
800 case comparison::does_not_hierarchally_match: 908 case comparison::does_not_hierarchally_match:
909 case comparison::field_equals:
910 case comparison::field_does_not_equal:
801 { 911 {
802 throw std::domain_error("This filter does not have an integer argument"); 912 throw std::domain_error("This filter does not have an integer argument");
803 } 913 }
@@ -817,6 +927,47 @@ namespace verbly {
817 } 927 }
818 } 928 }
819 929
930 field filter::getCompareField() const
931 {
932 if (type_ != type::singleton)
933 {
934 throw std::domain_error("This filter does not have a compare field");
935 }
936
937 switch (singleton_.filterType)
938 {
939 case comparison::field_equals:
940 case comparison::field_does_not_equal:
941 {
942 return singleton_.compareField;
943
944 break;
945 }
946
947 case comparison::int_equals:
948 case comparison::int_does_not_equal:
949 case comparison::int_is_at_least:
950 case comparison::int_is_greater_than:
951 case comparison::int_is_at_most:
952 case comparison::int_is_less_than:
953 case comparison::boolean_equals:
954 case comparison::string_equals:
955 case comparison::string_does_not_equal:
956 case comparison::string_is_like:
957 case comparison::string_is_not_like:
958 case comparison::is_null:
959 case comparison::is_not_null:
960 case comparison::matches:
961 case comparison::does_not_match:
962 case comparison::hierarchally_matches:
963 case comparison::does_not_hierarchally_match:
964 {
965 throw std::domain_error("This filter doesn't have a compare field");
966 }
967 }
968
969 }
970
820 filter::filter(bool orlogic) : type_(type::group) 971 filter::filter(bool orlogic) : type_(type::group)
821 { 972 {
822 new(&group_.children) std::list<filter>(); 973 new(&group_.children) std::list<filter>();
@@ -970,6 +1121,16 @@ namespace verbly {
970 { 1121 {
971 return filter(singleton_.filterField, comparison::hierarchally_matches, *singleton_.join); 1122 return filter(singleton_.filterField, comparison::hierarchally_matches, *singleton_.join);
972 } 1123 }
1124
1125 case comparison::field_equals:
1126 {
1127 return filter(singleton_.filterField, comparison::field_does_not_equal, singleton_.compareField);
1128 }
1129
1130 case comparison::field_does_not_equal:
1131 {
1132 return filter(singleton_.filterField, comparison::field_equals, singleton_.compareField);
1133 }
973 } 1134 }
974 } 1135 }
975 1136
@@ -1111,7 +1272,6 @@ namespace verbly {
1111 case object::word: 1272 case object::word:
1112 case object::frame: 1273 case object::frame:
1113 case object::part: 1274 case object::part:
1114 case object::lemma:
1115 case object::form: 1275 case object::form:
1116 case object::pronunciation: 1276 case object::pronunciation:
1117 { 1277 {
@@ -1141,11 +1301,10 @@ namespace verbly {
1141 return (verbly::word::frames %= *this); 1301 return (verbly::word::frames %= *this);
1142 } 1302 }
1143 1303
1144 case object::lemma:
1145 case object::form: 1304 case object::form:
1146 case object::pronunciation: 1305 case object::pronunciation:
1147 { 1306 {
1148 return (verbly::word::lemmas %= *this); 1307 return (verbly::word::forms(inflection::base) %= *this);
1149 } 1308 }
1150 } 1309 }
1151 1310
@@ -1161,7 +1320,6 @@ namespace verbly {
1161 1320
1162 case object::notion: 1321 case object::notion:
1163 case object::word: 1322 case object::word:
1164 case object::lemma:
1165 case object::form: 1323 case object::form:
1166 case object::pronunciation: 1324 case object::pronunciation:
1167 { 1325 {
@@ -1188,7 +1346,6 @@ namespace verbly {
1188 case object::notion: 1346 case object::notion:
1189 case object::word: 1347 case object::word:
1190 case object::frame: 1348 case object::frame:
1191 case object::lemma:
1192 case object::form: 1349 case object::form:
1193 case object::pronunciation: 1350 case object::pronunciation:
1194 { 1351 {
@@ -1197,32 +1354,6 @@ namespace verbly {
1197 } 1354 }
1198 } 1355 }
1199 1356
1200 case object::lemma:
1201 {
1202 switch (singleton_.filterField.getObject())
1203 {
1204 case object::notion:
1205 case object::word:
1206 case object::frame:
1207 case object::part:
1208 {
1209 return verbly::lemma::words %= *this;
1210 }
1211
1212 case object::undefined:
1213 case object::lemma:
1214 {
1215 return *this;
1216 }
1217
1218 case object::form:
1219 case object::pronunciation:
1220 {
1221 return (verbly::lemma::forms(inflection::base) %= *this);
1222 }
1223 }
1224 }
1225
1226 case object::form: 1357 case object::form:
1227 { 1358 {
1228 switch (singleton_.filterField.getObject()) 1359 switch (singleton_.filterField.getObject())
@@ -1231,9 +1362,8 @@ namespace verbly {
1231 case object::word: 1362 case object::word:
1232 case object::frame: 1363 case object::frame:
1233 case object::part: 1364 case object::part:
1234 case object::lemma:
1235 { 1365 {
1236 return verbly::form::lemmas %= *this; 1366 return verbly::form::words(inflection::base) %= *this;
1237 } 1367 }
1238 1368
1239 case object::undefined: 1369 case object::undefined:
@@ -1257,7 +1387,6 @@ namespace verbly {
1257 case object::word: 1387 case object::word:
1258 case object::frame: 1388 case object::frame:
1259 case object::part: 1389 case object::part:
1260 case object::lemma:
1261 case object::form: 1390 case object::form:
1262 { 1391 {
1263 return verbly::pronunciation::forms %= *this; 1392 return verbly::pronunciation::forms %= *this;
@@ -1354,6 +1483,8 @@ namespace verbly {
1354 case comparison::string_is_not_like: 1483 case comparison::string_is_not_like:
1355 case comparison::is_null: 1484 case comparison::is_null:
1356 case comparison::is_not_null: 1485 case comparison::is_not_null:
1486 case comparison::field_equals:
1487 case comparison::field_does_not_equal:
1357 { 1488 {
1358 result += std::move(normalized); 1489 result += std::move(normalized);
1359 1490
diff --git a/lib/filter.h b/lib/filter.h index dcadf95..a12a822 100644 --- a/lib/filter.h +++ b/lib/filter.h
@@ -34,7 +34,9 @@ namespace verbly {
34 matches, 34 matches,
35 does_not_match, 35 does_not_match,
36 hierarchally_matches, 36 hierarchally_matches,
37 does_not_hierarchally_match 37 does_not_hierarchally_match,
38 field_equals,
39 field_does_not_equal
38 }; 40 };
39 41
40 // Copy and move constructors 42 // Copy and move constructors
@@ -72,6 +74,7 @@ namespace verbly {
72 filter(field filterField, comparison filterType, bool filterValue); 74 filter(field filterField, comparison filterType, bool filterValue);
73 filter(field filterField, comparison filterType); 75 filter(field filterField, comparison filterType);
74 filter(field joinOn, comparison filterType, filter joinCondition); 76 filter(field joinOn, comparison filterType, filter joinCondition);
77 filter(field filterField, comparison filterType, field compareField);
75 78
76 field getField() const; 79 field getField() const;
77 80
@@ -85,6 +88,8 @@ namespace verbly {
85 88
86 bool getBooleanArgument() const; 89 bool getBooleanArgument() const;
87 90
91 field getCompareField() const;
92
88 // Group 93 // Group
89 94
90 explicit filter(bool orlogic); 95 explicit filter(bool orlogic);
@@ -129,6 +134,7 @@ namespace verbly {
129 std::string stringValue; 134 std::string stringValue;
130 int intValue; 135 int intValue;
131 bool boolValue; 136 bool boolValue;
137 field compareField;
132 }; 138 };
133 } singleton_; 139 } singleton_;
134 struct { 140 struct {
diff --git a/lib/form.cpp b/lib/form.cpp index 4548582..2f9509f 100644 --- a/lib/form.cpp +++ b/lib/form.cpp
@@ -17,9 +17,13 @@ namespace verbly {
17 const field form::complexity = field::integerField(object::form, "complexity"); 17 const field form::complexity = field::integerField(object::form, "complexity");
18 const field form::proper = field::booleanField(object::form, "proper"); 18 const field form::proper = field::booleanField(object::form, "proper");
19 19
20 const field form::lemmas = field::joinField(object::form, "form_id", object::lemma);
21 const field form::pronunciations = field::joinThrough(object::form, "form_id", object::pronunciation, "forms_pronunciations", "pronunciation_id"); 20 const field form::pronunciations = field::joinThrough(object::form, "form_id", object::pronunciation, "forms_pronunciations", "pronunciation_id");
22 21
22 field form::words(inflection category)
23 {
24 return field::joinThroughWhere(object::form, "form_id", object::word, "lemmas_forms", "lemma_id", "category", static_cast<int>(category));
25 }
26
23 form::form(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) 27 form::form(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true)
24 { 28 {
25 id_ = sqlite3_column_int(row, 0); 29 id_ = sqlite3_column_int(row, 0);
diff --git a/lib/form.h b/lib/form.h index a05d015..fe0886c 100644 --- a/lib/form.h +++ b/lib/form.h
@@ -104,7 +104,7 @@ namespace verbly {
104 104
105 // Relationships to other objects 105 // Relationships to other objects
106 106
107 static const field lemmas; 107 static field words(inflection category);
108 108
109 static const field pronunciations; 109 static const field pronunciations;
110 110
diff --git a/lib/frame.cpp b/lib/frame.cpp index b5ba914..2351973 100644 --- a/lib/frame.cpp +++ b/lib/frame.cpp
@@ -21,7 +21,7 @@ namespace verbly {
21 21
22 field frame::parts(int index) 22 field frame::parts(int index)
23 { 23 {
24 return field::joinWhere(object::frame, "frame_id", object::part, part::index, index); 24 return field::joinWhere(object::frame, "frame_id", object::part, "part_index", index);
25 } 25 }
26 26
27 frame::frame(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) 27 frame::frame(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true)
diff --git a/lib/lemma.cpp b/lib/lemma.cpp deleted file mode 100644 index ea7b0ea..0000000 --- a/lib/lemma.cpp +++ /dev/null
@@ -1,67 +0,0 @@
1#include "lemma.h"
2#include <sqlite3.h>
3#include "database.h"
4#include "query.h"
5
6namespace verbly {
7
8 const object lemma::objectType = object::lemma;
9
10 const std::list<std::string> lemma::select = {"lemma_id"};
11
12 const field lemma::id = field::integerField(object::lemma, "lemma_id");
13 const field lemma::inflectionCategory = field::integerField(object::lemma, "category");
14
15 const field lemma::words = field::joinField(object::lemma, "lemma_id", object::word);
16
17 field lemma::forms(inflection category)
18 {
19 return field::joinWhere(object::lemma, "form_id", object::form, inflectionCategory, static_cast<int>(category));
20 }
21
22 lemma::lemma(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true)
23 {
24 id_ = sqlite3_column_int(row, 0);
25 }
26
27 const form& lemma::getBaseForm() const
28 {
29 if (!valid_)
30 {
31 throw std::domain_error("Bad access to uninitialized lemma");
32 }
33
34 if (!forms_.count(inflection::base))
35 {
36 initializeForm(inflection::base);
37 }
38
39 return forms_.at(inflection::base).front();
40 }
41
42 bool lemma::hasInflection(inflection category) const
43 {
44 return !getInflections(category).empty();
45 }
46
47 const std::vector<form>& lemma::getInflections(inflection category) const
48 {
49 if (!valid_)
50 {
51 throw std::domain_error("Bad access to uninitialized lemma");
52 }
53
54 if (!forms_.count(category))
55 {
56 initializeForm(category);
57 }
58
59 return forms_.at(category);
60 }
61
62 void lemma::initializeForm(inflection infl) const
63 {
64 forms_[infl] = db_->forms(form::lemmas %= ((inflectionCategory == infl) && *this), verbly::form::id, -1).all();
65 }
66
67};
diff --git a/lib/lemma.h b/lib/lemma.h deleted file mode 100644 index bba5572..0000000 --- a/lib/lemma.h +++ /dev/null
@@ -1,97 +0,0 @@
1#ifndef LEMMA_H_0A180D30
2#define LEMMA_H_0A180D30
3
4#include <stdexcept>
5#include <vector>
6#include <list>
7#include <map>
8#include "field.h"
9#include "form.h"
10#include "enums.h"
11#include "filter.h"
12
13struct sqlite3_stmt;
14
15namespace verbly {
16
17 class database;
18
19 class lemma {
20 public:
21
22 // Default constructor
23
24 lemma() = default;
25
26 // Construct from database
27
28 lemma(const database& db, sqlite3_stmt* row);
29
30 // Accessors
31
32 operator bool() const
33 {
34 return valid_;
35 }
36
37 int getId() const
38 {
39 if (!valid_)
40 {
41 throw std::domain_error("Bad access to uninitialized lemma");
42 }
43
44 return id_;
45 }
46
47 const form& getBaseForm() const;
48
49 bool hasInflection(inflection category) const;
50
51 const std::vector<form>& getInflections(inflection category) const;
52
53 // Type info
54
55 static const object objectType;
56
57 static const std::list<std::string> select;
58
59 // Query fields
60
61 static const field id;
62
63 operator filter() const
64 {
65 if (!valid_)
66 {
67 throw std::domain_error("Bad access to uninitialized lemma");
68 }
69
70 return (id == id_);
71 }
72
73 // Relationships to other objects
74
75 static const field words;
76
77 static field forms(inflection category);
78
79 private:
80
81 void initializeForm(inflection category) const;
82
83 bool valid_ = false;
84
85 int id_;
86
87 mutable std::map<inflection, std::vector<form>> forms_;
88
89 const database* db_;
90
91 static const field inflectionCategory;
92
93 };
94
95};
96
97#endif /* end of include guard: LEMMA_H_0A180D30 */
diff --git a/lib/pronunciation.cpp b/lib/pronunciation.cpp index 3ddb1c5..fa471ec 100644 --- a/lib/pronunciation.cpp +++ b/lib/pronunciation.cpp
@@ -1,7 +1,6 @@
1#include "pronunciation.h" 1#include "pronunciation.h"
2#include <sqlite3.h> 2#include <sqlite3.h>
3#include "form.h" 3#include "form.h"
4#include "lemma.h"
5#include "word.h" 4#include "word.h"
6#include "util.h" 5#include "util.h"
7 6
@@ -20,6 +19,9 @@ namespace verbly {
20 const field pronunciation::prerhyme = field::stringField(object::pronunciation, "prerhyme", true); 19 const field pronunciation::prerhyme = field::stringField(object::pronunciation, "prerhyme", true);
21 const field pronunciation::rhyme = field::stringField(object::pronunciation, "rhyme", true); 20 const field pronunciation::rhyme = field::stringField(object::pronunciation, "rhyme", true);
22 21
22 const field pronunciation::rhymes_field::rhymeJoin = field::joinField(object::pronunciation, "rhyme", object::pronunciation);
23 const pronunciation::rhymes_field pronunciation::rhymes = {};
24
23 pronunciation::pronunciation(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) 25 pronunciation::pronunciation(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true)
24 { 26 {
25 id_ = sqlite3_column_int(row, 0); 27 id_ = sqlite3_column_int(row, 0);
@@ -39,31 +41,22 @@ namespace verbly {
39 } 41 }
40 } 42 }
41 43
42 filter pronunciation::rhymesWith(const pronunciation& arg) 44 filter pronunciation::rhymes_field::operator%=(filter joinCondition) const
43 { 45 {
44 return (prerhyme != arg.getPrerhyme()) && (rhyme == arg.getRhyme()); 46 return (rhymeJoin %= (
45 } 47 joinCondition
46 48 && filter(
47 /*filter pronunciation::rhymesWith(const class form& arg) 49 verbly::pronunciation::prerhyme,
48 { 50 filter::comparison::field_does_not_equal,
49 filter result; 51 verbly::pronunciation::prerhyme)));
50
51 for (const pronunciation& p : arg.getPronunciations())
52 {
53 result |= rhymesWith(p);
54 }
55
56 return result;
57 } 52 }
58 53
59 filter pronunciation::rhymesWith(const lemma& arg) 54 pronunciation::rhymes_field::operator filter() const
60 { 55 {
61 return rhymesWith(arg.getBaseForm()); 56 return (rhymeJoin %= filter(
57 verbly::pronunciation::prerhyme,
58 filter::comparison::field_does_not_equal,
59 verbly::pronunciation::prerhyme));
62 } 60 }
63 61
64 filter pronunciation::rhymesWith(const word& arg)
65 {
66 return rhymesWith(arg.getLemma());
67 }*/
68
69}; 62};
diff --git a/lib/pronunciation.h b/lib/pronunciation.h index e171fe8..1162360 100644 --- a/lib/pronunciation.h +++ b/lib/pronunciation.h
@@ -12,7 +12,6 @@ struct sqlite3_stmt;
12namespace verbly { 12namespace verbly {
13 13
14 class form; 14 class form;
15 class lemma;
16 class word; 15 class word;
17 class database; 16 class database;
18 17
@@ -131,14 +130,26 @@ namespace verbly {
131 return (id == id_); 130 return (id == id_);
132 } 131 }
133 132
134 static filter rhymesWith(const pronunciation& arg);
135 static filter rhymesWith(const class form& arg);
136 static filter rhymesWith(const lemma& arg);
137 static filter rhymesWith(const word& arg);
138
139 // Relationships to other objects 133 // Relationships to other objects
140 134
141 static const field forms; 135 static const field forms;
136
137 // Rhyming relationship
138
139 class rhymes_field {
140 public:
141
142 filter operator%=(filter joinCondition) const;
143
144 operator filter() const;
145
146 private:
147
148 static const field rhymeJoin;
149
150 };
151
152 static const rhymes_field rhymes;
142 153
143 private: 154 private:
144 bool valid_ = false; 155 bool valid_ = false;
diff --git a/lib/query.h b/lib/query.h index 75651f6..0f490ed 100644 --- a/lib/query.h +++ b/lib/query.h
@@ -81,6 +81,11 @@ namespace verbly {
81 { 81 {
82 throw std::logic_error("Cannot use invalid bindings"); 82 throw std::logic_error("Cannot use invalid bindings");
83 } 83 }
84
85 case binding::type::field:
86 {
87 throw std::logic_error("Compare field binding made it past statement generation");
88 }
84 } 89 }
85 90
86 i++; 91 i++;
diff --git a/lib/statement.cpp b/lib/statement.cpp index 562eef2..b892cab 100644 --- a/lib/statement.cpp +++ b/lib/statement.cpp
@@ -3,13 +3,6 @@
3#include <utility> 3#include <utility>
4#include "filter.h" 4#include "filter.h"
5#include "util.h" 5#include "util.h"
6#include "notion.h"
7#include "word.h"
8#include "frame.h"
9#include "part.h"
10#include "lemma.h"
11#include "form.h"
12#include "pronunciation.h"
13#include "order.h" 6#include "order.h"
14 7
15namespace verbly { 8namespace verbly {
@@ -17,7 +10,7 @@ namespace verbly {
17 statement::statement( 10 statement::statement(
18 object context, 11 object context,
19 filter queryFilter) : 12 filter queryFilter) :
20 statement(getTableForContext(context), queryFilter.compact().normalize(context)) 13 statement(context, getTableForContext(context), queryFilter.compact().normalize(context))
21 { 14 {
22 } 15 }
23 16
@@ -104,24 +97,29 @@ namespace verbly {
104 queryStream << " WHERE "; 97 queryStream << " WHERE ";
105 queryStream << topCondition_.flatten().toSql(true, debug); 98 queryStream << topCondition_.flatten().toSql(true, debug);
106 } 99 }
107 100
101 queryStream << " GROUP BY ";
102 queryStream << topTable_;
103 queryStream << ".";
104 queryStream << select.front();
105
108 queryStream << " ORDER BY "; 106 queryStream << " ORDER BY ";
109 107
110 switch (sortOrder.getType()) 108 switch (sortOrder.getType())
111 { 109 {
112 case order::type::random: 110 case order::type::random:
113 { 111 {
114 queryStream << "RANDOM()"; 112 queryStream << "RANDOM()";
115 113
116 break; 114 break;
117 } 115 }
118 116
119 case order::type::field: 117 case order::type::field:
120 { 118 {
121 queryStream << topTable_; 119 queryStream << topTable_;
122 queryStream << "."; 120 queryStream << ".";
123 queryStream << sortOrder.getSortField().getColumn(); 121 queryStream << sortOrder.getSortField().getColumn();
124 122
125 break; 123 break;
126 } 124 }
127 } 125 }
@@ -156,10 +154,12 @@ namespace verbly {
156 } 154 }
157 155
158 statement::statement( 156 statement::statement(
157 object context,
159 std::string tableName, 158 std::string tableName,
160 filter clause, 159 filter clause,
161 int nextTableId, 160 int nextTableId,
162 int nextWithId) : 161 int nextWithId) :
162 context_(context),
163 nextTableId_(nextTableId), 163 nextTableId_(nextTableId),
164 nextWithId_(nextWithId), 164 nextWithId_(nextWithId),
165 topTable_(instantiateTable(std::move(tableName))), 165 topTable_(instantiateTable(std::move(tableName))),
@@ -266,6 +266,16 @@ namespace verbly {
266 return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_not_like, clause.getStringArgument()); 266 return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_not_like, clause.getStringArgument());
267 } 267 }
268 268
269 case filter::comparison::field_equals:
270 {
271 return condition(topTable_, clause.getField().getColumn(), condition::comparison::equals, {"", clause.getCompareField().getColumn()}, clause.getCompareField().getObject());
272 }
273
274 case filter::comparison::field_does_not_equal:
275 {
276 return condition(topTable_, clause.getField().getColumn(), condition::comparison::does_not_equal, {"", clause.getCompareField().getColumn()}, clause.getCompareField().getObject());
277 }
278
269 case filter::comparison::matches: 279 case filter::comparison::matches:
270 case filter::comparison::does_not_match: 280 case filter::comparison::does_not_match:
271 case filter::comparison::hierarchally_matches: 281 case filter::comparison::hierarchally_matches:
@@ -281,26 +291,29 @@ namespace verbly {
281 { 291 {
282 // First, figure out what table we need to join against. 292 // First, figure out what table we need to join against.
283 std::string joinTableName; 293 std::string joinTableName;
294 object joinContext = object::undefined;
284 if (clause.getField().hasTable()) 295 if (clause.getField().hasTable())
285 { 296 {
286 joinTableName = clause.getField().getTable(); 297 joinTableName = clause.getField().getTable();
287 } else { 298 } else {
288 joinTableName = getTableForContext(clause.getField().getJoinObject()); 299 joinContext = clause.getField().getJoinObject();
300 joinTableName = getTableForContext(joinContext);
289 } 301 }
290 302
291 filter joinCondition = clause.getJoinCondition(); 303 filter joinCondition = clause.getJoinCondition();
292 304
293 // If this is a condition join, we need to add the field join 305 // If this is a condition join, we need to add the field join
294 // condition to the clause. 306 // condition to the clause.
295 if (clause.getField().getType() == field::type::join_where) 307 if (clause.getField().getType() == field::type::join_where)
296 { 308 {
297 joinCondition &= (clause.getField().getConditionField() == clause.getField().getConditionValue()); 309 joinCondition &= (field::integerField(joinTableName.c_str(), clause.getField().getConditionColumn()) == clause.getField().getConditionValue());
298 } 310 }
299 311
300 // Recursively parse the subquery, and therefore obtain an 312 // Recursively parse the subquery, and therefore obtain an
301 // instantiated table to join against, as well as any joins or CTEs 313 // instantiated table to join against, as well as any joins or CTEs
302 // that the subquery may require to function. 314 // that the subquery may require to function.
303 statement joinStmt( 315 statement joinStmt(
316 joinContext,
304 joinTableName, 317 joinTableName,
305 std::move(joinCondition).normalize(clause.getField().getJoinObject()), 318 std::move(joinCondition).normalize(clause.getField().getJoinObject()),
306 nextTableId_, 319 nextTableId_,
@@ -368,11 +381,13 @@ namespace verbly {
368 } 381 }
369 382
370 case field::type::join_through: 383 case field::type::join_through:
384 case field::type::join_through_where:
371 { 385 {
372 // Recursively parse the subquery, and therefore obtain an 386 // Recursively parse the subquery, and therefore obtain an
373 // instantiated table to join against, as well as any joins or CTEs 387 // instantiated table to join against, as well as any joins or CTEs
374 // that the subquery may require to function. 388 // that the subquery may require to function.
375 statement joinStmt( 389 statement joinStmt(
390 clause.getField().getJoinObject(),
376 getTableForContext(clause.getField().getJoinObject()), 391 getTableForContext(clause.getField().getJoinObject()),
377 clause.getJoinCondition().normalize(clause.getField().getJoinObject()), 392 clause.getJoinCondition().normalize(clause.getField().getJoinObject()),
378 nextTableId_, 393 nextTableId_,
@@ -424,6 +439,17 @@ namespace verbly {
424 std::list<join> cteJoins = std::move(joinStmt.joins_); 439 std::list<join> cteJoins = std::move(joinStmt.joins_);
425 condition cteCondition = integrate(std::move(joinStmt), true); 440 condition cteCondition = integrate(std::move(joinStmt), true);
426 441
442 // If this is a condition join, add the condition.
443 if (clause.getField().getType() == field::type::join_through_where)
444 {
445 cteCondition &=
446 condition(
447 throughTable,
448 clause.getField().getConditionColumn(),
449 condition::comparison::equals,
450 clause.getField().getConditionValue());
451 }
452
427 withs_.emplace_back( 453 withs_.emplace_back(
428 std::move(withName), 454 std::move(withName),
429 clause.getField(), 455 clause.getField(),
@@ -453,7 +479,7 @@ namespace verbly {
453 joins_.emplace_back( 479 joins_.emplace_back(
454 false, 480 false,
455 getTableForContext(clause.getField().getJoinObject()), 481 getTableForContext(clause.getField().getJoinObject()),
456 std::move(throughTable), 482 throughTable,
457 clause.getField().getForeignJoinColumn(), 483 clause.getField().getForeignJoinColumn(),
458 std::move(joinTable), 484 std::move(joinTable),
459 clause.getField().getForeignColumn()); 485 clause.getField().getForeignColumn());
@@ -461,7 +487,20 @@ namespace verbly {
461 // Integrate the subquery's table mappings, joins, and CTEs into 487 // Integrate the subquery's table mappings, joins, and CTEs into
462 // this statement, and return the subquery condition as our 488 // this statement, and return the subquery condition as our
463 // condition. 489 // condition.
464 return integrate(std::move(joinStmt)); 490 condition resultCond = integrate(std::move(joinStmt));
491
492 // If this is a condition join, add the condition.
493 if (clause.getField().getType() == field::type::join_through_where)
494 {
495 resultCond &=
496 condition(
497 throughTable,
498 clause.getField().getConditionColumn(),
499 condition::comparison::equals,
500 clause.getField().getConditionValue());
501 }
502
503 return std::move(resultCond);
465 } 504 }
466 } 505 }
467 506
@@ -491,6 +530,7 @@ namespace verbly {
491 530
492 // Recursively parse the subquery in order to create the CTE. 531 // Recursively parse the subquery in order to create the CTE.
493 statement withStmt( 532 statement withStmt(
533 clause.getField().getObject(),
494 getTableForContext(clause.getField().getObject()), 534 getTableForContext(clause.getField().getObject()),
495 clause.getJoinCondition().normalize(clause.getField().getObject()), 535 clause.getJoinCondition().normalize(clause.getField().getObject()),
496 nextTableId_, 536 nextTableId_,
@@ -593,7 +633,7 @@ namespace verbly {
593 nextTableId_ = subStmt.nextTableId_; 633 nextTableId_ = subStmt.nextTableId_;
594 nextWithId_ = subStmt.nextWithId_; 634 nextWithId_ = subStmt.nextWithId_;
595 635
596 return subStmt.topCondition_; 636 return subStmt.topCondition_.resolveCompareFields(context_, topTable_);
597 } 637 }
598 638
599 std::ostream& operator<<(std::ostream& oss, const statement::join& j) 639 std::ostream& operator<<(std::ostream& oss, const statement::join& j)
@@ -637,6 +677,7 @@ namespace verbly {
637 new(&singleton_.column_) std::string(other.singleton_.column_); 677 new(&singleton_.column_) std::string(other.singleton_.column_);
638 singleton_.comparison_ = other.singleton_.comparison_; 678 singleton_.comparison_ = other.singleton_.comparison_;
639 new(&singleton_.value_) binding(other.singleton_.value_); 679 new(&singleton_.value_) binding(other.singleton_.value_);
680 singleton_.parentObject_ = other.singleton_.parentObject_;
640 681
641 break; 682 break;
642 } 683 }
@@ -673,6 +714,7 @@ namespace verbly {
673 std::string tempColumn; 714 std::string tempColumn;
674 condition::comparison tempComparison; 715 condition::comparison tempComparison;
675 binding tempBinding; 716 binding tempBinding;
717 object tempParentObject;
676 std::list<condition> tempChildren; 718 std::list<condition> tempChildren;
677 bool tempOrlogic; 719 bool tempOrlogic;
678 720
@@ -689,6 +731,7 @@ namespace verbly {
689 tempColumn = std::move(first.singleton_.column_); 731 tempColumn = std::move(first.singleton_.column_);
690 tempComparison = first.singleton_.comparison_; 732 tempComparison = first.singleton_.comparison_;
691 tempBinding = std::move(first.singleton_.value_); 733 tempBinding = std::move(first.singleton_.value_);
734 tempParentObject = first.singleton_.parentObject_;
692 735
693 break; 736 break;
694 } 737 }
@@ -719,6 +762,7 @@ namespace verbly {
719 new(&first.singleton_.column_) std::string(std::move(second.singleton_.column_)); 762 new(&first.singleton_.column_) std::string(std::move(second.singleton_.column_));
720 first.singleton_.comparison_ = second.singleton_.comparison_; 763 first.singleton_.comparison_ = second.singleton_.comparison_;
721 new(&first.singleton_.value_) binding(std::move(second.singleton_.value_)); 764 new(&first.singleton_.value_) binding(std::move(second.singleton_.value_));
765 first.singleton_.parentObject_ = second.singleton_.parentObject_;
722 766
723 break; 767 break;
724 } 768 }
@@ -749,6 +793,7 @@ namespace verbly {
749 new(&second.singleton_.column_) std::string(std::move(tempColumn)); 793 new(&second.singleton_.column_) std::string(std::move(tempColumn));
750 second.singleton_.comparison_ = tempComparison; 794 second.singleton_.comparison_ = tempComparison;
751 new(&second.singleton_.value_) binding(std::move(tempBinding)); 795 new(&second.singleton_.value_) binding(std::move(tempBinding));
796 second.singleton_.parentObject_ = tempParentObject;
752 797
753 break; 798 break;
754 } 799 }
@@ -813,19 +858,23 @@ namespace verbly {
813 } else { 858 } else {
814 singleton_.comparison_ = comparison::is_not_null; 859 singleton_.comparison_ = comparison::is_not_null;
815 } 860 }
861
862 singleton_.parentObject_ = object::undefined;
816 } 863 }
817 864
818 statement::condition::condition( 865 statement::condition::condition(
819 std::string table, 866 std::string table,
820 std::string column, 867 std::string column,
821 comparison comp, 868 comparison comp,
822 binding value) : 869 binding value,
870 object parentObject) :
823 type_(type::singleton) 871 type_(type::singleton)
824 { 872 {
825 new(&singleton_.table_) std::string(std::move(table)); 873 new(&singleton_.table_) std::string(std::move(table));
826 new(&singleton_.column_) std::string(std::move(column)); 874 new(&singleton_.column_) std::string(std::move(column));
827 singleton_.comparison_ = comp; 875 singleton_.comparison_ = comp;
828 new(&singleton_.value_) binding(std::move(value)); 876 new(&singleton_.value_) binding(std::move(value));
877 singleton_.parentObject_ = parentObject;
829 } 878 }
830 879
831 std::string statement::condition::toSql(bool toplevel, bool debug) const 880 std::string statement::condition::toSql(bool toplevel, bool debug) const
@@ -845,14 +894,35 @@ namespace verbly {
845 { 894 {
846 if (debug) 895 if (debug)
847 { 896 {
848 if (singleton_.value_.getType() == binding::type::string) 897 switch (singleton_.value_.getType())
849 { 898 {
850 return singleton_.table_ + "." + singleton_.column_ + " = \"" + singleton_.value_.getString() + "\""; 899 case binding::type::string:
851 } else { 900 {
852 return singleton_.table_ + "." + singleton_.column_ + " = " + std::to_string(singleton_.value_.getInteger()); 901 return singleton_.table_ + "." + singleton_.column_ + " = \"" + singleton_.value_.getString() + "\"";
902 }
903
904 case binding::type::integer:
905 {
906 return singleton_.table_ + "." + singleton_.column_ + " = " + std::to_string(singleton_.value_.getInteger());
907 }
908
909 case binding::type::field:
910 {
911 return singleton_.table_ + "." + singleton_.column_ + " = " + singleton_.value_.getTable() + "." + singleton_.value_.getColumn();
912 }
913
914 case binding::type::invalid:
915 {
916 throw std::logic_error("Invalid binding in statement generation");
917 }
853 } 918 }
854 } else { 919 } else {
855 return singleton_.table_ + "." + singleton_.column_ + " = ?"; 920 if (singleton_.value_.getType() == binding::type::field)
921 {
922 return singleton_.table_ + "." + singleton_.column_ + " = " + singleton_.value_.getTable() + "." + singleton_.value_.getColumn();
923 } else {
924 return singleton_.table_ + "." + singleton_.column_ + " = ?";
925 }
856 } 926 }
857 } 927 }
858 928
@@ -860,14 +930,35 @@ namespace verbly {
860 { 930 {
861 if (debug) 931 if (debug)
862 { 932 {
863 if (singleton_.value_.getType() == binding::type::string) 933 switch (singleton_.value_.getType())
864 { 934 {
865 return singleton_.table_ + "." + singleton_.column_ + " != \"" + singleton_.value_.getString() + "\""; 935 case binding::type::string:
866 } else { 936 {
867 return singleton_.table_ + "." + singleton_.column_ + " != " + std::to_string(singleton_.value_.getInteger()); 937 return singleton_.table_ + "." + singleton_.column_ + " != \"" + singleton_.value_.getString() + "\"";
938 }
939
940 case binding::type::integer:
941 {
942 return singleton_.table_ + "." + singleton_.column_ + " != " + std::to_string(singleton_.value_.getInteger());
943 }
944
945 case binding::type::field:
946 {
947 return singleton_.table_ + "." + singleton_.column_ + " != " + singleton_.value_.getTable() + "." + singleton_.value_.getColumn();
948 }
949
950 case binding::type::invalid:
951 {
952 throw std::logic_error("Invalid binding in statement generation");
953 }
868 } 954 }
869 } else { 955 } else {
870 return singleton_.table_ + "." + singleton_.column_ + " != ?"; 956 if (singleton_.value_.getType() == binding::type::field)
957 {
958 return singleton_.table_ + "." + singleton_.column_ + " != " + singleton_.value_.getTable() + "." + singleton_.value_.getColumn();
959 } else {
960 return singleton_.table_ + "." + singleton_.column_ + " != ?";
961 }
871 } 962 }
872 } 963 }
873 964
@@ -959,7 +1050,7 @@ namespace verbly {
959 return clauses.front(); 1050 return clauses.front();
960 } else { 1051 } else {
961 std::string result = implode(std::begin(clauses), std::end(clauses), group_.orlogic_ ? " OR " : " AND "); 1052 std::string result = implode(std::begin(clauses), std::end(clauses), group_.orlogic_ ? " OR " : " AND ");
962 1053
963 if (toplevel) 1054 if (toplevel)
964 { 1055 {
965 return result; 1056 return result;
@@ -982,24 +1073,29 @@ namespace verbly {
982 1073
983 case type::singleton: 1074 case type::singleton:
984 { 1075 {
985 switch (singleton_.comparison_) 1076 if (singleton_.value_.getType() == binding::type::field)
986 { 1077 {
987 case comparison::equals: 1078 return {};
988 case comparison::does_not_equal: 1079 } else {
989 case comparison::is_greater_than: 1080 switch (singleton_.comparison_)
990 case comparison::is_at_most:
991 case comparison::is_less_than:
992 case comparison::is_at_least:
993 case comparison::is_like:
994 case comparison::is_not_like:
995 { 1081 {
996 return {singleton_.value_}; 1082 case comparison::equals:
997 } 1083 case comparison::does_not_equal:
1084 case comparison::is_greater_than:
1085 case comparison::is_at_most:
1086 case comparison::is_less_than:
1087 case comparison::is_at_least:
1088 case comparison::is_like:
1089 case comparison::is_not_like:
1090 {
1091 return {singleton_.value_};
1092 }
998 1093
999 case comparison::is_not_null: 1094 case comparison::is_not_null:
1000 case comparison::is_null: 1095 case comparison::is_null:
1001 { 1096 {
1002 return {}; 1097 return {};
1098 }
1003 } 1099 }
1004 } 1100 }
1005 } 1101 }
@@ -1080,7 +1176,7 @@ namespace verbly {
1080 throw std::domain_error("Cannot get children of non-group condition"); 1176 throw std::domain_error("Cannot get children of non-group condition");
1081 } 1177 }
1082 } 1178 }
1083 1179
1084 statement::condition statement::condition::flatten() const 1180 statement::condition statement::condition::flatten() const
1085 { 1181 {
1086 switch (type_) 1182 switch (type_)
@@ -1090,15 +1186,15 @@ namespace verbly {
1090 { 1186 {
1091 return *this; 1187 return *this;
1092 } 1188 }
1093 1189
1094 case type::group: 1190 case type::group:
1095 { 1191 {
1096 condition result(group_.orlogic_); 1192 condition result(group_.orlogic_);
1097 1193
1098 for (const condition& child : group_.children_) 1194 for (const condition& child : group_.children_)
1099 { 1195 {
1100 condition newChild = child.flatten(); 1196 condition newChild = child.flatten();
1101 1197
1102 if ((newChild.type_ == type::group) && (newChild.group_.orlogic_ == group_.orlogic_)) 1198 if ((newChild.type_ == type::group) && (newChild.group_.orlogic_ == group_.orlogic_))
1103 { 1199 {
1104 for (condition subChild : std::move(newChild.group_.children_)) 1200 for (condition subChild : std::move(newChild.group_.children_))
@@ -1109,7 +1205,39 @@ namespace verbly {
1109 result += std::move(newChild); 1205 result += std::move(newChild);
1110 } 1206 }
1111 } 1207 }
1112 1208
1209 return result;
1210 }
1211 }
1212 }
1213
1214 statement::condition statement::condition::resolveCompareFields(object context, std::string tableName) const
1215 {
1216 switch (type_)
1217 {
1218 case type::empty:
1219 {
1220 return *this;
1221 }
1222
1223 case type::singleton:
1224 {
1225 if ((singleton_.parentObject_ != object::undefined) && (singleton_.parentObject_ == context))
1226 {
1227 return condition(singleton_.table_, singleton_.column_, singleton_.comparison_, {tableName, singleton_.value_.getColumn()});
1228 } else {
1229 return *this;
1230 }
1231 }
1232
1233 case type::group:
1234 {
1235 condition result(group_.orlogic_);
1236 for (const condition& cond : group_.children_)
1237 {
1238 result += cond.resolveCompareFields(context, tableName);
1239 }
1240
1113 return result; 1241 return result;
1114 } 1242 }
1115 } 1243 }
diff --git a/lib/statement.h b/lib/statement.h index 15c4ac3..2fadf05 100644 --- a/lib/statement.h +++ b/lib/statement.h
@@ -140,7 +140,7 @@ namespace verbly {
140 140
141 condition(std::string table, std::string column, bool isNull); 141 condition(std::string table, std::string column, bool isNull);
142 142
143 condition(std::string table, std::string column, comparison comp, binding value); 143 condition(std::string table, std::string column, comparison comp, binding value, object parentObject = object::undefined);
144 144
145 // Group 145 // Group
146 146
@@ -160,6 +160,8 @@ namespace verbly {
160 160
161 condition flatten() const; 161 condition flatten() const;
162 162
163 condition resolveCompareFields(object context, std::string tableName) const;
164
163 private: 165 private:
164 union { 166 union {
165 struct { 167 struct {
@@ -167,6 +169,7 @@ namespace verbly {
167 std::string column_; 169 std::string column_;
168 comparison comparison_; 170 comparison comparison_;
169 binding value_; 171 binding value_;
172 object parentObject_;
170 } singleton_; 173 } singleton_;
171 struct { 174 struct {
172 std::list<condition> children_; 175 std::list<condition> children_;
@@ -251,7 +254,6 @@ namespace verbly {
251 : (context == object::word) ? "words" 254 : (context == object::word) ? "words"
252 : (context == object::frame) ? "frames" 255 : (context == object::frame) ? "frames"
253 : (context == object::part) ? "parts" 256 : (context == object::part) ? "parts"
254 : (context == object::lemma) ? "lemmas_forms"
255 : (context == object::form) ? "forms" 257 : (context == object::form) ? "forms"
256 : (context == object::pronunciation) ? "pronunciations" 258 : (context == object::pronunciation) ? "pronunciations"
257 : throw std::domain_error("Provided context has no associated table"); 259 : throw std::domain_error("Provided context has no associated table");
@@ -259,7 +261,7 @@ namespace verbly {
259 261
260 static const std::list<field> getSelectForContext(object context); 262 static const std::list<field> getSelectForContext(object context);
261 263
262 statement(std::string tableName, filter clause, int nextTableId = 0, int nextWithId = 0); 264 statement(object context, std::string tableName, filter clause, int nextTableId = 0, int nextWithId = 0);
263 265
264 condition parseFilter(filter queryFilter); 266 condition parseFilter(filter queryFilter);
265 267
@@ -272,6 +274,7 @@ namespace verbly {
272 int nextTableId_; 274 int nextTableId_;
273 int nextWithId_; 275 int nextWithId_;
274 276
277 object context_;
275 std::map<std::string, std::string> tables_; 278 std::map<std::string, std::string> tables_;
276 std::string topTable_; 279 std::string topTable_;
277 std::list<join> joins_; 280 std::list<join> joins_;
diff --git a/lib/token.cpp b/lib/token.cpp index d2024b8..735aa7e 100644 --- a/lib/token.cpp +++ b/lib/token.cpp
@@ -261,7 +261,7 @@ namespace verbly {
261 { 261 {
262 switch (type_) 262 switch (type_)
263 { 263 {
264 case type::word: return word_.word_.getInflections(word_.category_).front(); 264 case type::word: return word_.word_.getInflections(word_.category_).front().getText();
265 case type::literal: return literal_; 265 case type::literal: return literal_;
266 case type::part: throw std::domain_error("Cannot compile incomplete token"); 266 case type::part: throw std::domain_error("Cannot compile incomplete token");
267 case type::fillin: throw std::domain_error("Cannot compile incomplete token"); 267 case type::fillin: throw std::domain_error("Cannot compile incomplete token");
diff --git a/lib/verbly.h b/lib/verbly.h index 0f48a8c..5bf204d 100644 --- a/lib/verbly.h +++ b/lib/verbly.h
@@ -11,7 +11,6 @@
11#include "word.h" 11#include "word.h"
12#include "frame.h" 12#include "frame.h"
13#include "part.h" 13#include "part.h"
14#include "lemma.h"
15#include "form.h" 14#include "form.h"
16#include "pronunciation.h" 15#include "pronunciation.h"
17#include "token.h" 16#include "token.h"
diff --git a/lib/word.cpp b/lib/word.cpp index d75159c..4dea569 100644 --- a/lib/word.cpp +++ b/lib/word.cpp
@@ -16,7 +16,6 @@ namespace verbly {
16 const field word::adjectivePosition = field::integerField(object::word, "position", true); 16 const field word::adjectivePosition = field::integerField(object::word, "position", true);
17 17
18 const field word::notions = field::joinField(object::word, "notion_id", object::notion); 18 const field word::notions = field::joinField(object::word, "notion_id", object::notion);
19 const field word::lemmas = field::joinField(object::word, "lemma_id", object::lemma);
20 const field word::frames = field::joinField(object::word, "group_id", object::frame, true); 19 const field word::frames = field::joinField(object::word, "group_id", object::frame, true);
21 20
22 const field word::antonyms = field::selfJoin(object::word, "word_id", "antonymy", "antonym_2_id", "antonym_1_id"); 21 const field word::antonyms = field::selfJoin(object::word, "word_id", "antonymy", "antonym_2_id", "antonym_1_id");
@@ -39,6 +38,11 @@ namespace verbly {
39 const field word::regionalTerms = field::selfJoin(object::word, "word_id", "regionality", "domain_id", "term_id"); 38 const field word::regionalTerms = field::selfJoin(object::word, "word_id", "regionality", "domain_id", "term_id");
40 const field word::regionalDomains = field::selfJoin(object::word, "word_id", "regionality", "term_id", "domain_id"); 39 const field word::regionalDomains = field::selfJoin(object::word, "word_id", "regionality", "term_id", "domain_id");
41 40
41 field word::forms(inflection category)
42 {
43 return field::joinThroughWhere(object::word, "lemma_id", object::form, "lemmas_forms", "form_id", "category", static_cast<int>(category));
44 }
45
42 word::word(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) 46 word::word(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true)
43 { 47 {
44 id_ = sqlite3_column_int(row, 0); 48 id_ = sqlite3_column_int(row, 0);
@@ -78,21 +82,6 @@ namespace verbly {
78 return notion_; 82 return notion_;
79 } 83 }
80 84
81 const lemma& word::getLemma() const
82 {
83 if (!valid_)
84 {
85 throw std::domain_error("Bad access to uninitialized word");
86 }
87
88 if (!lemma_)
89 {
90 lemma_ = db_->lemmas(lemma::id == lemmaId_).first();
91 }
92
93 return lemma_;
94 }
95
96 bool word::hasFrames() const 85 bool word::hasFrames() const
97 { 86 {
98 if (!valid_) 87 if (!valid_)
@@ -133,26 +122,50 @@ namespace verbly {
133 return frames_; 122 return frames_;
134 } 123 }
135 124
136 std::string word::getBaseForm() const 125 void word::initializeFrames() const
137 { 126 {
138 return getLemma().getBaseForm().getText(); 127 initializedFrames_ = true;
128 frames_ = db_->frames(*this, {}, -1).all();
139 } 129 }
140 130
141 std::vector<std::string> word::getInflections(inflection category) const 131 const form& word::getBaseForm() const
142 { 132 {
143 std::vector<std::string> result; 133 if (!valid_)
144 for (const form& infl : getLemma().getInflections(category))
145 { 134 {
146 result.push_back(infl.getText()); 135 throw std::domain_error("Bad access to uninitialized word");
147 } 136 }
148 137
149 return result; 138 if (!forms_.count(inflection::base))
139 {
140 initializeForm(inflection::base);
141 }
142
143 return forms_.at(inflection::base).front();
150 } 144 }
151 145
152 void word::initializeFrames() const 146 bool word::hasInflection(inflection category) const
153 { 147 {
154 initializedFrames_ = true; 148 return !getInflections(category).empty();
155 frames_ = db_->frames(*this, {}, -1).all(); 149 }
150
151 const std::vector<form>& word::getInflections(inflection category) const
152 {
153 if (!valid_)
154 {
155 throw std::domain_error("Bad access to uninitialized word");
156 }
157
158 if (!forms_.count(category))
159 {
160 initializeForm(category);
161 }
162
163 return forms_.at(category);
164 }
165
166 void word::initializeForm(inflection infl) const
167 {
168 forms_[infl] = db_->forms(form::words(infl) %= *this, verbly::form::id, -1).all();
156 } 169 }
157 170
158}; 171};
diff --git a/lib/word.h b/lib/word.h index 864cee1..a16319d 100644 --- a/lib/word.h +++ b/lib/word.h
@@ -6,8 +6,8 @@
6#include "field.h" 6#include "field.h"
7#include "filter.h" 7#include "filter.h"
8#include "notion.h" 8#include "notion.h"
9#include "lemma.h"
10#include "frame.h" 9#include "frame.h"
10#include "form.h"
11 11
12struct sqlite3_stmt; 12struct sqlite3_stmt;
13 13
@@ -95,17 +95,17 @@ namespace verbly {
95 95
96 const notion& getNotion() const; 96 const notion& getNotion() const;
97 97
98 const lemma& getLemma() const;
99
100 bool hasFrames() const; 98 bool hasFrames() const;
101 99
102 const std::vector<frame>& getFrames() const; 100 const std::vector<frame>& getFrames() const;
103 101
104 // Convenience accessors 102 const form& getBaseForm() const;
103
104 bool hasInflection(inflection category) const;
105
106 const std::vector<form>& getInflections(inflection category) const;
105 107
106 std::string getBaseForm() const;
107 108
108 std::vector<std::string> getInflections(inflection infl) const;
109 109
110 // Type info 110 // Type info
111 111
@@ -127,9 +127,10 @@ namespace verbly {
127 // Relationships with other objects 127 // Relationships with other objects
128 128
129 static const field notions; 129 static const field notions;
130 static const field lemmas;
131 static const field frames; 130 static const field frames;
132 131
132 static field forms(inflection category);
133
133 // Relationships with self 134 // Relationships with self
134 135
135 static const field antonyms; 136 static const field antonyms;
@@ -154,6 +155,7 @@ namespace verbly {
154 155
155 private: 156 private:
156 157
158 void initializeForm(inflection category) const;
157 void initializeFrames() const; 159 void initializeFrames() const;
158 160
159 bool valid_ = false; 161 bool valid_ = false;
@@ -170,10 +172,9 @@ namespace verbly {
170 const database* db_; 172 const database* db_;
171 173
172 mutable notion notion_; 174 mutable notion notion_;
173 mutable lemma lemma_;
174
175 mutable bool initializedFrames_ = false; 175 mutable bool initializedFrames_ = false;
176 mutable std::vector<frame> frames_; 176 mutable std::vector<frame> frames_;
177 mutable std::map<inflection, std::vector<form>> forms_;
177 178
178 }; 179 };
179 180