diff options
author | Kelly Rauchenberger <fefferburbia@gmail.com> | 2017-01-28 12:59:42 -0500 |
---|---|---|
committer | Kelly Rauchenberger <fefferburbia@gmail.com> | 2017-01-28 12:59:42 -0500 |
commit | a7645346293ed6a912c26d0c50b6f7943f1f3072 (patch) | |
tree | d4d144e03a5e2dfcebbad2692fa71e790719d8fd /lib | |
parent | 6ba8989bbbd497f949a3e8b17abed1d0bd048347 (diff) | |
download | verbly-a7645346293ed6a912c26d0c50b6f7943f1f3072.tar.gz verbly-a7645346293ed6a912c26d0c50b6f7943f1f3072.tar.bz2 verbly-a7645346293ed6a912c26d0c50b6f7943f1f3072.zip |
Restructured verb frame schema to be more queryable
Groups are much less significant now, and they no longer have a database table, nor are they considered a top level object anymore. Instead of containing their own role data, that data is folded into the frames so that it's easier to query; as a result, each group has its own copy of the frames that it contains. Additionally, parts are considered top level objects now, and you can query for frames based on attributes of their indexed parts. Synrestrs are also contained in their own table now, so that parts can be filtered against their synrestrs; they are however not considered top level objects. Created a new type of field, the "join where" or "condition join" field, which is a normal join field that has a built in condition on a specified field. This is used to allow creating multiple distinct join fields from one object to another. This is required for the lemma::form and frame::part joins, because filters for forms of separate inflections should not be coalesced; similarly, filters on differently indexed frame parts should not be coalesced. Queries can now be ordered, ascending or descending, by a field, in addition to randomly as before. This is necessary for accessing the parts of a verb frame in the correct order, but may be useful to an end user as well. Fixed a bug with statement generation in that condition groups were not being surrounded in parentheses, which made mixing OR groups and AND groups generate inaccurate statements. This has been fixed; additionally, parentheses are not placed around the top level condition, and nested condition groups with the same logic type are coalesced, to make query strings as easy to read as possible. Also simplified the form::lemma field; it no longer conditions on the inflection of the form like the lemma::form field does. Also added a debug flag to statement::getQueryString that makes it return a query string with all of the bindings filled in, for debug use only.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/database.cpp | 60 | ||||
-rw-r--r-- | lib/database.h | 20 | ||||
-rw-r--r-- | lib/enums.h | 14 | ||||
-rw-r--r-- | lib/field.cpp | 20 | ||||
-rw-r--r-- | lib/field.h | 78 | ||||
-rw-r--r-- | lib/filter.cpp | 35 | ||||
-rw-r--r-- | lib/form.cpp | 16 | ||||
-rw-r--r-- | lib/form.h | 31 | ||||
-rw-r--r-- | lib/frame.cpp | 95 | ||||
-rw-r--r-- | lib/frame.h | 21 | ||||
-rw-r--r-- | lib/group.cpp | 61 | ||||
-rw-r--r-- | lib/group.h | 91 | ||||
-rw-r--r-- | lib/lemma.cpp | 15 | ||||
-rw-r--r-- | lib/lemma.h | 27 | ||||
-rw-r--r-- | lib/order.h | 69 | ||||
-rw-r--r-- | lib/part.cpp | 128 | ||||
-rw-r--r-- | lib/part.h | 64 | ||||
-rw-r--r-- | lib/query.h | 11 | ||||
-rw-r--r-- | lib/role.h | 60 | ||||
-rw-r--r-- | lib/statement.cpp | 164 | ||||
-rw-r--r-- | lib/statement.h | 9 | ||||
-rw-r--r-- | lib/verbly.h | 5 | ||||
-rw-r--r-- | lib/word.cpp | 36 | ||||
-rw-r--r-- | lib/word.h | 23 |
24 files changed, 644 insertions, 509 deletions
diff --git a/lib/database.cpp b/lib/database.cpp index fb00ef3..563ec31 100644 --- a/lib/database.cpp +++ b/lib/database.cpp | |||
@@ -41,39 +41,71 @@ namespace verbly { | |||
41 | sqlite3_close_v2(ppdb_); | 41 | sqlite3_close_v2(ppdb_); |
42 | } | 42 | } |
43 | 43 | ||
44 | query<notion> database::notions(filter where, bool random, int limit) const | 44 | query<notion> database::notions(filter where, order sortOrder, int limit) const |
45 | { | 45 | { |
46 | return query<notion>(*this, ppdb_, std::move(where), random, limit); | 46 | return query<notion>(*this, ppdb_, std::move(where), std::move(sortOrder), limit); |
47 | } | 47 | } |
48 | 48 | ||
49 | query<word> database::words(filter where, bool random, int limit) const | 49 | query<word> database::words(filter where, order sortOrder, int limit) const |
50 | { | 50 | { |
51 | return query<word>(*this, ppdb_, std::move(where), random, limit); | 51 | return query<word>(*this, ppdb_, std::move(where), std::move(sortOrder), limit); |
52 | } | 52 | } |
53 | 53 | ||
54 | query<group> database::groups(filter where, bool random, int limit) const | 54 | query<frame> database::frames(filter where, order sortOrder, int limit) const |
55 | { | 55 | { |
56 | return query<group>(*this, ppdb_, std::move(where), random, limit); | 56 | return query<frame>(*this, ppdb_, std::move(where), std::move(sortOrder), limit); |
57 | } | 57 | } |
58 | 58 | ||
59 | query<frame> database::frames(filter where, bool random, int limit) const | 59 | query<part> database::parts(filter where, order sortOrder, int limit) const |
60 | { | 60 | { |
61 | return query<frame>(*this, ppdb_, std::move(where), random, 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, bool random, int limit) const | 64 | query<lemma> database::lemmas(filter where, order sortOrder, int limit) const |
65 | { | 65 | { |
66 | return query<lemma>(*this, ppdb_, std::move(where), random, limit); | 66 | return query<lemma>(*this, ppdb_, std::move(where), std::move(sortOrder), limit); |
67 | } | 67 | } |
68 | 68 | ||
69 | query<form> database::forms(filter where, bool random, int limit) const | 69 | query<form> database::forms(filter where, order sortOrder, int limit) const |
70 | { | 70 | { |
71 | return query<form>(*this, ppdb_, std::move(where), random, limit); | 71 | return query<form>(*this, ppdb_, std::move(where), std::move(sortOrder), limit); |
72 | } | 72 | } |
73 | 73 | ||
74 | query<pronunciation> database::pronunciations(filter where, bool random, int limit) const | 74 | query<pronunciation> database::pronunciations(filter where, order sortOrder, int limit) const |
75 | { | 75 | { |
76 | return query<pronunciation>(*this, ppdb_, std::move(where), random, limit); | 76 | return query<pronunciation>(*this, ppdb_, std::move(where), std::move(sortOrder), limit); |
77 | } | ||
78 | |||
79 | std::set<std::string> database::synrestrs(int partId) const | ||
80 | { | ||
81 | std::string queryString = "SELECT synrestr FROM synrestrs WHERE part_id = ?"; | ||
82 | |||
83 | sqlite3_stmt* ppstmt; | ||
84 | if (sqlite3_prepare_v2(ppdb_, queryString.c_str(), queryString.length(), &ppstmt, NULL) != SQLITE_OK) | ||
85 | { | ||
86 | std::string errorMsg = sqlite3_errmsg(ppdb_); | ||
87 | sqlite3_finalize(ppstmt); | ||
88 | |||
89 | throw database_error("Error preparing query", errorMsg); | ||
90 | } | ||
91 | |||
92 | if (sqlite3_bind_int(ppstmt, 1, partId) != SQLITE_OK) | ||
93 | { | ||
94 | std::string errorMsg = sqlite3_errmsg(ppdb_); | ||
95 | sqlite3_finalize(ppstmt); | ||
96 | |||
97 | throw database_error("Error binding value to query", errorMsg); | ||
98 | } | ||
99 | |||
100 | std::set<std::string> result; | ||
101 | while (sqlite3_step(ppstmt) == SQLITE_ROW) | ||
102 | { | ||
103 | result.insert(reinterpret_cast<const char*>(sqlite3_column_blob(ppstmt, 0))); | ||
104 | } | ||
105 | |||
106 | sqlite3_finalize(ppstmt); | ||
107 | |||
108 | return result; | ||
77 | } | 109 | } |
78 | 110 | ||
79 | }; | 111 | }; |
diff --git a/lib/database.h b/lib/database.h index ef50739..0b10eba 100644 --- a/lib/database.h +++ b/lib/database.h | |||
@@ -4,13 +4,15 @@ | |||
4 | #include <string> | 4 | #include <string> |
5 | #include <exception> | 5 | #include <exception> |
6 | #include <list> | 6 | #include <list> |
7 | #include <set> | ||
7 | #include "notion.h" | 8 | #include "notion.h" |
8 | #include "word.h" | 9 | #include "word.h" |
9 | #include "group.h" | ||
10 | #include "frame.h" | 10 | #include "frame.h" |
11 | #include "part.h" | ||
11 | #include "lemma.h" | 12 | #include "lemma.h" |
12 | #include "form.h" | 13 | #include "form.h" |
13 | #include "pronunciation.h" | 14 | #include "pronunciation.h" |
15 | #include "order.h" | ||
14 | 16 | ||
15 | struct sqlite3; | 17 | struct sqlite3; |
16 | 18 | ||
@@ -46,19 +48,21 @@ namespace verbly { | |||
46 | 48 | ||
47 | // Queries | 49 | // Queries |
48 | 50 | ||
49 | query<notion> notions(filter where, bool random = true, int limit = 1) const; | 51 | query<notion> notions(filter where, order sortOrder = {}, int limit = 1) const; |
50 | 52 | ||
51 | query<word> words(filter where, bool random = true, int limit = 1) const; | 53 | query<word> words(filter where, order sortOrder = {}, int limit = 1) const; |
52 | 54 | ||
53 | query<group> groups(filter where, bool random = true, int limit = 1) const; | 55 | query<frame> frames(filter where, order sortOrder = {}, int limit = 1) const; |
54 | 56 | ||
55 | query<frame> frames(filter where, bool random = true, int limit = 1) const; | 57 | query<part> parts(filter where, order sortOrder = {}, int limit = 1) const; |
56 | 58 | ||
57 | query<lemma> lemmas(filter where, bool random = true, int limit = 1) const; | 59 | query<lemma> lemmas(filter where, order sortOrder = {}, int limit = 1) const; |
58 | 60 | ||
59 | query<form> forms(filter where, bool random = true, int limit = 1) const; | 61 | query<form> forms(filter where, order sortOrder = {}, int limit = 1) const; |
60 | 62 | ||
61 | query<pronunciation> pronunciations(filter where, bool random = true, int limit = 1) const; | 63 | query<pronunciation> pronunciations(filter where, order sortOrder = {}, int limit = 1) const; |
64 | |||
65 | std::set<std::string> synrestrs(int partId) const; | ||
62 | 66 | ||
63 | private: | 67 | private: |
64 | 68 | ||
diff --git a/lib/enums.h b/lib/enums.h index e634959..2646fa4 100644 --- a/lib/enums.h +++ b/lib/enums.h | |||
@@ -33,13 +33,23 @@ namespace verbly { | |||
33 | undefined = -1, | 33 | undefined = -1, |
34 | notion = 0, | 34 | notion = 0, |
35 | word = 1, | 35 | word = 1, |
36 | group = 2, | 36 | frame = 2, |
37 | frame = 3, | 37 | part = 3, |
38 | lemma = 4, | 38 | lemma = 4, |
39 | form = 5, | 39 | form = 5, |
40 | pronunciation = 6 | 40 | pronunciation = 6 |
41 | }; | 41 | }; |
42 | 42 | ||
43 | enum class part_type { | ||
44 | invalid = -1, | ||
45 | noun_phrase = 0, | ||
46 | verb = 1, | ||
47 | preposition = 2, | ||
48 | adjective = 3, | ||
49 | adverb = 4, | ||
50 | literal = 5 | ||
51 | }; | ||
52 | |||
43 | }; | 53 | }; |
44 | 54 | ||
45 | #endif /* end of include guard: ENUMS_H_260BA847 */ | 55 | #endif /* end of include guard: ENUMS_H_260BA847 */ |
diff --git a/lib/field.cpp b/lib/field.cpp index deecb06..5b51ef4 100644 --- a/lib/field.cpp +++ b/lib/field.cpp | |||
@@ -48,6 +48,11 @@ namespace verbly { | |||
48 | return filter(*this, filter::comparison::int_equals, static_cast<int>(value)); | 48 | return filter(*this, filter::comparison::int_equals, static_cast<int>(value)); |
49 | } | 49 | } |
50 | 50 | ||
51 | filter field::operator==(part_type value) const | ||
52 | { | ||
53 | return filter(*this, filter::comparison::int_equals, static_cast<int>(value)); | ||
54 | } | ||
55 | |||
51 | filter field::operator==(bool value) const | 56 | filter field::operator==(bool value) const |
52 | { | 57 | { |
53 | return filter(*this, filter::comparison::boolean_equals, value); | 58 | return filter(*this, filter::comparison::boolean_equals, value); |
@@ -68,6 +73,21 @@ namespace verbly { | |||
68 | return filter(*this, filter::comparison::string_is_like, std::move(value)); | 73 | return filter(*this, filter::comparison::string_is_like, std::move(value)); |
69 | } | 74 | } |
70 | 75 | ||
76 | filter field::operator==(const char* value) const | ||
77 | { | ||
78 | return filter(*this, filter::comparison::string_equals, std::string(value)); | ||
79 | } | ||
80 | |||
81 | filter field::operator!=(const char* value) const | ||
82 | { | ||
83 | return filter(*this, filter::comparison::string_does_not_equal, std::string(value)); | ||
84 | } | ||
85 | |||
86 | filter field::operator%=(const char* value) const | ||
87 | { | ||
88 | return filter(*this, filter::comparison::string_is_like, std::string(value)); | ||
89 | } | ||
90 | |||
71 | field::operator filter() const | 91 | field::operator filter() const |
72 | { | 92 | { |
73 | if (isJoin()) | 93 | if (isJoin()) |
diff --git a/lib/field.h b/lib/field.h index f61e038..b4bf02d 100644 --- a/lib/field.h +++ b/lib/field.h | |||
@@ -17,6 +17,7 @@ namespace verbly { | |||
17 | integer, | 17 | integer, |
18 | boolean, | 18 | boolean, |
19 | join, | 19 | join, |
20 | join_where, | ||
20 | join_through, | 21 | join_through, |
21 | hierarchal_join | 22 | hierarchal_join |
22 | }; | 23 | }; |
@@ -95,6 +96,17 @@ namespace verbly { | |||
95 | return field(obj, type::join, name, nullable, table); | 96 | return field(obj, type::join, name, nullable, table); |
96 | } | 97 | } |
97 | 98 | ||
99 | static field joinWhere( | ||
100 | object obj, | ||
101 | const char* name, | ||
102 | object joinWith, | ||
103 | const field& conditionField, | ||
104 | int conditionValue, | ||
105 | bool nullable = false) | ||
106 | { | ||
107 | return field(obj, type::join_where, name, nullable, 0, joinWith, 0, 0, 0, &conditionField, conditionValue); | ||
108 | } | ||
109 | |||
98 | static field joinThrough( | 110 | static field joinThrough( |
99 | object obj, | 111 | object obj, |
100 | const char* name, | 112 | const char* name, |
@@ -151,7 +163,10 @@ namespace verbly { | |||
151 | 163 | ||
152 | bool isJoin() const | 164 | bool isJoin() const |
153 | { | 165 | { |
154 | return ((type_ == type::join) || (type_ == type::join_through) || (type_ == type::hierarchal_join)); | 166 | return ((type_ == type::join) |
167 | || (type_ == type::join_where) | ||
168 | || (type_ == type::join_through) | ||
169 | || (type_ == type::hierarchal_join)); | ||
155 | } | 170 | } |
156 | 171 | ||
157 | const char* getColumn() const | 172 | const char* getColumn() const |
@@ -180,7 +195,7 @@ namespace verbly { | |||
180 | { | 195 | { |
181 | return (type_ == type::hierarchal_join) | 196 | return (type_ == type::hierarchal_join) |
182 | ? object_ | 197 | ? object_ |
183 | : ((type_ == type::join) || (type_ == type::join_through)) | 198 | : ((type_ == type::join) || (type_ == type::join_where) || (type_ == type::join_through)) |
184 | ? joinObject_ | 199 | ? joinObject_ |
185 | : throw std::domain_error("Non-join fields don't have join objects"); | 200 | : throw std::domain_error("Non-join fields don't have join objects"); |
186 | } | 201 | } |
@@ -209,6 +224,22 @@ namespace verbly { | |||
209 | : throw std::domain_error("Only many-to-many join fields have a foreign join column"); | 224 | : throw std::domain_error("Only many-to-many join fields have a foreign join column"); |
210 | } | 225 | } |
211 | 226 | ||
227 | // Condition joins | ||
228 | |||
229 | const field& getConditionField() const | ||
230 | { | ||
231 | return (type_ == type::join_where) | ||
232 | ? *conditionField_ | ||
233 | : throw std::domain_error("Only condition join fields have a condition field"); | ||
234 | } | ||
235 | |||
236 | int getConditionValue() const | ||
237 | { | ||
238 | return (type_ == type::join_where) | ||
239 | ? conditionValue_ | ||
240 | : throw std::domain_error("Only condition join fields have a condition value"); | ||
241 | } | ||
242 | |||
212 | // Ordering | 243 | // Ordering |
213 | 244 | ||
214 | bool operator<(const field& other) const | 245 | bool operator<(const field& other) const |
@@ -217,20 +248,30 @@ namespace verbly { | |||
217 | // However, there do exist a number of relationships from an object to | 248 | // However, there do exist a number of relationships from an object to |
218 | // itself, such as notion hypernymy/hyponymy. Hypernymy and hyponymy have | 249 | // itself, such as notion hypernymy/hyponymy. Hypernymy and hyponymy have |
219 | // the same object (notion), the same column (notion_id), and the same | 250 | // the same object (notion), the same column (notion_id), and the same |
220 | // table (hypernymy); however, they have different join columns. | 251 | // table (hypernymy); however, they have different join columns. For |
221 | return std::tie(object_, column_, table_, joinColumn_) < std::tie(other.object_, other.column_, other.table_, other.joinColumn_); | 252 | // condition joins, the condition field and condition value are also |
253 | // significant. | ||
254 | if (conditionField_) | ||
255 | { | ||
256 | return std::tie(object_, column_, table_, joinColumn_, *conditionField_, conditionValue_) | ||
257 | < std::tie(other.object_, other.column_, other.table_, other.joinColumn_, *other.conditionField_, other.conditionValue_); | ||
258 | } else { | ||
259 | return std::tie(object_, column_, table_, joinColumn_) < std::tie(other.object_, other.column_, other.table_, other.joinColumn_); | ||
260 | } | ||
222 | } | 261 | } |
223 | 262 | ||
224 | // Equality | 263 | // Equality |
225 | 264 | ||
226 | bool operator==(const field& other) const | 265 | bool operator==(const field& other) const |
227 | { | 266 | { |
228 | // For the most part, (object, column) uniquely identifies fields. | 267 | // See operator<() for documentation. |
229 | // However, there do exist a number of relationships from an object to | 268 | if (conditionField_) |
230 | // itself, such as notion hypernymy/hyponymy. Hypernymy and hyponymy have | 269 | { |
231 | // the same object (notion), the same column (notion_id), and the same | 270 | return std::tie(object_, column_, table_, joinColumn_, *conditionField_, conditionValue_) |
232 | // table (hypernymy); however, they have different join columns. | 271 | == std::tie(other.object_, other.column_, other.table_, other.joinColumn_, *other.conditionField_, other.conditionValue_); |
233 | return std::tie(object_, column_, table_, joinColumn_) == std::tie(other.object_, other.column_, other.table_, other.joinColumn_); | 272 | } else { |
273 | return std::tie(object_, column_, table_, joinColumn_) == std::tie(other.object_, other.column_, other.table_, other.joinColumn_); | ||
274 | } | ||
234 | } | 275 | } |
235 | 276 | ||
236 | // Filter construction | 277 | // Filter construction |
@@ -245,6 +286,7 @@ namespace verbly { | |||
245 | filter operator==(part_of_speech value) const; // Part of speech equality | 286 | filter operator==(part_of_speech value) const; // Part of speech equality |
246 | filter operator==(positioning value) const; // Adjective positioning equality | 287 | filter operator==(positioning value) const; // Adjective positioning equality |
247 | filter operator==(inflection value) const; // Inflection category equality | 288 | filter operator==(inflection value) const; // Inflection category equality |
289 | filter operator==(part_type value) const; // Verb frame part type equality | ||
248 | 290 | ||
249 | filter operator==(bool value) const; // Boolean equality | 291 | filter operator==(bool value) const; // Boolean equality |
250 | 292 | ||
@@ -252,6 +294,10 @@ namespace verbly { | |||
252 | filter operator!=(std::string value) const; // String inequality | 294 | filter operator!=(std::string value) const; // String inequality |
253 | filter operator%=(std::string value) const; // String matching | 295 | filter operator%=(std::string value) const; // String matching |
254 | 296 | ||
297 | filter operator==(const char* value) const; // String equality | ||
298 | filter operator!=(const char* value) const; // String inequality | ||
299 | filter operator%=(const char* value) const; // String matching | ||
300 | |||
255 | operator filter() const; // Non-nullity | 301 | operator filter() const; // Non-nullity |
256 | filter operator!() const; // Nullity | 302 | filter operator!() const; // Nullity |
257 | 303 | ||
@@ -270,7 +316,9 @@ namespace verbly { | |||
270 | object joinObject = object::undefined, | 316 | object joinObject = object::undefined, |
271 | const char* foreignColumn = 0, | 317 | const char* foreignColumn = 0, |
272 | const char* joinColumn = 0, | 318 | const char* joinColumn = 0, |
273 | const char* foreignJoinColumn = 0) : | 319 | const char* foreignJoinColumn = 0, |
320 | const field* conditionField = 0, | ||
321 | int conditionValue = 0) : | ||
274 | object_(obj), | 322 | object_(obj), |
275 | type_(datatype), | 323 | type_(datatype), |
276 | column_(column), | 324 | column_(column), |
@@ -279,7 +327,9 @@ namespace verbly { | |||
279 | joinObject_(joinObject), | 327 | joinObject_(joinObject), |
280 | foreignColumn_(foreignColumn), | 328 | foreignColumn_(foreignColumn), |
281 | joinColumn_(joinColumn), | 329 | joinColumn_(joinColumn), |
282 | foreignJoinColumn_(foreignJoinColumn) | 330 | foreignJoinColumn_(foreignJoinColumn), |
331 | conditionField_(conditionField), | ||
332 | conditionValue_(conditionValue) | ||
283 | { | 333 | { |
284 | } | 334 | } |
285 | 335 | ||
@@ -300,6 +350,10 @@ namespace verbly { | |||
300 | const char* joinColumn_ = 0; | 350 | const char* joinColumn_ = 0; |
301 | const char* foreignJoinColumn_ = 0; | 351 | const char* foreignJoinColumn_ = 0; |
302 | 352 | ||
353 | // Condition joins | ||
354 | const field* conditionField_ = 0; | ||
355 | int conditionValue_ = 0; | ||
356 | |||
303 | }; | 357 | }; |
304 | 358 | ||
305 | }; | 359 | }; |
diff --git a/lib/filter.cpp b/lib/filter.cpp index ceb9327..ab46df2 100644 --- a/lib/filter.cpp +++ b/lib/filter.cpp | |||
@@ -3,8 +3,8 @@ | |||
3 | #include <map> | 3 | #include <map> |
4 | #include "notion.h" | 4 | #include "notion.h" |
5 | #include "word.h" | 5 | #include "word.h" |
6 | #include "group.h" | ||
7 | #include "frame.h" | 6 | #include "frame.h" |
7 | #include "part.h" | ||
8 | #include "lemma.h" | 8 | #include "lemma.h" |
9 | #include "form.h" | 9 | #include "form.h" |
10 | #include "pronunciation.h" | 10 | #include "pronunciation.h" |
@@ -594,6 +594,7 @@ namespace verbly { | |||
594 | switch (joinOn.getType()) | 594 | switch (joinOn.getType()) |
595 | { | 595 | { |
596 | case field::type::join: | 596 | case field::type::join: |
597 | case field::type::join_where: | ||
597 | case field::type::join_through: | 598 | case field::type::join_through: |
598 | { | 599 | { |
599 | switch (filterType) | 600 | switch (filterType) |
@@ -1108,8 +1109,8 @@ namespace verbly { | |||
1108 | } | 1109 | } |
1109 | 1110 | ||
1110 | case object::word: | 1111 | case object::word: |
1111 | case object::group: | ||
1112 | case object::frame: | 1112 | case object::frame: |
1113 | case object::part: | ||
1113 | case object::lemma: | 1114 | case object::lemma: |
1114 | case object::form: | 1115 | case object::form: |
1115 | case object::pronunciation: | 1116 | case object::pronunciation: |
@@ -1134,10 +1135,10 @@ namespace verbly { | |||
1134 | return *this; | 1135 | return *this; |
1135 | } | 1136 | } |
1136 | 1137 | ||
1137 | case object::group: | ||
1138 | case object::frame: | 1138 | case object::frame: |
1139 | case object::part: | ||
1139 | { | 1140 | { |
1140 | return (verbly::word::group %= *this); | 1141 | return (verbly::word::frame %= *this); |
1141 | } | 1142 | } |
1142 | 1143 | ||
1143 | case object::lemma: | 1144 | case object::lemma: |
@@ -1148,12 +1149,12 @@ namespace verbly { | |||
1148 | } | 1149 | } |
1149 | } | 1150 | } |
1150 | 1151 | ||
1151 | case object::group: | 1152 | case object::frame: |
1152 | { | 1153 | { |
1153 | switch (singleton_.filterField.getObject()) | 1154 | switch (singleton_.filterField.getObject()) |
1154 | { | 1155 | { |
1155 | case object::undefined: | 1156 | case object::undefined: |
1156 | case object::group: | 1157 | case object::frame: |
1157 | { | 1158 | { |
1158 | return *this; | 1159 | return *this; |
1159 | } | 1160 | } |
@@ -1164,34 +1165,34 @@ namespace verbly { | |||
1164 | case object::form: | 1165 | case object::form: |
1165 | case object::pronunciation: | 1166 | case object::pronunciation: |
1166 | { | 1167 | { |
1167 | return (verbly::group::word %= *this); | 1168 | return (verbly::frame::word %= *this); |
1168 | } | 1169 | } |
1169 | 1170 | ||
1170 | case object::frame: | 1171 | case object::part: |
1171 | { | 1172 | { |
1172 | return (verbly::group::frame %= *this); | 1173 | return (verbly::frame::part() %= *this); |
1173 | } | 1174 | } |
1174 | } | 1175 | } |
1175 | } | 1176 | } |
1176 | 1177 | ||
1177 | case object::frame: | 1178 | case object::part: |
1178 | { | 1179 | { |
1179 | switch (singleton_.filterField.getObject()) | 1180 | switch (singleton_.filterField.getObject()) |
1180 | { | 1181 | { |
1181 | case object::undefined: | 1182 | case object::undefined: |
1182 | case object::frame: | 1183 | case object::part: |
1183 | { | 1184 | { |
1184 | return *this; | 1185 | return *this; |
1185 | } | 1186 | } |
1186 | 1187 | ||
1187 | case object::notion: | 1188 | case object::notion: |
1188 | case object::word: | 1189 | case object::word: |
1189 | case object::group: | 1190 | case object::frame: |
1190 | case object::lemma: | 1191 | case object::lemma: |
1191 | case object::form: | 1192 | case object::form: |
1192 | case object::pronunciation: | 1193 | case object::pronunciation: |
1193 | { | 1194 | { |
1194 | return (verbly::frame::group %= *this); | 1195 | return (verbly::part::frame %= *this); |
1195 | } | 1196 | } |
1196 | } | 1197 | } |
1197 | } | 1198 | } |
@@ -1202,8 +1203,8 @@ namespace verbly { | |||
1202 | { | 1203 | { |
1203 | case object::notion: | 1204 | case object::notion: |
1204 | case object::word: | 1205 | case object::word: |
1205 | case object::group: | ||
1206 | case object::frame: | 1206 | case object::frame: |
1207 | case object::part: | ||
1207 | { | 1208 | { |
1208 | return verbly::lemma::word %= *this; | 1209 | return verbly::lemma::word %= *this; |
1209 | } | 1210 | } |
@@ -1228,11 +1229,11 @@ namespace verbly { | |||
1228 | { | 1229 | { |
1229 | case object::notion: | 1230 | case object::notion: |
1230 | case object::word: | 1231 | case object::word: |
1231 | case object::group: | ||
1232 | case object::frame: | 1232 | case object::frame: |
1233 | case object::part: | ||
1233 | case object::lemma: | 1234 | case object::lemma: |
1234 | { | 1235 | { |
1235 | return verbly::form::lemma(inflection::base) %= *this; | 1236 | return verbly::form::lemma %= *this; |
1236 | } | 1237 | } |
1237 | 1238 | ||
1238 | case object::undefined: | 1239 | case object::undefined: |
@@ -1254,8 +1255,8 @@ namespace verbly { | |||
1254 | { | 1255 | { |
1255 | case object::notion: | 1256 | case object::notion: |
1256 | case object::word: | 1257 | case object::word: |
1257 | case object::group: | ||
1258 | case object::frame: | 1258 | case object::frame: |
1259 | case object::part: | ||
1259 | case object::lemma: | 1260 | case object::lemma: |
1260 | case object::form: | 1261 | case object::form: |
1261 | { | 1262 | { |
diff --git a/lib/form.cpp b/lib/form.cpp index 5d4c343..4811f14 100644 --- a/lib/form.cpp +++ b/lib/form.cpp | |||
@@ -16,11 +16,9 @@ namespace verbly { | |||
16 | const field form::complexity = field::integerField(object::form, "complexity"); | 16 | const field form::complexity = field::integerField(object::form, "complexity"); |
17 | const field form::proper = field::booleanField(object::form, "proper"); | 17 | const field form::proper = field::booleanField(object::form, "proper"); |
18 | 18 | ||
19 | const field form::lemma = field::joinField(object::form, "form_id", object::lemma); | ||
19 | const field form::pronunciation = field::joinThrough(object::form, "form_id", object::pronunciation, "forms_pronunciations", "pronunciation_id"); | 20 | const field form::pronunciation = field::joinThrough(object::form, "form_id", object::pronunciation, "forms_pronunciations", "pronunciation_id"); |
20 | 21 | ||
21 | const field form::lemmaJoin = field::joinField(object::form, "form_id", object::lemma); | ||
22 | const field form::inflectionCategory = field::integerField("lemmas_forms", "category"); | ||
23 | |||
24 | form::form(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) | 22 | form::form(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) |
25 | { | 23 | { |
26 | id_ = sqlite3_column_int(row, 0); | 24 | id_ = sqlite3_column_int(row, 0); |
@@ -29,16 +27,6 @@ namespace verbly { | |||
29 | proper_ = (sqlite3_column_int(row, 3) == 1); | 27 | proper_ = (sqlite3_column_int(row, 3) == 1); |
30 | } | 28 | } |
31 | 29 | ||
32 | filter operator%=(form::inflection_field check, filter joinCondition) | ||
33 | { | ||
34 | return (form::lemmaJoin %= (joinCondition && (form::inflectionCategory == check.getCategory()))); | ||
35 | } | ||
36 | |||
37 | form::inflection_field::operator filter() const | ||
38 | { | ||
39 | return (form::lemmaJoin %= (form::inflectionCategory == category_)); | ||
40 | } | ||
41 | |||
42 | const std::vector<pronunciation>& form::getPronunciations() const | 30 | const std::vector<pronunciation>& form::getPronunciations() const |
43 | { | 31 | { |
44 | if (!valid_) | 32 | if (!valid_) |
@@ -48,7 +36,7 @@ namespace verbly { | |||
48 | 36 | ||
49 | if (!initializedPronunciations_) | 37 | if (!initializedPronunciations_) |
50 | { | 38 | { |
51 | pronunciations_ = db_->pronunciations(pronunciation::form %= *this, false, -1).all(); | 39 | pronunciations_ = db_->pronunciations(pronunciation::form %= *this, verbly::pronunciation::id, -1).all(); |
52 | initializedPronunciations_ = true; | 40 | initializedPronunciations_ = true; |
53 | } | 41 | } |
54 | 42 | ||
diff --git a/lib/form.h b/lib/form.h index aca5b2f..cf64117 100644 --- a/lib/form.h +++ b/lib/form.h | |||
@@ -104,33 +104,9 @@ namespace verbly { | |||
104 | 104 | ||
105 | // Relationships to other objects | 105 | // Relationships to other objects |
106 | 106 | ||
107 | static const field pronunciation; | 107 | static const field lemma; |
108 | |||
109 | class inflection_field { | ||
110 | public: | ||
111 | |||
112 | inflection_field(inflection category) : category_(category) | ||
113 | { | ||
114 | } | ||
115 | |||
116 | const inflection getCategory() const | ||
117 | { | ||
118 | return category_; | ||
119 | } | ||
120 | 108 | ||
121 | operator filter() const; | 109 | static const field pronunciation; |
122 | |||
123 | private: | ||
124 | |||
125 | const inflection category_; | ||
126 | }; | ||
127 | |||
128 | static const inflection_field lemma(inflection category) | ||
129 | { | ||
130 | return inflection_field(category); | ||
131 | } | ||
132 | |||
133 | friend filter operator%=(form::inflection_field check, filter joinCondition); | ||
134 | 110 | ||
135 | private: | 111 | private: |
136 | bool valid_ = false; | 112 | bool valid_ = false; |
@@ -145,9 +121,6 @@ namespace verbly { | |||
145 | mutable bool initializedPronunciations_ = false; | 121 | mutable bool initializedPronunciations_ = false; |
146 | mutable std::vector<class pronunciation> pronunciations_; | 122 | mutable std::vector<class pronunciation> pronunciations_; |
147 | 123 | ||
148 | static const field lemmaJoin; | ||
149 | static const field inflectionCategory; | ||
150 | |||
151 | }; | 124 | }; |
152 | 125 | ||
153 | }; | 126 | }; |
diff --git a/lib/frame.cpp b/lib/frame.cpp index 8cab56b..a73fbda 100644 --- a/lib/frame.cpp +++ b/lib/frame.cpp | |||
@@ -1,95 +1,36 @@ | |||
1 | #include "frame.h" | 1 | #include "frame.h" |
2 | #include <sqlite3.h> | 2 | #include <sqlite3.h> |
3 | #include <json.hpp> | 3 | #include "database.h" |
4 | #include "query.h" | ||
4 | 5 | ||
5 | namespace verbly { | 6 | namespace verbly { |
6 | 7 | ||
7 | const object frame::objectType = object::frame; | 8 | const object frame::objectType = object::frame; |
8 | 9 | ||
9 | const std::list<std::string> frame::select = {"frame_id", "data"}; | 10 | const std::list<std::string> frame::select = {"frame_id", "group_id", "length"}; |
10 | 11 | ||
11 | const field frame::id = field::integerField(object::frame, "frame_id"); | 12 | const field frame::id = field::integerField(object::frame, "frame_id"); |
13 | const field frame::length = field::integerField(object::frame, "length"); | ||
12 | 14 | ||
13 | const field frame::group = field::joinThrough(object::frame, "frame_id", object::group, "groups_frames", "group_id"); | 15 | const field frame::word = field::joinField(object::frame, "group_id", object::word); |
14 | 16 | ||
15 | frame::frame(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) | 17 | field frame::part() |
16 | { | 18 | { |
17 | id_ = sqlite3_column_int(row, 0); | 19 | return field::joinField(object::frame, "frame_id", object::part); |
18 | 20 | } | |
19 | std::string partsJsonStr(reinterpret_cast<const char*>(sqlite3_column_blob(row, 1))); | ||
20 | nlohmann::json partsJson = nlohmann::json::parse(std::move(partsJsonStr)); | ||
21 | |||
22 | for (const nlohmann::json& partJson : partsJson) | ||
23 | { | ||
24 | part::type partType = static_cast<part::type>(partJson["type"].get<int>()); | ||
25 | |||
26 | switch (partType) | ||
27 | { | ||
28 | case part::type::noun_phrase: | ||
29 | { | ||
30 | std::set<std::string> synrestrs; | ||
31 | for (const nlohmann::json& synrestrJson : partJson["synrestrs"]) | ||
32 | { | ||
33 | synrestrs.insert(synrestrJson.get<std::string>()); | ||
34 | } | ||
35 | |||
36 | parts_.push_back(part::createNounPhrase( | ||
37 | partJson["role"].get<std::string>(), | ||
38 | selrestr(partJson["selrestrs"]), | ||
39 | std::move(synrestrs))); | ||
40 | |||
41 | break; | ||
42 | } | ||
43 | |||
44 | case part::type::preposition: | ||
45 | { | ||
46 | std::vector<std::string> choices; | ||
47 | for (const nlohmann::json& choiceJson : partJson["choices"]) | ||
48 | { | ||
49 | choices.push_back(choiceJson.get<std::string>()); | ||
50 | } | ||
51 | |||
52 | parts_.push_back(part::createPreposition( | ||
53 | std::move(choices), | ||
54 | partJson["literal"].get<bool>())); | ||
55 | |||
56 | break; | ||
57 | } | ||
58 | |||
59 | case part::type::verb: | ||
60 | { | ||
61 | parts_.push_back(part::createVerb()); | ||
62 | |||
63 | break; | ||
64 | } | ||
65 | |||
66 | case part::type::adjective: | ||
67 | { | ||
68 | parts_.push_back(part::createAdjective()); | ||
69 | |||
70 | break; | ||
71 | } | ||
72 | |||
73 | case part::type::adverb: | ||
74 | { | ||
75 | parts_.push_back(part::createAdverb()); | ||
76 | |||
77 | break; | ||
78 | } | ||
79 | 21 | ||
80 | case part::type::literal: | 22 | field frame::part(int index) |
81 | { | 23 | { |
82 | parts_.push_back(part::createLiteral(partJson["value"].get<std::string>())); | 24 | return field::joinWhere(object::frame, "frame_id", object::part, part::index, index); |
25 | } | ||
83 | 26 | ||
84 | break; | 27 | frame::frame(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) |
85 | } | 28 | { |
29 | id_ = sqlite3_column_int(row, 0); | ||
30 | groupId_ = sqlite3_column_int(row, 1); | ||
31 | length_ = sqlite3_column_int(row, 2); | ||
86 | 32 | ||
87 | case part::type::invalid: | 33 | parts_ = db.parts(*this, verbly::part::index, -1).all(); |
88 | { | ||
89 | throw std::domain_error("Invalid part data"); | ||
90 | } | ||
91 | } | ||
92 | } | ||
93 | } | 34 | } |
94 | 35 | ||
95 | }; | 36 | }; |
diff --git a/lib/frame.h b/lib/frame.h index 97473a0..36e179e 100644 --- a/lib/frame.h +++ b/lib/frame.h | |||
@@ -41,6 +41,16 @@ namespace verbly { | |||
41 | return id_; | 41 | return id_; |
42 | } | 42 | } |
43 | 43 | ||
44 | int getLength() const | ||
45 | { | ||
46 | if (!valid_) | ||
47 | { | ||
48 | throw std::domain_error("Bad access to uninitialized frame"); | ||
49 | } | ||
50 | |||
51 | return length_; | ||
52 | } | ||
53 | |||
44 | const std::vector<part>& getParts() const | 54 | const std::vector<part>& getParts() const |
45 | { | 55 | { |
46 | if (!valid_) | 56 | if (!valid_) |
@@ -61,6 +71,8 @@ namespace verbly { | |||
61 | 71 | ||
62 | static const field id; | 72 | static const field id; |
63 | 73 | ||
74 | static const field length; | ||
75 | |||
64 | operator filter() const | 76 | operator filter() const |
65 | { | 77 | { |
66 | if (!valid_) | 78 | if (!valid_) |
@@ -73,13 +85,18 @@ namespace verbly { | |||
73 | 85 | ||
74 | // Relationships to other objects | 86 | // Relationships to other objects |
75 | 87 | ||
76 | static const field group; | 88 | static const field word; |
89 | |||
90 | static field part(); | ||
91 | static field part(int index); | ||
77 | 92 | ||
78 | private: | 93 | private: |
79 | bool valid_ = false; | 94 | bool valid_ = false; |
80 | 95 | ||
81 | int id_; | 96 | int id_; |
82 | std::vector<part> parts_; | 97 | int groupId_; |
98 | int length_; | ||
99 | std::vector<class part> parts_; | ||
83 | 100 | ||
84 | const database* db_; | 101 | const database* db_; |
85 | 102 | ||
diff --git a/lib/group.cpp b/lib/group.cpp deleted file mode 100644 index d5790e9..0000000 --- a/lib/group.cpp +++ /dev/null | |||
@@ -1,61 +0,0 @@ | |||
1 | #include "group.h" | ||
2 | #include <sqlite3.h> | ||
3 | #include <json.hpp> | ||
4 | #include "database.h" | ||
5 | #include "query.h" | ||
6 | |||
7 | namespace verbly { | ||
8 | |||
9 | const object group::objectType = object::group; | ||
10 | |||
11 | const std::list<std::string> group::select = {"group_id", "data"}; | ||
12 | |||
13 | const field group::id = field::integerField(object::group, "group_id"); | ||
14 | |||
15 | const field group::frame = field::joinThrough(object::group, "group_id", object::frame, "groups_frames", "frame_id"); | ||
16 | const field group::word = field::joinField(object::group, "group_id", object::word); | ||
17 | |||
18 | group::group(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) | ||
19 | { | ||
20 | id_ = sqlite3_column_int(row, 0); | ||
21 | |||
22 | std::string rolesJsonStr(reinterpret_cast<const char*>(sqlite3_column_blob(row, 1))); | ||
23 | nlohmann::json rolesJson = nlohmann::json::parse(std::move(rolesJsonStr)); | ||
24 | for (const nlohmann::json& roleJson : rolesJson) | ||
25 | { | ||
26 | std::string roleName = roleJson["type"]; | ||
27 | selrestr roleSelrestr; | ||
28 | |||
29 | if (roleJson.find("selrestrs") != roleJson.end()) | ||
30 | { | ||
31 | roleSelrestr = selrestr(roleJson["selrestrs"]); | ||
32 | } | ||
33 | |||
34 | roles_[roleName] = role(roleName, std::move(roleSelrestr)); | ||
35 | } | ||
36 | } | ||
37 | |||
38 | const std::vector<frame>& group::getFrames() const | ||
39 | { | ||
40 | if (!valid_) | ||
41 | { | ||
42 | throw std::domain_error("Bad access to uninitialized group"); | ||
43 | } | ||
44 | |||
45 | if (!initializedFrames_) | ||
46 | { | ||
47 | frames_ = db_->frames(frame::group %= *this, false, -1).all(); | ||
48 | |||
49 | initializedFrames_ = true; | ||
50 | } | ||
51 | |||
52 | return frames_; | ||
53 | } | ||
54 | |||
55 | const role& group::getRole(std::string roleName) const | ||
56 | { | ||
57 | return roles_.at(roleName); | ||
58 | } | ||
59 | |||
60 | }; | ||
61 | |||
diff --git a/lib/group.h b/lib/group.h deleted file mode 100644 index fe62d39..0000000 --- a/lib/group.h +++ /dev/null | |||
@@ -1,91 +0,0 @@ | |||
1 | #ifndef GROUP_H_BD6933C0 | ||
2 | #define GROUP_H_BD6933C0 | ||
3 | |||
4 | #include <stdexcept> | ||
5 | #include <list> | ||
6 | #include <vector> | ||
7 | #include "field.h" | ||
8 | #include "filter.h" | ||
9 | #include "frame.h" | ||
10 | #include "role.h" | ||
11 | |||
12 | struct sqlite3_stmt; | ||
13 | |||
14 | namespace verbly { | ||
15 | |||
16 | class database; | ||
17 | |||
18 | class group { | ||
19 | public: | ||
20 | |||
21 | // Default constructor | ||
22 | |||
23 | group() = default; | ||
24 | |||
25 | // Construct from database | ||
26 | |||
27 | group(const database& db, sqlite3_stmt* row); | ||
28 | |||
29 | // Accessors | ||
30 | |||
31 | operator bool() const | ||
32 | { | ||
33 | return valid_; | ||
34 | } | ||
35 | |||
36 | int getId() const | ||
37 | { | ||
38 | if (!valid_) | ||
39 | { | ||
40 | throw std::domain_error("Bad access to uninitialized group"); | ||
41 | } | ||
42 | |||
43 | return id_; | ||
44 | } | ||
45 | |||
46 | const std::vector<frame>& getFrames() const; | ||
47 | |||
48 | const role& getRole(std::string roleName) const; | ||
49 | |||
50 | // Type info | ||
51 | |||
52 | static const object objectType; | ||
53 | |||
54 | static const std::list<std::string> select; | ||
55 | |||
56 | // Query fields | ||
57 | |||
58 | static const field id; | ||
59 | |||
60 | operator filter() const | ||
61 | { | ||
62 | if (!valid_) | ||
63 | { | ||
64 | throw std::domain_error("Bad access to uninitialized group"); | ||
65 | } | ||
66 | |||
67 | return (id == id_); | ||
68 | } | ||
69 | |||
70 | // Relationships to other objects | ||
71 | |||
72 | static const field frame; | ||
73 | |||
74 | static const field word; | ||
75 | |||
76 | private: | ||
77 | bool valid_ = false; | ||
78 | |||
79 | int id_; | ||
80 | std::map<std::string, role> roles_; | ||
81 | |||
82 | const database* db_; | ||
83 | |||
84 | mutable bool initializedFrames_ = false; | ||
85 | mutable std::vector<class frame> frames_; | ||
86 | |||
87 | }; | ||
88 | |||
89 | }; | ||
90 | |||
91 | #endif /* end of include guard: GROUP_H_BD6933C0 */ | ||
diff --git a/lib/lemma.cpp b/lib/lemma.cpp index 1601460..0c6e99e 100644 --- a/lib/lemma.cpp +++ b/lib/lemma.cpp | |||
@@ -10,20 +10,13 @@ namespace verbly { | |||
10 | const std::list<std::string> lemma::select = {"lemma_id"}; | 10 | const std::list<std::string> lemma::select = {"lemma_id"}; |
11 | 11 | ||
12 | const field lemma::id = field::integerField(object::lemma, "lemma_id"); | 12 | const field lemma::id = field::integerField(object::lemma, "lemma_id"); |
13 | |||
14 | const field lemma::word = field::joinField(object::lemma, "lemma_id", object::word); | ||
15 | |||
16 | const field lemma::formJoin = field::joinField(object::lemma, "form_id", object::form); | ||
17 | const field lemma::inflectionCategory = field::integerField(object::lemma, "category"); | 13 | const field lemma::inflectionCategory = field::integerField(object::lemma, "category"); |
18 | 14 | ||
19 | filter operator%=(lemma::inflection_field check, filter joinCondition) | 15 | const field lemma::word = field::joinField(object::lemma, "lemma_id", object::word); |
20 | { | ||
21 | return (lemma::formJoin %= joinCondition) && (lemma::inflectionCategory == check.getCategory()); | ||
22 | } | ||
23 | 16 | ||
24 | lemma::inflection_field::operator filter() const | 17 | field lemma::form(inflection category) |
25 | { | 18 | { |
26 | return (lemma::inflectionCategory == category_); | 19 | return field::joinWhere(object::lemma, "form_id", object::form, inflectionCategory, static_cast<int>(category)); |
27 | } | 20 | } |
28 | 21 | ||
29 | lemma::lemma(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) | 22 | lemma::lemma(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) |
@@ -68,7 +61,7 @@ namespace verbly { | |||
68 | 61 | ||
69 | void lemma::initializeForm(inflection infl) const | 62 | void lemma::initializeForm(inflection infl) const |
70 | { | 63 | { |
71 | forms_[infl] = db_->forms(form::lemma(infl) %= *this, false, -1).all(); | 64 | forms_[infl] = db_->forms(form::lemma %= ((inflectionCategory == infl) && *this), verbly::form::id, -1).all(); |
72 | } | 65 | } |
73 | 66 | ||
74 | }; | 67 | }; |
diff --git a/lib/lemma.h b/lib/lemma.h index 407fa3c..56cfc56 100644 --- a/lib/lemma.h +++ b/lib/lemma.h | |||
@@ -74,31 +74,7 @@ namespace verbly { | |||
74 | 74 | ||
75 | static const field word; | 75 | static const field word; |
76 | 76 | ||
77 | class inflection_field { | 77 | static field form(inflection category); |
78 | public: | ||
79 | |||
80 | inflection_field(inflection category) : category_(category) | ||
81 | { | ||
82 | } | ||
83 | |||
84 | const inflection getCategory() const | ||
85 | { | ||
86 | return category_; | ||
87 | } | ||
88 | |||
89 | operator filter() const; | ||
90 | |||
91 | private: | ||
92 | |||
93 | const inflection category_; | ||
94 | }; | ||
95 | |||
96 | static const inflection_field form(inflection category) | ||
97 | { | ||
98 | return inflection_field(category); | ||
99 | } | ||
100 | |||
101 | friend filter operator%=(lemma::inflection_field check, filter joinCondition); | ||
102 | 78 | ||
103 | private: | 79 | private: |
104 | 80 | ||
@@ -112,7 +88,6 @@ namespace verbly { | |||
112 | 88 | ||
113 | const database* db_; | 89 | const database* db_; |
114 | 90 | ||
115 | static const field formJoin; | ||
116 | static const field inflectionCategory; | 91 | static const field inflectionCategory; |
117 | 92 | ||
118 | }; | 93 | }; |
diff --git a/lib/order.h b/lib/order.h new file mode 100644 index 0000000..d2f0f92 --- /dev/null +++ b/lib/order.h | |||
@@ -0,0 +1,69 @@ | |||
1 | #ifndef ORDER_H_0EC669D5 | ||
2 | #define ORDER_H_0EC669D5 | ||
3 | |||
4 | #include <stdexcept> | ||
5 | #include "field.h" | ||
6 | |||
7 | namespace verbly { | ||
8 | |||
9 | class order { | ||
10 | public: | ||
11 | enum class type { | ||
12 | random, | ||
13 | field | ||
14 | }; | ||
15 | |||
16 | // Type | ||
17 | |||
18 | type getType() const | ||
19 | { | ||
20 | return type_; | ||
21 | } | ||
22 | |||
23 | // Random | ||
24 | |||
25 | order() : type_(type::random) | ||
26 | { | ||
27 | } | ||
28 | |||
29 | // Field | ||
30 | |||
31 | order( | ||
32 | field arg, | ||
33 | bool asc = true) : | ||
34 | type_(type::field), | ||
35 | sortField_(std::move(arg)), | ||
36 | ascending_(asc) | ||
37 | { | ||
38 | } | ||
39 | |||
40 | field getSortField() const | ||
41 | { | ||
42 | if (type_ != type::field) | ||
43 | { | ||
44 | throw std::domain_error("Invalid access to non-field order"); | ||
45 | } | ||
46 | |||
47 | return sortField_; | ||
48 | } | ||
49 | |||
50 | bool isAscending() const | ||
51 | { | ||
52 | if (type_ != type::field) | ||
53 | { | ||
54 | throw std::domain_error("Invalid access to non-field order"); | ||
55 | } | ||
56 | |||
57 | return ascending_; | ||
58 | } | ||
59 | |||
60 | private: | ||
61 | type type_; | ||
62 | field sortField_; | ||
63 | bool ascending_; | ||
64 | |||
65 | }; | ||
66 | |||
67 | }; | ||
68 | |||
69 | #endif /* end of include guard: ORDER_H_0EC669D5 */ | ||
diff --git a/lib/part.cpp b/lib/part.cpp index e66d151..1fbb24d 100644 --- a/lib/part.cpp +++ b/lib/part.cpp | |||
@@ -1,12 +1,30 @@ | |||
1 | #include "part.h" | 1 | #include "part.h" |
2 | #include <stdexcept> | 2 | #include <stdexcept> |
3 | #include <sqlite3.h> | ||
3 | #include "selrestr.h" | 4 | #include "selrestr.h" |
5 | #include "database.h" | ||
4 | 6 | ||
5 | namespace verbly { | 7 | namespace verbly { |
6 | 8 | ||
9 | const object part::objectType = object::part; | ||
10 | |||
11 | const std::list<std::string> part::select = {"part_id", "frame_id", "part_index", "type", "role", "selrestrs", "prepositions", "preposition_literality", "literal_value"}; | ||
12 | |||
13 | const field part::index = field::integerField(object::part, "part_index"); | ||
14 | const field part::type = field::integerField(object::part, "type"); | ||
15 | |||
16 | const field part::role = field::stringField(object::part, "role", true); | ||
17 | |||
18 | const field part::frame = field::joinField(object::part, "frame_id", object::frame); | ||
19 | |||
20 | const field part::synrestr_field::synrestrJoin = field::joinField(object::part, "part_id", "synrestrs"); | ||
21 | const field part::synrestr_field::synrestrField = field::stringField("synrestrs", "synrestr"); | ||
22 | |||
23 | const part::synrestr_field part::synrestr = {}; | ||
24 | |||
7 | part part::createNounPhrase(std::string role, selrestr selrestrs, std::set<std::string> synrestrs) | 25 | part part::createNounPhrase(std::string role, selrestr selrestrs, std::set<std::string> synrestrs) |
8 | { | 26 | { |
9 | part p(type::noun_phrase); | 27 | part p(part_type::noun_phrase); |
10 | 28 | ||
11 | new(&p.noun_phrase_.role) std::string(std::move(role)); | 29 | new(&p.noun_phrase_.role) std::string(std::move(role)); |
12 | new(&p.noun_phrase_.selrestrs) selrestr(std::move(selrestrs)); | 30 | new(&p.noun_phrase_.selrestrs) selrestr(std::move(selrestrs)); |
@@ -17,12 +35,12 @@ namespace verbly { | |||
17 | 35 | ||
18 | part part::createVerb() | 36 | part part::createVerb() |
19 | { | 37 | { |
20 | return part(type::verb); | 38 | return part(part_type::verb); |
21 | } | 39 | } |
22 | 40 | ||
23 | part part::createPreposition(std::vector<std::string> choices, bool literal) | 41 | part part::createPreposition(std::vector<std::string> choices, bool literal) |
24 | { | 42 | { |
25 | part p(type::preposition); | 43 | part p(part_type::preposition); |
26 | 44 | ||
27 | new(&p.preposition_.choices) std::vector<std::string>(std::move(choices)); | 45 | new(&p.preposition_.choices) std::vector<std::string>(std::move(choices)); |
28 | p.preposition_.literal = literal; | 46 | p.preposition_.literal = literal; |
@@ -32,30 +50,79 @@ namespace verbly { | |||
32 | 50 | ||
33 | part part::createAdjective() | 51 | part part::createAdjective() |
34 | { | 52 | { |
35 | return part(type::adjective); | 53 | return part(part_type::adjective); |
36 | } | 54 | } |
37 | 55 | ||
38 | part part::createAdverb() | 56 | part part::createAdverb() |
39 | { | 57 | { |
40 | return part(type::adverb); | 58 | return part(part_type::adverb); |
41 | } | 59 | } |
42 | 60 | ||
43 | part part::createLiteral(std::string value) | 61 | part part::createLiteral(std::string value) |
44 | { | 62 | { |
45 | part p(type::literal); | 63 | part p(part_type::literal); |
46 | 64 | ||
47 | new(&p.literal_) std::string(std::move(value)); | 65 | new(&p.literal_) std::string(std::move(value)); |
48 | 66 | ||
49 | return p; | 67 | return p; |
50 | } | 68 | } |
51 | 69 | ||
70 | part::part(const database& db, sqlite3_stmt* row) | ||
71 | { | ||
72 | int id = sqlite3_column_int(row, 0); | ||
73 | |||
74 | type_ = static_cast<part_type>(sqlite3_column_int(row, 3)); | ||
75 | |||
76 | switch (type_) | ||
77 | { | ||
78 | case part_type::noun_phrase: | ||
79 | { | ||
80 | new(&noun_phrase_.role) std::string(reinterpret_cast<const char*>(sqlite3_column_blob(row, 4))); | ||
81 | new(&noun_phrase_.selrestrs) selrestr(nlohmann::json::parse(reinterpret_cast<const char*>(sqlite3_column_blob(row, 5)))); | ||
82 | new(&noun_phrase_.synrestrs) std::set<std::string>(db.synrestrs(id)); | ||
83 | |||
84 | break; | ||
85 | } | ||
86 | |||
87 | case part_type::preposition: | ||
88 | { | ||
89 | new(&preposition_.choices) std::vector<std::string>(); | ||
90 | preposition_.literal = (sqlite3_column_int(row, 7) == 1); | ||
91 | |||
92 | std::string choicesJsonStr(reinterpret_cast<const char*>(sqlite3_column_blob(row, 6))); | ||
93 | nlohmann::json choicesJson = nlohmann::json::parse(std::move(choicesJsonStr)); | ||
94 | for (const nlohmann::json& choiceJson : choicesJson) | ||
95 | { | ||
96 | preposition_.choices.push_back(choiceJson.get<std::string>()); | ||
97 | } | ||
98 | |||
99 | break; | ||
100 | } | ||
101 | |||
102 | case part_type::literal: | ||
103 | { | ||
104 | new(&literal_) std::string(reinterpret_cast<const char*>(sqlite3_column_blob(row, 8))); | ||
105 | |||
106 | break; | ||
107 | } | ||
108 | |||
109 | case part_type::verb: | ||
110 | case part_type::adjective: | ||
111 | case part_type::adverb: | ||
112 | case part_type::invalid: | ||
113 | { | ||
114 | break; | ||
115 | } | ||
116 | } | ||
117 | } | ||
118 | |||
52 | part::part(const part& other) | 119 | part::part(const part& other) |
53 | { | 120 | { |
54 | type_ = other.type_; | 121 | type_ = other.type_; |
55 | 122 | ||
56 | switch (type_) | 123 | switch (type_) |
57 | { | 124 | { |
58 | case type::noun_phrase: | 125 | case part_type::noun_phrase: |
59 | { | 126 | { |
60 | new(&noun_phrase_.role) std::string(other.noun_phrase_.role); | 127 | new(&noun_phrase_.role) std::string(other.noun_phrase_.role); |
61 | new(&noun_phrase_.selrestrs) selrestr(other.noun_phrase_.selrestrs); | 128 | new(&noun_phrase_.selrestrs) selrestr(other.noun_phrase_.selrestrs); |
@@ -64,7 +131,7 @@ namespace verbly { | |||
64 | break; | 131 | break; |
65 | } | 132 | } |
66 | 133 | ||
67 | case type::preposition: | 134 | case part_type::preposition: |
68 | { | 135 | { |
69 | new(&preposition_.choices) std::vector<std::string>(other.preposition_.choices); | 136 | new(&preposition_.choices) std::vector<std::string>(other.preposition_.choices); |
70 | preposition_.literal = other.preposition_.literal; | 137 | preposition_.literal = other.preposition_.literal; |
@@ -72,17 +139,17 @@ namespace verbly { | |||
72 | break; | 139 | break; |
73 | } | 140 | } |
74 | 141 | ||
75 | case type::literal: | 142 | case part_type::literal: |
76 | { | 143 | { |
77 | new(&literal_) std::string(other.literal_); | 144 | new(&literal_) std::string(other.literal_); |
78 | 145 | ||
79 | break; | 146 | break; |
80 | } | 147 | } |
81 | 148 | ||
82 | case type::verb: | 149 | case part_type::verb: |
83 | case type::adjective: | 150 | case part_type::adjective: |
84 | case type::adverb: | 151 | case part_type::adverb: |
85 | case type::invalid: | 152 | case part_type::invalid: |
86 | { | 153 | { |
87 | break; | 154 | break; |
88 | } | 155 | } |
@@ -103,7 +170,7 @@ namespace verbly { | |||
103 | 170 | ||
104 | void swap(part& first, part& second) | 171 | void swap(part& first, part& second) |
105 | { | 172 | { |
106 | using type = part::type; | 173 | using type = part_type; |
107 | 174 | ||
108 | type tempType = first.type_; | 175 | type tempType = first.type_; |
109 | std::string tempRole; | 176 | std::string tempRole; |
@@ -231,7 +298,7 @@ namespace verbly { | |||
231 | { | 298 | { |
232 | switch (type_) | 299 | switch (type_) |
233 | { | 300 | { |
234 | case type::noun_phrase: | 301 | case part_type::noun_phrase: |
235 | { | 302 | { |
236 | using string_type = std::string; | 303 | using string_type = std::string; |
237 | using set_type = std::set<std::string>; | 304 | using set_type = std::set<std::string>; |
@@ -243,7 +310,7 @@ namespace verbly { | |||
243 | break; | 310 | break; |
244 | } | 311 | } |
245 | 312 | ||
246 | case type::preposition: | 313 | case part_type::preposition: |
247 | { | 314 | { |
248 | using vector_type = std::vector<std::string>; | 315 | using vector_type = std::vector<std::string>; |
249 | 316 | ||
@@ -252,7 +319,7 @@ namespace verbly { | |||
252 | break; | 319 | break; |
253 | } | 320 | } |
254 | 321 | ||
255 | case type::literal: | 322 | case part_type::literal: |
256 | { | 323 | { |
257 | using string_type = std::string; | 324 | using string_type = std::string; |
258 | 325 | ||
@@ -261,10 +328,10 @@ namespace verbly { | |||
261 | break; | 328 | break; |
262 | } | 329 | } |
263 | 330 | ||
264 | case type::verb: | 331 | case part_type::verb: |
265 | case type::adjective: | 332 | case part_type::adjective: |
266 | case type::adverb: | 333 | case part_type::adverb: |
267 | case type::invalid: | 334 | case part_type::invalid: |
268 | { | 335 | { |
269 | break; | 336 | break; |
270 | } | 337 | } |
@@ -273,7 +340,7 @@ namespace verbly { | |||
273 | 340 | ||
274 | std::string part::getNounRole() const | 341 | std::string part::getNounRole() const |
275 | { | 342 | { |
276 | if (type_ == type::noun_phrase) | 343 | if (type_ == part_type::noun_phrase) |
277 | { | 344 | { |
278 | return noun_phrase_.role; | 345 | return noun_phrase_.role; |
279 | } else { | 346 | } else { |
@@ -283,7 +350,7 @@ namespace verbly { | |||
283 | 350 | ||
284 | selrestr part::getNounSelrestrs() const | 351 | selrestr part::getNounSelrestrs() const |
285 | { | 352 | { |
286 | if (type_ == type::noun_phrase) | 353 | if (type_ == part_type::noun_phrase) |
287 | { | 354 | { |
288 | return noun_phrase_.selrestrs; | 355 | return noun_phrase_.selrestrs; |
289 | } else { | 356 | } else { |
@@ -293,7 +360,7 @@ namespace verbly { | |||
293 | 360 | ||
294 | std::set<std::string> part::getNounSynrestrs() const | 361 | std::set<std::string> part::getNounSynrestrs() const |
295 | { | 362 | { |
296 | if (type_ == type::noun_phrase) | 363 | if (type_ == part_type::noun_phrase) |
297 | { | 364 | { |
298 | return noun_phrase_.synrestrs; | 365 | return noun_phrase_.synrestrs; |
299 | } else { | 366 | } else { |
@@ -303,7 +370,7 @@ namespace verbly { | |||
303 | 370 | ||
304 | bool part::nounHasSynrestr(std::string synrestr) const | 371 | bool part::nounHasSynrestr(std::string synrestr) const |
305 | { | 372 | { |
306 | if (type_ != type::noun_phrase) | 373 | if (type_ != part_type::noun_phrase) |
307 | { | 374 | { |
308 | throw std::domain_error("part::nounHasSynrestr is only valid for noun phrase parts"); | 375 | throw std::domain_error("part::nounHasSynrestr is only valid for noun phrase parts"); |
309 | } | 376 | } |
@@ -313,7 +380,7 @@ namespace verbly { | |||
313 | 380 | ||
314 | std::vector<std::string> part::getPrepositionChoices() const | 381 | std::vector<std::string> part::getPrepositionChoices() const |
315 | { | 382 | { |
316 | if (type_ == type::preposition) | 383 | if (type_ == part_type::preposition) |
317 | { | 384 | { |
318 | return preposition_.choices; | 385 | return preposition_.choices; |
319 | } else { | 386 | } else { |
@@ -323,7 +390,7 @@ namespace verbly { | |||
323 | 390 | ||
324 | bool part::isPrepositionLiteral() const | 391 | bool part::isPrepositionLiteral() const |
325 | { | 392 | { |
326 | if (type_ == type::preposition) | 393 | if (type_ == part_type::preposition) |
327 | { | 394 | { |
328 | return preposition_.literal; | 395 | return preposition_.literal; |
329 | } else { | 396 | } else { |
@@ -333,7 +400,7 @@ namespace verbly { | |||
333 | 400 | ||
334 | std::string part::getLiteralValue() const | 401 | std::string part::getLiteralValue() const |
335 | { | 402 | { |
336 | if (type_ == type::literal) | 403 | if (type_ == part_type::literal) |
337 | { | 404 | { |
338 | return literal_; | 405 | return literal_; |
339 | } else { | 406 | } else { |
@@ -341,4 +408,9 @@ namespace verbly { | |||
341 | } | 408 | } |
342 | } | 409 | } |
343 | 410 | ||
411 | filter part::synrestr_field::operator%=(std::string synrestr) const | ||
412 | { | ||
413 | return (synrestrJoin %= (synrestrField == synrestr)); | ||
414 | } | ||
415 | |||
344 | }; | 416 | }; |
diff --git a/lib/part.h b/lib/part.h index 3a15638..9a01312 100644 --- a/lib/part.h +++ b/lib/part.h | |||
@@ -4,21 +4,20 @@ | |||
4 | #include <string> | 4 | #include <string> |
5 | #include <vector> | 5 | #include <vector> |
6 | #include <set> | 6 | #include <set> |
7 | #include <list> | ||
7 | #include "selrestr.h" | 8 | #include "selrestr.h" |
9 | #include "field.h" | ||
10 | #include "filter.h" | ||
11 | #include "enums.h" | ||
12 | |||
13 | struct sqlite3_stmt; | ||
8 | 14 | ||
9 | namespace verbly { | 15 | namespace verbly { |
10 | 16 | ||
17 | class database; | ||
18 | |||
11 | class part { | 19 | class part { |
12 | public: | 20 | public: |
13 | enum class type { | ||
14 | invalid = -1, | ||
15 | noun_phrase = 0, | ||
16 | verb = 1, | ||
17 | preposition = 2, | ||
18 | adjective = 3, | ||
19 | adverb = 4, | ||
20 | literal = 5 | ||
21 | }; | ||
22 | 21 | ||
23 | // Static factories | 22 | // Static factories |
24 | 23 | ||
@@ -40,6 +39,10 @@ namespace verbly { | |||
40 | { | 39 | { |
41 | } | 40 | } |
42 | 41 | ||
42 | // Construct from database | ||
43 | |||
44 | part(const database& db, sqlite3_stmt* row); | ||
45 | |||
43 | // Copy and move constructors | 46 | // Copy and move constructors |
44 | 47 | ||
45 | part(const part& other); | 48 | part(const part& other); |
@@ -60,7 +63,12 @@ namespace verbly { | |||
60 | 63 | ||
61 | // General accessors | 64 | // General accessors |
62 | 65 | ||
63 | type getType() const | 66 | operator bool() const |
67 | { | ||
68 | return (type_ != part_type::invalid); | ||
69 | } | ||
70 | |||
71 | part_type getType() const | ||
64 | { | 72 | { |
65 | return type_; | 73 | return type_; |
66 | } | 74 | } |
@@ -85,11 +93,43 @@ namespace verbly { | |||
85 | 93 | ||
86 | std::string getLiteralValue() const; | 94 | std::string getLiteralValue() const; |
87 | 95 | ||
96 | // Type info | ||
97 | |||
98 | static const object objectType; | ||
99 | |||
100 | static const std::list<std::string> select; | ||
101 | |||
102 | // Query fields | ||
103 | |||
104 | static const field index; | ||
105 | static const field type; | ||
106 | |||
107 | static const field role; | ||
108 | |||
109 | // Relationships to other objects | ||
110 | |||
111 | static const field frame; | ||
112 | |||
113 | // Noun synrestr relationship | ||
114 | |||
115 | class synrestr_field { | ||
116 | public: | ||
117 | |||
118 | filter operator%=(std::string synrestr) const; | ||
119 | |||
120 | private: | ||
121 | |||
122 | static const field synrestrJoin; | ||
123 | static const field synrestrField; | ||
124 | }; | ||
125 | |||
126 | static const synrestr_field synrestr; | ||
127 | |||
88 | private: | 128 | private: |
89 | 129 | ||
90 | // Private constructors | 130 | // Private constructors |
91 | 131 | ||
92 | part(type t) : type_(t) | 132 | part(part_type t) : type_(t) |
93 | { | 133 | { |
94 | } | 134 | } |
95 | 135 | ||
@@ -108,7 +148,7 @@ namespace verbly { | |||
108 | std::string literal_; | 148 | std::string literal_; |
109 | }; | 149 | }; |
110 | 150 | ||
111 | type type_ = type::invalid; | 151 | part_type type_ = part_type::invalid; |
112 | 152 | ||
113 | }; | 153 | }; |
114 | 154 | ||
diff --git a/lib/query.h b/lib/query.h index 214bf99..75651f6 100644 --- a/lib/query.h +++ b/lib/query.h | |||
@@ -9,6 +9,7 @@ | |||
9 | #include <iostream> | 9 | #include <iostream> |
10 | #include "statement.h" | 10 | #include "statement.h" |
11 | #include "binding.h" | 11 | #include "binding.h" |
12 | #include "order.h" | ||
12 | 13 | ||
13 | namespace verbly { | 14 | namespace verbly { |
14 | 15 | ||
@@ -24,11 +25,17 @@ namespace verbly { | |||
24 | class query { | 25 | class query { |
25 | public: | 26 | public: |
26 | 27 | ||
27 | query(const database& db, sqlite3* ppdb, filter queryFilter, bool random, int limit) : db_(&db) | 28 | query(const database& db, sqlite3* ppdb, filter queryFilter, order sortOrder, int limit) : db_(&db) |
28 | { | 29 | { |
30 | if ((sortOrder.getType() == order::type::field) | ||
31 | && (sortOrder.getSortField().getObject() != Object::objectType)) | ||
32 | { | ||
33 | throw std::invalid_argument("Can only sort query by a field in the result table"); | ||
34 | } | ||
35 | |||
29 | statement stmt(Object::objectType, std::move(queryFilter)); | 36 | statement stmt(Object::objectType, std::move(queryFilter)); |
30 | 37 | ||
31 | std::string queryString = stmt.getQueryString(Object::select, random, limit); | 38 | std::string queryString = stmt.getQueryString(Object::select, std::move(sortOrder), limit); |
32 | std::list<binding> bindings = stmt.getBindings(); | 39 | std::list<binding> bindings = stmt.getBindings(); |
33 | 40 | ||
34 | if (sqlite3_prepare_v2(ppdb, queryString.c_str(), queryString.length(), &ppstmt_, NULL) != SQLITE_OK) | 41 | if (sqlite3_prepare_v2(ppdb, queryString.c_str(), queryString.length(), &ppstmt_, NULL) != SQLITE_OK) |
diff --git a/lib/role.h b/lib/role.h deleted file mode 100644 index 4884ef3..0000000 --- a/lib/role.h +++ /dev/null | |||
@@ -1,60 +0,0 @@ | |||
1 | #ifndef ROLE_H_249F9A9C | ||
2 | #define ROLE_H_249F9A9C | ||
3 | |||
4 | #include <stdexcept> | ||
5 | #include <string> | ||
6 | #include "../lib/selrestr.h" | ||
7 | |||
8 | namespace verbly { | ||
9 | |||
10 | class role { | ||
11 | public: | ||
12 | |||
13 | // Default constructor | ||
14 | |||
15 | role() = default; | ||
16 | |||
17 | // Constructor | ||
18 | |||
19 | role( | ||
20 | std::string name, | ||
21 | selrestr selrestrs = {}) : | ||
22 | valid_(true), | ||
23 | name_(name), | ||
24 | selrestrs_(selrestrs) | ||
25 | { | ||
26 | } | ||
27 | |||
28 | // Accessors | ||
29 | |||
30 | const std::string& getName() const | ||
31 | { | ||
32 | if (!valid_) | ||
33 | { | ||
34 | throw std::domain_error("Bad access to invalid role"); | ||
35 | } | ||
36 | |||
37 | return name_; | ||
38 | } | ||
39 | |||
40 | const selrestr& getSelrestrs() const | ||
41 | { | ||
42 | if (!valid_) | ||
43 | { | ||
44 | throw std::domain_error("Bad access to invalid role"); | ||
45 | } | ||
46 | |||
47 | return selrestrs_; | ||
48 | } | ||
49 | |||
50 | private: | ||
51 | |||
52 | bool valid_ = false; | ||
53 | std::string name_; | ||
54 | selrestr selrestrs_; | ||
55 | |||
56 | }; | ||
57 | |||
58 | }; | ||
59 | |||
60 | #endif /* end of include guard: ROLE_H_249F9A9C */ | ||
diff --git a/lib/statement.cpp b/lib/statement.cpp index 846b9de..1512aa5 100644 --- a/lib/statement.cpp +++ b/lib/statement.cpp | |||
@@ -5,11 +5,12 @@ | |||
5 | #include "util.h" | 5 | #include "util.h" |
6 | #include "notion.h" | 6 | #include "notion.h" |
7 | #include "word.h" | 7 | #include "word.h" |
8 | #include "group.h" | ||
9 | #include "frame.h" | 8 | #include "frame.h" |
9 | #include "part.h" | ||
10 | #include "lemma.h" | 10 | #include "lemma.h" |
11 | #include "form.h" | 11 | #include "form.h" |
12 | #include "pronunciation.h" | 12 | #include "pronunciation.h" |
13 | #include "order.h" | ||
13 | 14 | ||
14 | namespace verbly { | 15 | namespace verbly { |
15 | 16 | ||
@@ -20,7 +21,7 @@ namespace verbly { | |||
20 | { | 21 | { |
21 | } | 22 | } |
22 | 23 | ||
23 | std::string statement::getQueryString(std::list<std::string> select, bool random, int limit) const | 24 | std::string statement::getQueryString(std::list<std::string> select, order sortOrder, int limit, bool debug) const |
24 | { | 25 | { |
25 | std::stringstream queryStream; | 26 | std::stringstream queryStream; |
26 | 27 | ||
@@ -49,7 +50,7 @@ namespace verbly { | |||
49 | if (cte.getCondition().getType() != condition::type::empty) | 50 | if (cte.getCondition().getType() != condition::type::empty) |
50 | { | 51 | { |
51 | cteStream << " WHERE "; | 52 | cteStream << " WHERE "; |
52 | cteStream << cte.getCondition().toSql(); | 53 | cteStream << cte.getCondition().flatten().toSql(true, debug); |
53 | } | 54 | } |
54 | 55 | ||
55 | if (cte.isRecursive()) | 56 | if (cte.isRecursive()) |
@@ -101,12 +102,28 @@ namespace verbly { | |||
101 | if (topCondition_.getType() != condition::type::empty) | 102 | if (topCondition_.getType() != condition::type::empty) |
102 | { | 103 | { |
103 | queryStream << " WHERE "; | 104 | queryStream << " WHERE "; |
104 | queryStream << topCondition_.toSql(); | 105 | queryStream << topCondition_.flatten().toSql(true, debug); |
105 | } | 106 | } |
106 | 107 | ||
107 | if (random) | 108 | queryStream << " ORDER BY "; |
109 | |||
110 | switch (sortOrder.getType()) | ||
108 | { | 111 | { |
109 | queryStream << " ORDER BY RANDOM()"; | 112 | case order::type::random: |
113 | { | ||
114 | queryStream << "RANDOM()"; | ||
115 | |||
116 | break; | ||
117 | } | ||
118 | |||
119 | case order::type::field: | ||
120 | { | ||
121 | queryStream << topTable_; | ||
122 | queryStream << "."; | ||
123 | queryStream << sortOrder.getSortField().getColumn(); | ||
124 | |||
125 | break; | ||
126 | } | ||
110 | } | 127 | } |
111 | 128 | ||
112 | if (limit > 0) | 129 | if (limit > 0) |
@@ -260,6 +277,7 @@ namespace verbly { | |||
260 | } | 277 | } |
261 | 278 | ||
262 | case field::type::join: | 279 | case field::type::join: |
280 | case field::type::join_where: | ||
263 | { | 281 | { |
264 | // First, figure out what table we need to join against. | 282 | // First, figure out what table we need to join against. |
265 | std::string joinTableName; | 283 | std::string joinTableName; |
@@ -269,13 +287,22 @@ namespace verbly { | |||
269 | } else { | 287 | } else { |
270 | joinTableName = getTableForContext(clause.getField().getJoinObject()); | 288 | joinTableName = getTableForContext(clause.getField().getJoinObject()); |
271 | } | 289 | } |
290 | |||
291 | filter joinCondition = clause.getJoinCondition(); | ||
292 | |||
293 | // If this is a condition join, we need to add the field join | ||
294 | // condition to the clause. | ||
295 | if (clause.getField().getType() == field::type::join_where) | ||
296 | { | ||
297 | joinCondition &= (clause.getField().getConditionField() == clause.getField().getConditionValue()); | ||
298 | } | ||
272 | 299 | ||
273 | // Recursively parse the subquery, and therefore obtain an | 300 | // Recursively parse the subquery, and therefore obtain an |
274 | // instantiated table to join against, as well as any joins or CTEs | 301 | // instantiated table to join against, as well as any joins or CTEs |
275 | // that the subquery may require to function. | 302 | // that the subquery may require to function. |
276 | statement joinStmt( | 303 | statement joinStmt( |
277 | joinTableName, | 304 | joinTableName, |
278 | clause.getJoinCondition().normalize(clause.getField().getJoinObject()), | 305 | std::move(joinCondition).normalize(clause.getField().getJoinObject()), |
279 | nextTableId_, | 306 | nextTableId_, |
280 | nextWithId_); | 307 | nextWithId_); |
281 | 308 | ||
@@ -801,7 +828,7 @@ namespace verbly { | |||
801 | new(&singleton_.value_) binding(std::move(value)); | 828 | new(&singleton_.value_) binding(std::move(value)); |
802 | } | 829 | } |
803 | 830 | ||
804 | std::string statement::condition::toSql() const | 831 | std::string statement::condition::toSql(bool toplevel, bool debug) const |
805 | { | 832 | { |
806 | switch (type_) | 833 | switch (type_) |
807 | { | 834 | { |
@@ -816,42 +843,92 @@ namespace verbly { | |||
816 | { | 843 | { |
817 | case comparison::equals: | 844 | case comparison::equals: |
818 | { | 845 | { |
819 | return singleton_.table_ + "." + singleton_.column_ + " = ?"; | 846 | if (debug) |
847 | { | ||
848 | if (singleton_.value_.getType() == binding::type::string) | ||
849 | { | ||
850 | return singleton_.table_ + "." + singleton_.column_ + " = \"" + singleton_.value_.getString() + "\""; | ||
851 | } else { | ||
852 | return singleton_.table_ + "." + singleton_.column_ + " = " + std::to_string(singleton_.value_.getInteger()); | ||
853 | } | ||
854 | } else { | ||
855 | return singleton_.table_ + "." + singleton_.column_ + " = ?"; | ||
856 | } | ||
820 | } | 857 | } |
821 | 858 | ||
822 | case comparison::does_not_equal: | 859 | case comparison::does_not_equal: |
823 | { | 860 | { |
824 | return singleton_.table_ + "." + singleton_.column_ + " != ?"; | 861 | if (debug) |
862 | { | ||
863 | if (singleton_.value_.getType() == binding::type::string) | ||
864 | { | ||
865 | return singleton_.table_ + "." + singleton_.column_ + " != \"" + singleton_.value_.getString() + "\""; | ||
866 | } else { | ||
867 | return singleton_.table_ + "." + singleton_.column_ + " != " + std::to_string(singleton_.value_.getInteger()); | ||
868 | } | ||
869 | } else { | ||
870 | return singleton_.table_ + "." + singleton_.column_ + " != ?"; | ||
871 | } | ||
825 | } | 872 | } |
826 | 873 | ||
827 | case comparison::is_greater_than: | 874 | case comparison::is_greater_than: |
828 | { | 875 | { |
829 | return singleton_.table_ + "." + singleton_.column_ + " > ?"; | 876 | if (debug) |
877 | { | ||
878 | return singleton_.table_ + "." + singleton_.column_ + " > " + std::to_string(singleton_.value_.getInteger()); | ||
879 | } else { | ||
880 | return singleton_.table_ + "." + singleton_.column_ + " > ?"; | ||
881 | } | ||
830 | } | 882 | } |
831 | 883 | ||
832 | case comparison::is_at_most: | 884 | case comparison::is_at_most: |
833 | { | 885 | { |
834 | return singleton_.table_ + "." + singleton_.column_ + " <= ?"; | 886 | if (debug) |
887 | { | ||
888 | return singleton_.table_ + "." + singleton_.column_ + " <= " + std::to_string(singleton_.value_.getInteger()); | ||
889 | } else { | ||
890 | return singleton_.table_ + "." + singleton_.column_ + " <= ?"; | ||
891 | } | ||
835 | } | 892 | } |
836 | 893 | ||
837 | case comparison::is_less_than: | 894 | case comparison::is_less_than: |
838 | { | 895 | { |
839 | return singleton_.table_ + "." + singleton_.column_ + " < ?"; | 896 | if (debug) |
897 | { | ||
898 | return singleton_.table_ + "." + singleton_.column_ + " < " + std::to_string(singleton_.value_.getInteger()); | ||
899 | } else { | ||
900 | return singleton_.table_ + "." + singleton_.column_ + " < ?"; | ||
901 | } | ||
840 | } | 902 | } |
841 | 903 | ||
842 | case comparison::is_at_least: | 904 | case comparison::is_at_least: |
843 | { | 905 | { |
844 | return singleton_.table_ + "." + singleton_.column_ + " >= ?"; | 906 | if (debug) |
907 | { | ||
908 | return singleton_.table_ + "." + singleton_.column_ + " >= " + std::to_string(singleton_.value_.getInteger()); | ||
909 | } else { | ||
910 | return singleton_.table_ + "." + singleton_.column_ + " >= ?"; | ||
911 | } | ||
845 | } | 912 | } |
846 | 913 | ||
847 | case comparison::is_like: | 914 | case comparison::is_like: |
848 | { | 915 | { |
849 | return singleton_.table_ + "." + singleton_.column_ + " LIKE ?"; | 916 | if (debug) |
917 | { | ||
918 | return singleton_.table_ + "." + singleton_.column_ + " LIKE \"" + singleton_.value_.getString() + "\""; | ||
919 | } else { | ||
920 | return singleton_.table_ + "." + singleton_.column_ + " LIKE ?"; | ||
921 | } | ||
850 | } | 922 | } |
851 | 923 | ||
852 | case comparison::is_not_like: | 924 | case comparison::is_not_like: |
853 | { | 925 | { |
854 | return singleton_.table_ + "." + singleton_.column_ + " NOT LIKE ?"; | 926 | if (debug) |
927 | { | ||
928 | return singleton_.table_ + "." + singleton_.column_ + " NOT LIKE \"" + singleton_.value_.getString() + "\""; | ||
929 | } else { | ||
930 | return singleton_.table_ + "." + singleton_.column_ + " NOT LIKE ?"; | ||
931 | } | ||
855 | } | 932 | } |
856 | 933 | ||
857 | case comparison::is_not_null: | 934 | case comparison::is_not_null: |
@@ -871,10 +948,25 @@ namespace verbly { | |||
871 | std::list<std::string> clauses; | 948 | std::list<std::string> clauses; |
872 | for (const condition& cond : group_.children_) | 949 | for (const condition& cond : group_.children_) |
873 | { | 950 | { |
874 | clauses.push_back(cond.toSql()); | 951 | clauses.push_back(cond.toSql(false, debug)); |
875 | } | 952 | } |
876 | 953 | ||
877 | return implode(std::begin(clauses), std::end(clauses), group_.orlogic_ ? " OR " : " AND "); | 954 | if (clauses.empty()) |
955 | { | ||
956 | return ""; | ||
957 | } else if (clauses.size() == 1) | ||
958 | { | ||
959 | return clauses.front(); | ||
960 | } else { | ||
961 | std::string result = implode(std::begin(clauses), std::end(clauses), group_.orlogic_ ? " OR " : " AND "); | ||
962 | |||
963 | if (toplevel) | ||
964 | { | ||
965 | return result; | ||
966 | } else { | ||
967 | return "(" + result + ")"; | ||
968 | } | ||
969 | } | ||
878 | } | 970 | } |
879 | } | 971 | } |
880 | } | 972 | } |
@@ -988,5 +1080,39 @@ namespace verbly { | |||
988 | throw std::domain_error("Cannot get children of non-group condition"); | 1080 | throw std::domain_error("Cannot get children of non-group condition"); |
989 | } | 1081 | } |
990 | } | 1082 | } |
1083 | |||
1084 | statement::condition statement::condition::flatten() const | ||
1085 | { | ||
1086 | switch (type_) | ||
1087 | { | ||
1088 | case type::empty: | ||
1089 | case type::singleton: | ||
1090 | { | ||
1091 | return *this; | ||
1092 | } | ||
1093 | |||
1094 | case type::group: | ||
1095 | { | ||
1096 | condition result(group_.orlogic_); | ||
1097 | |||
1098 | for (const condition& child : group_.children_) | ||
1099 | { | ||
1100 | condition newChild = child.flatten(); | ||
1101 | |||
1102 | if ((newChild.type_ == type::group) && (newChild.group_.orlogic_ == group_.orlogic_)) | ||
1103 | { | ||
1104 | for (condition subChild : std::move(newChild.group_.children_)) | ||
1105 | { | ||
1106 | result += std::move(subChild); | ||
1107 | } | ||
1108 | } else { | ||
1109 | result += std::move(newChild); | ||
1110 | } | ||
1111 | } | ||
1112 | |||
1113 | return result; | ||
1114 | } | ||
1115 | } | ||
1116 | } | ||
991 | 1117 | ||
992 | }; | 1118 | }; |
diff --git a/lib/statement.h b/lib/statement.h index aa56568..15c4ac3 100644 --- a/lib/statement.h +++ b/lib/statement.h | |||
@@ -13,13 +13,14 @@ | |||
13 | namespace verbly { | 13 | namespace verbly { |
14 | 14 | ||
15 | class filter; | 15 | class filter; |
16 | class order; | ||
16 | 17 | ||
17 | class statement { | 18 | class statement { |
18 | public: | 19 | public: |
19 | 20 | ||
20 | statement(object context, filter queryFilter); | 21 | statement(object context, filter queryFilter); |
21 | 22 | ||
22 | std::string getQueryString(std::list<std::string> select, bool random, int limit) const; | 23 | std::string getQueryString(std::list<std::string> select, order sortOrder, int limit, bool debug = false) const; |
23 | 24 | ||
24 | std::list<binding> getBindings() const; | 25 | std::list<binding> getBindings() const; |
25 | 26 | ||
@@ -153,10 +154,12 @@ namespace verbly { | |||
153 | 154 | ||
154 | // Utility | 155 | // Utility |
155 | 156 | ||
156 | std::string toSql() const; | 157 | std::string toSql(bool toplevel, bool debug = false) const; |
157 | 158 | ||
158 | std::list<binding> flattenBindings() const; | 159 | std::list<binding> flattenBindings() const; |
159 | 160 | ||
161 | condition flatten() const; | ||
162 | |||
160 | private: | 163 | private: |
161 | union { | 164 | union { |
162 | struct { | 165 | struct { |
@@ -246,8 +249,8 @@ namespace verbly { | |||
246 | { | 249 | { |
247 | return (context == object::notion) ? "notions" | 250 | return (context == object::notion) ? "notions" |
248 | : (context == object::word) ? "words" | 251 | : (context == object::word) ? "words" |
249 | : (context == object::group) ? "groups" | ||
250 | : (context == object::frame) ? "frames" | 252 | : (context == object::frame) ? "frames" |
253 | : (context == object::part) ? "parts" | ||
251 | : (context == object::lemma) ? "lemmas_forms" | 254 | : (context == object::lemma) ? "lemmas_forms" |
252 | : (context == object::form) ? "forms" | 255 | : (context == object::form) ? "forms" |
253 | : (context == object::pronunciation) ? "pronunciations" | 256 | : (context == object::pronunciation) ? "pronunciations" |
diff --git a/lib/verbly.h b/lib/verbly.h index d8875b3..112907b 100644 --- a/lib/verbly.h +++ b/lib/verbly.h | |||
@@ -6,16 +6,15 @@ | |||
6 | #include "filter.h" | 6 | #include "filter.h" |
7 | #include "field.h" | 7 | #include "field.h" |
8 | #include "query.h" | 8 | #include "query.h" |
9 | #include "order.h" | ||
9 | #include "notion.h" | 10 | #include "notion.h" |
10 | #include "word.h" | 11 | #include "word.h" |
11 | #include "group.h" | ||
12 | #include "frame.h" | 12 | #include "frame.h" |
13 | #include "part.h" | ||
13 | #include "lemma.h" | 14 | #include "lemma.h" |
14 | #include "form.h" | 15 | #include "form.h" |
15 | #include "pronunciation.h" | 16 | #include "pronunciation.h" |
16 | #include "token.h" | 17 | #include "token.h" |
17 | #include "selrestr.h" | 18 | #include "selrestr.h" |
18 | #include "part.h" | ||
19 | #include "role.h" | ||
20 | 19 | ||
21 | #endif /* end of include guard: VERBLY_H_5B39CE50 */ | 20 | #endif /* end of include guard: VERBLY_H_5B39CE50 */ |
diff --git a/lib/word.cpp b/lib/word.cpp index a928659..90eab1d 100644 --- a/lib/word.cpp +++ b/lib/word.cpp | |||
@@ -17,7 +17,7 @@ namespace verbly { | |||
17 | 17 | ||
18 | const field word::notion = field::joinField(object::word, "notion_id", object::notion); | 18 | const field word::notion = field::joinField(object::word, "notion_id", object::notion); |
19 | const field word::lemma = field::joinField(object::word, "lemma_id", object::lemma); | 19 | const field word::lemma = field::joinField(object::word, "lemma_id", object::lemma); |
20 | const field word::group = field::joinField(object::word, "group_id", object::group, true); | 20 | const field word::frame = field::joinField(object::word, "group_id", object::frame, true); |
21 | 21 | ||
22 | const field word::antonyms = field::selfJoin(object::word, "word_id", "antonymy", "antonym_2_id", "antonym_1_id"); | 22 | const field word::antonyms = field::selfJoin(object::word, "word_id", "antonymy", "antonym_2_id", "antonym_1_id"); |
23 | 23 | ||
@@ -93,7 +93,27 @@ namespace verbly { | |||
93 | return lemma_; | 93 | return lemma_; |
94 | } | 94 | } |
95 | 95 | ||
96 | const group& word::getGroup() const | 96 | bool word::hasFrames() const |
97 | { | ||
98 | if (!valid_) | ||
99 | { | ||
100 | throw std::domain_error("Bad access to uninitialized word"); | ||
101 | } | ||
102 | |||
103 | if (!hasGroup_) | ||
104 | { | ||
105 | return false; | ||
106 | } | ||
107 | |||
108 | if (!initializedFrames_) | ||
109 | { | ||
110 | initializeFrames(); | ||
111 | } | ||
112 | |||
113 | return !frames_.empty(); | ||
114 | } | ||
115 | |||
116 | const std::vector<frame>& word::getFrames() const | ||
97 | { | 117 | { |
98 | if (!valid_) | 118 | if (!valid_) |
99 | { | 119 | { |
@@ -105,12 +125,12 @@ namespace verbly { | |||
105 | throw std::domain_error("Word does not have a group"); | 125 | throw std::domain_error("Word does not have a group"); |
106 | } | 126 | } |
107 | 127 | ||
108 | if (!group_) | 128 | if (!initializedFrames_) |
109 | { | 129 | { |
110 | group_ = db_->groups(group::id == groupId_).first(); | 130 | initializeFrames(); |
111 | } | 131 | } |
112 | 132 | ||
113 | return group_; | 133 | return frames_; |
114 | } | 134 | } |
115 | 135 | ||
116 | std::string word::getBaseForm() const | 136 | std::string word::getBaseForm() const |
@@ -129,4 +149,10 @@ namespace verbly { | |||
129 | return result; | 149 | return result; |
130 | } | 150 | } |
131 | 151 | ||
152 | void word::initializeFrames() const | ||
153 | { | ||
154 | initializedFrames_ = true; | ||
155 | frames_ = db_->frames(*this, {}, -1).all(); | ||
156 | } | ||
157 | |||
132 | }; | 158 | }; |
diff --git a/lib/word.h b/lib/word.h index ddcabe4..8a333a4 100644 --- a/lib/word.h +++ b/lib/word.h | |||
@@ -7,7 +7,7 @@ | |||
7 | #include "filter.h" | 7 | #include "filter.h" |
8 | #include "notion.h" | 8 | #include "notion.h" |
9 | #include "lemma.h" | 9 | #include "lemma.h" |
10 | #include "group.h" | 10 | #include "frame.h" |
11 | 11 | ||
12 | struct sqlite3_stmt; | 12 | struct sqlite3_stmt; |
13 | 13 | ||
@@ -97,17 +97,9 @@ namespace verbly { | |||
97 | 97 | ||
98 | const lemma& getLemma() const; | 98 | const lemma& getLemma() const; |
99 | 99 | ||
100 | bool hasGroup() const | 100 | bool hasFrames() const; |
101 | { | ||
102 | if (!valid_) | ||
103 | { | ||
104 | throw std::domain_error("Bad access to uninitialized word"); | ||
105 | } | ||
106 | 101 | ||
107 | return hasGroup_; | 102 | const std::vector<frame>& getFrames() const; |
108 | } | ||
109 | |||
110 | const group& getGroup() const; | ||
111 | 103 | ||
112 | // Convenience accessors | 104 | // Convenience accessors |
113 | 105 | ||
@@ -136,7 +128,7 @@ namespace verbly { | |||
136 | 128 | ||
137 | static const field notion; | 129 | static const field notion; |
138 | static const field lemma; | 130 | static const field lemma; |
139 | static const field group; | 131 | static const field frame; |
140 | 132 | ||
141 | // Relationships with self | 133 | // Relationships with self |
142 | 134 | ||
@@ -161,6 +153,9 @@ namespace verbly { | |||
161 | static const field regionalDomains; | 153 | static const field regionalDomains; |
162 | 154 | ||
163 | private: | 155 | private: |
156 | |||
157 | void initializeFrames() const; | ||
158 | |||
164 | bool valid_ = false; | 159 | bool valid_ = false; |
165 | 160 | ||
166 | int id_; | 161 | int id_; |
@@ -176,7 +171,9 @@ namespace verbly { | |||
176 | 171 | ||
177 | mutable class notion notion_; | 172 | mutable class notion notion_; |
178 | mutable class lemma lemma_; | 173 | mutable class lemma lemma_; |
179 | mutable class group group_; | 174 | |
175 | mutable bool initializedFrames_ = false; | ||
176 | mutable std::vector<class frame> frames_; | ||
180 | 177 | ||
181 | }; | 178 | }; |
182 | 179 | ||