From f1f67e62cebb4144f0599196263cd93b41fa972e Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Mon, 6 Feb 2017 20:58:37 -0500 Subject: 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. --- CMakeLists.txt | 2 +- generator/schema.sql | 2 + lib/binding.cpp | 71 ++++++++++++++- lib/binding.h | 14 ++- lib/database.cpp | 5 -- lib/database.h | 3 - lib/enums.h | 5 +- lib/field.h | 61 +++++++------ lib/filter.cpp | 201 +++++++++++++++++++++++++++++++++++-------- lib/filter.h | 8 +- lib/form.cpp | 6 +- lib/form.h | 2 +- lib/frame.cpp | 2 +- lib/lemma.cpp | 67 --------------- lib/lemma.h | 97 --------------------- lib/pronunciation.cpp | 37 ++++---- lib/pronunciation.h | 23 +++-- lib/query.h | 5 ++ lib/statement.cpp | 232 +++++++++++++++++++++++++++++++++++++++----------- lib/statement.h | 9 +- lib/token.cpp | 2 +- lib/verbly.h | 1 - lib/word.cpp | 65 ++++++++------ lib/word.h | 19 +++-- 24 files changed, 574 insertions(+), 365 deletions(-) delete mode 100644 lib/lemma.cpp delete mode 100644 lib/lemma.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ea601e..5d4f476 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ pkg_check_modules(sqlite3 sqlite3>=3.8.3 REQUIRED) set(CMAKE_BUILD_TYPE Debug) -add_library(verbly lib/filter.cpp lib/field.cpp lib/notion.cpp lib/word.cpp lib/frame.cpp lib/part.cpp lib/lemma.cpp lib/form.cpp lib/pronunciation.cpp lib/statement.cpp lib/binding.cpp lib/database.cpp lib/token.cpp lib/part.cpp) +add_library(verbly lib/filter.cpp lib/field.cpp lib/notion.cpp lib/word.cpp lib/frame.cpp lib/part.cpp lib/form.cpp lib/pronunciation.cpp lib/statement.cpp lib/binding.cpp lib/database.cpp lib/token.cpp lib/part.cpp) set_property(TARGET verbly PROPERTY CXX_STANDARD 11) set_property(TARGET verbly PROPERTY CXX_STANDARD_REQUIRED ON) target_link_libraries(verbly ${sqlite3_LIBRARIES}) diff --git a/generator/schema.sql b/generator/schema.sql index 2ac658c..c07bf57 100644 --- a/generator/schema.sql +++ b/generator/schema.sql @@ -178,6 +178,8 @@ CREATE TABLE `pronunciations` ( `stress` VARCHAR(64) NOT NULL ); +CREATE INDEX `rhymes_with` ON `pronunciations`(`rhyme`); + CREATE TABLE `forms_pronunciations` ( `form_id` INTEGER NOT NULL, `pronunciation_id` INTEGER NOT NULL 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 { break; } + case type::field: + { + new(&field_.table_) std::string(other.field_.table_); + new(&field_.column_) std::string(other.field_.column_); + + break; + } + case type::invalid: { break; @@ -50,6 +58,8 @@ namespace verbly { type tempType = first.type_; int tempInteger; std::string tempString; + std::string tempTable; + std::string tempColumn; switch (first.type_) { @@ -67,6 +77,14 @@ namespace verbly { break; } + case type::field: + { + tempTable = std::move(first.field_.table_); + tempColumn = std::move(first.field_.column_); + + break; + } + case type::invalid: { break; @@ -93,6 +111,14 @@ namespace verbly { break; } + case type::field: + { + new(&first.field_.table_) std::string(std::move(second.field_.table_)); + new(&first.field_.column_) std::string(std::move(second.field_.column_)); + + break; + } + case type::invalid: { break; @@ -119,6 +145,14 @@ namespace verbly { break; } + case type::field: + { + new(&first.field_.table_) std::string(std::move(tempTable)); + new(&first.field_.column_) std::string(std::move(tempColumn)); + + break; + } + case type::invalid: { break; @@ -138,6 +172,15 @@ namespace verbly { break; } + case type::field: + { + using string_type = std::string; + field_.table_.~string_type(); + field_.column_.~string_type(); + + break; + } + case type::integer: case type::invalid: { @@ -164,7 +207,7 @@ namespace verbly { binding::binding(std::string arg) : type_(type::string) { - new(&string_) std::string(arg); + new(&string_) std::string(std::move(arg)); } std::string binding::getString() const @@ -177,4 +220,30 @@ namespace verbly { return string_; } + binding::binding(std::string table, std::string column) : type_(type::field) + { + new(&field_.table_) std::string(std::move(table)); + new(&field_.column_) std::string(std::move(column)); + } + + std::string binding::getTable() const + { + if (type_ != type::field) + { + throw std::domain_error("binding::getTable called on non-field binding"); + } + + return field_.table_; + } + + std::string binding::getColumn() const + { + if (type_ != type::field) + { + throw std::domain_error("binding::getColumn called on non-field binding"); + } + + return field_.column_; + } + }; 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 { enum class type { invalid, integer, - string + string, + field }; // Default constructor @@ -55,11 +56,22 @@ namespace verbly { std::string getString() const; + // Field + + binding(std::string table, std::string column); + + std::string getTable() const; + std::string getColumn() const; + private: union { int integer_; std::string string_; + struct { + std::string table_; + std::string column_; + } field_; }; 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 { return query(*this, ppdb_, std::move(where), std::move(sortOrder), limit); } - query database::lemmas(filter where, order sortOrder, int limit) const - { - return query(*this, ppdb_, std::move(where), std::move(sortOrder), limit); - } - query
database::forms(filter where, order sortOrder, int limit) const { return query(*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 @@ #include "word.h" #include "frame.h" #include "part.h" -#include "lemma.h" #include "form.h" #include "pronunciation.h" #include "order.h" @@ -56,8 +55,6 @@ namespace verbly { query parts(filter where, order sortOrder = {}, int limit = 1) const; - query lemmas(filter where, order sortOrder = {}, int limit = 1) const; - query forms(filter where, order sortOrder = {}, int limit = 1) const; query 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 { word = 1, frame = 2, part = 3, - lemma = 4, - form = 5, - pronunciation = 6 + form = 4, + pronunciation = 5 }; 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 { join, join_where, join_through, + join_through_where, hierarchal_join }; @@ -100,11 +101,11 @@ namespace verbly { object obj, const char* name, object joinWith, - const field& conditionField, + const char* conditionColumn, int conditionValue, bool nullable = false) { - return field(obj, type::join_where, name, nullable, 0, joinWith, 0, 0, 0, &conditionField, conditionValue); + return field(obj, type::join_where, name, nullable, 0, joinWith, 0, 0, 0, conditionColumn, conditionValue); } static field joinThrough( @@ -129,6 +130,19 @@ namespace verbly { return field(obj, type::join_through, name, true, joinTable, joinWith, foreignColumn, joinColumn, foreignJoinColumn); } + static field joinThroughWhere( + object obj, + const char* name, + object joinWith, + const char* joinTable, + const char* foreignColumn, + const char* conditionColumn, + int conditionValue, + bool nullable = false) + { + return field(obj, type::join_through_where, name, nullable, joinTable, joinWith, foreignColumn, name, foreignColumn, conditionColumn, conditionValue); + } + static field selfJoin( object obj, const char* name, @@ -166,6 +180,7 @@ namespace verbly { return ((type_ == type::join) || (type_ == type::join_where) || (type_ == type::join_through) + || (type_ == type::join_through_where) || (type_ == type::hierarchal_join)); } @@ -195,7 +210,7 @@ namespace verbly { { return (type_ == type::hierarchal_join) ? object_ - : ((type_ == type::join) || (type_ == type::join_where) || (type_ == type::join_through)) + : ((type_ == type::join) || (type_ == type::join_where) || (type_ == type::join_through) || (type_ == type::join_through_where)) ? joinObject_ : throw std::domain_error("Non-join fields don't have join objects"); } @@ -205,40 +220,40 @@ namespace verbly { const char* getForeignColumn() const { // We ignore hierarchal joins because they are always self joins. - return (type_ == type::join_through) + return ((type_ == type::join_through) || (type_ == type::join_through_where)) ? foreignColumn_ : throw std::domain_error("Only many-to-many join fields have a foreign column"); } const char* getJoinColumn() const { - return ((type_ == type::join_through) || (type_ == type::hierarchal_join)) + return ((type_ == type::join_through) || (type_ == type::join_through_where) || (type_ == type::hierarchal_join)) ? joinColumn_ : throw std::domain_error("Only many-to-many join fields have a join column"); } const char* getForeignJoinColumn() const { - return ((type_ == type::join_through) || (type_ == type::hierarchal_join)) + return ((type_ == type::join_through) || (type_ == type::join_through_where) || (type_ == type::hierarchal_join)) ? foreignJoinColumn_ : throw std::domain_error("Only many-to-many join fields have a foreign join column"); } // Condition joins - const field& getConditionField() const + const char* getConditionColumn() const { - if (type_ == type::join_where) + if ((type_ == type::join_where) || (type_ == type::join_through_where)) { - return *conditionField_; + return conditionColumn_; } else { - throw std::domain_error("Only condition join fields have a condition field"); + throw std::domain_error("Only condition join fields have a condition column"); } } int getConditionValue() const { - return (type_ == type::join_where) + return ((type_ == type::join_where) || (type_ == type::join_through_where)) ? conditionValue_ : throw std::domain_error("Only condition join fields have a condition value"); } @@ -254,13 +269,8 @@ namespace verbly { // table (hypernymy); however, they have different join columns. For // condition joins, the condition field and condition value are also // significant. - if (conditionField_) - { - return std::tie(object_, column_, table_, joinColumn_, *conditionField_, conditionValue_) - < std::tie(other.object_, other.column_, other.table_, other.joinColumn_, *other.conditionField_, other.conditionValue_); - } else { - return std::tie(object_, column_, table_, joinColumn_) < std::tie(other.object_, other.column_, other.table_, other.joinColumn_); - } + return std::tie(object_, column_, table_, joinColumn_, conditionColumn_, conditionValue_) + < std::tie(other.object_, other.column_, other.table_, other.joinColumn_, other.conditionColumn_, other.conditionValue_); } // Equality @@ -268,13 +278,8 @@ namespace verbly { bool operator==(const field& other) const { // See operator<() for documentation. - if (conditionField_) - { - return std::tie(object_, column_, table_, joinColumn_, *conditionField_, conditionValue_) - == std::tie(other.object_, other.column_, other.table_, other.joinColumn_, *other.conditionField_, other.conditionValue_); - } else { - return std::tie(object_, column_, table_, joinColumn_) == std::tie(other.object_, other.column_, other.table_, other.joinColumn_); - } + return std::tie(object_, column_, table_, joinColumn_, conditionColumn_, conditionValue_) + == std::tie(other.object_, other.column_, other.table_, other.joinColumn_, other.conditionColumn_, other.conditionValue_); } // Filter construction @@ -325,7 +330,7 @@ namespace verbly { const char* foreignColumn = 0, const char* joinColumn = 0, const char* foreignJoinColumn = 0, - const field* conditionField = 0, + const char* conditionColumn = 0, int conditionValue = 0) : object_(obj), type_(datatype), @@ -336,7 +341,7 @@ namespace verbly { foreignColumn_(foreignColumn), joinColumn_(joinColumn), foreignJoinColumn_(foreignJoinColumn), - conditionField_(conditionField), + conditionColumn_(conditionColumn), conditionValue_(conditionValue) { } @@ -359,7 +364,7 @@ namespace verbly { const char* foreignJoinColumn_ = 0; // Condition joins - const field* conditionField_ = 0; + const char* conditionColumn_ = 0; int conditionValue_ = 0; }; 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 @@ #include "word.h" #include "frame.h" #include "part.h" -#include "lemma.h" #include "form.h" #include "pronunciation.h" @@ -73,6 +72,14 @@ namespace verbly { break; } + + case comparison::field_equals: + case comparison::field_does_not_equal: + { + new(&singleton_.compareField) field(other.singleton_.compareField); + + break; + } } break; @@ -112,6 +119,7 @@ namespace verbly { std::string tempStringValue; int tempIntValue; bool tempBoolValue; + field tempCompareField; std::list tempChildren; bool tempOrlogic; @@ -173,6 +181,14 @@ namespace verbly { break; } + + case comparison::field_equals: + case comparison::field_does_not_equal: + { + tempCompareField = std::move(first.singleton_.compareField); + + break; + } } break; @@ -249,6 +265,14 @@ namespace verbly { break; } + + case comparison::field_equals: + case comparison::field_does_not_equal: + { + new(&first.singleton_.compareField) field(std::move(second.singleton_.compareField)); + + break; + } } break; @@ -325,6 +349,14 @@ namespace verbly { break; } + + case comparison::field_equals: + case comparison::field_does_not_equal: + { + new(&second.singleton_.compareField) field(std::move(tempCompareField)); + + break; + } } break; @@ -391,6 +423,14 @@ namespace verbly { break; } + + case comparison::field_equals: + case comparison::field_does_not_equal: + { + singleton_.compareField.~field(); + + break; + } } break; @@ -446,6 +486,8 @@ namespace verbly { case comparison::does_not_match: case comparison::hierarchally_matches: case comparison::does_not_hierarchally_match: + case comparison::field_equals: + case comparison::field_does_not_equal: { throw std::invalid_argument("Invalid comparison for integer field"); } @@ -490,6 +532,8 @@ namespace verbly { case comparison::does_not_match: case comparison::hierarchally_matches: case comparison::does_not_hierarchally_match: + case comparison::field_equals: + case comparison::field_does_not_equal: { throw std::invalid_argument("Invalid comparison for string field"); } @@ -534,6 +578,8 @@ namespace verbly { case comparison::does_not_match: case comparison::hierarchally_matches: case comparison::does_not_hierarchally_match: + case comparison::field_equals: + case comparison::field_does_not_equal: { throw std::invalid_argument("Invalid comparison for boolean field"); } @@ -576,6 +622,8 @@ namespace verbly { case comparison::does_not_match: case comparison::hierarchally_matches: case comparison::does_not_hierarchally_match: + case comparison::field_equals: + case comparison::field_does_not_equal: { throw std::invalid_argument("Incorrect constructor for given comparison"); } @@ -596,6 +644,7 @@ namespace verbly { case field::type::join: case field::type::join_where: case field::type::join_through: + case field::type::join_through_where: { switch (filterType) { @@ -624,6 +673,8 @@ namespace verbly { case comparison::is_not_null: case comparison::hierarchally_matches: case comparison::does_not_hierarchally_match: + case comparison::field_equals: + case comparison::field_does_not_equal: { throw std::invalid_argument("Incorrect constructor for given comparison"); } @@ -661,6 +712,8 @@ namespace verbly { case comparison::is_not_null: case comparison::matches: case comparison::does_not_match: + case comparison::field_equals: + case comparison::field_does_not_equal: { throw std::invalid_argument("Incorrect constructor for given comparison"); } @@ -679,6 +732,57 @@ namespace verbly { } } + filter::filter( + field filterField, + comparison filterType, + field compareField) : + type_(type::singleton) + { + switch (filterType) + { + case comparison::field_equals: + case comparison::field_does_not_equal: + { + if (filterField.getType() != compareField.getType()) + { + throw std::invalid_argument("Cannot compare two fields of different types"); + } + + if (filterField.isJoin()) + { + throw std::domain_error("Cannot compare join fields"); + } + + new(&singleton_.filterField) field(std::move(filterField)); + singleton_.filterType = filterType; + new(&singleton_.compareField) field(std::move(compareField)); + + break; + } + + case comparison::int_equals: + case comparison::int_does_not_equal: + case comparison::int_is_at_least: + case comparison::int_is_greater_than: + case comparison::int_is_at_most: + case comparison::int_is_less_than: + case comparison::boolean_equals: + case comparison::string_equals: + case comparison::string_does_not_equal: + case comparison::string_is_like: + case comparison::string_is_not_like: + case comparison::is_null: + case comparison::is_not_null: + case comparison::matches: + case comparison::does_not_match: + case comparison::hierarchally_matches: + case comparison::does_not_hierarchally_match: + { + throw std::domain_error("Incorrect constructor for given comparison"); + } + } + } + field filter::getField() const { if (type_ == type::singleton) @@ -726,6 +830,8 @@ namespace verbly { case comparison::boolean_equals: case comparison::is_null: case comparison::is_not_null: + case comparison::field_equals: + case comparison::field_does_not_equal: { throw std::domain_error("This filter does not have a join condition"); } @@ -762,6 +868,8 @@ namespace verbly { case comparison::does_not_match: case comparison::hierarchally_matches: case comparison::does_not_hierarchally_match: + case comparison::field_equals: + case comparison::field_does_not_equal: { throw std::domain_error("This filter does not have a string argument"); } @@ -798,6 +906,8 @@ namespace verbly { case comparison::does_not_match: case comparison::hierarchally_matches: case comparison::does_not_hierarchally_match: + case comparison::field_equals: + case comparison::field_does_not_equal: { throw std::domain_error("This filter does not have an integer argument"); } @@ -817,6 +927,47 @@ namespace verbly { } } + field filter::getCompareField() const + { + if (type_ != type::singleton) + { + throw std::domain_error("This filter does not have a compare field"); + } + + switch (singleton_.filterType) + { + case comparison::field_equals: + case comparison::field_does_not_equal: + { + return singleton_.compareField; + + break; + } + + case comparison::int_equals: + case comparison::int_does_not_equal: + case comparison::int_is_at_least: + case comparison::int_is_greater_than: + case comparison::int_is_at_most: + case comparison::int_is_less_than: + case comparison::boolean_equals: + case comparison::string_equals: + case comparison::string_does_not_equal: + case comparison::string_is_like: + case comparison::string_is_not_like: + case comparison::is_null: + case comparison::is_not_null: + case comparison::matches: + case comparison::does_not_match: + case comparison::hierarchally_matches: + case comparison::does_not_hierarchally_match: + { + throw std::domain_error("This filter doesn't have a compare field"); + } + } + + } + filter::filter(bool orlogic) : type_(type::group) { new(&group_.children) std::list(); @@ -970,6 +1121,16 @@ namespace verbly { { return filter(singleton_.filterField, comparison::hierarchally_matches, *singleton_.join); } + + case comparison::field_equals: + { + return filter(singleton_.filterField, comparison::field_does_not_equal, singleton_.compareField); + } + + case comparison::field_does_not_equal: + { + return filter(singleton_.filterField, comparison::field_equals, singleton_.compareField); + } } } @@ -1111,7 +1272,6 @@ namespace verbly { case object::word: case object::frame: case object::part: - case object::lemma: case object::form: case object::pronunciation: { @@ -1141,11 +1301,10 @@ namespace verbly { return (verbly::word::frames %= *this); } - case object::lemma: case object::form: case object::pronunciation: { - return (verbly::word::lemmas %= *this); + return (verbly::word::forms(inflection::base) %= *this); } } @@ -1161,7 +1320,6 @@ namespace verbly { case object::notion: case object::word: - case object::lemma: case object::form: case object::pronunciation: { @@ -1188,7 +1346,6 @@ namespace verbly { case object::notion: case object::word: case object::frame: - case object::lemma: case object::form: case object::pronunciation: { @@ -1197,32 +1354,6 @@ namespace verbly { } } - case object::lemma: - { - switch (singleton_.filterField.getObject()) - { - case object::notion: - case object::word: - case object::frame: - case object::part: - { - return verbly::lemma::words %= *this; - } - - case object::undefined: - case object::lemma: - { - return *this; - } - - case object::form: - case object::pronunciation: - { - return (verbly::lemma::forms(inflection::base) %= *this); - } - } - } - case object::form: { switch (singleton_.filterField.getObject()) @@ -1231,9 +1362,8 @@ namespace verbly { case object::word: case object::frame: case object::part: - case object::lemma: { - return verbly::form::lemmas %= *this; + return verbly::form::words(inflection::base) %= *this; } case object::undefined: @@ -1257,7 +1387,6 @@ namespace verbly { case object::word: case object::frame: case object::part: - case object::lemma: case object::form: { return verbly::pronunciation::forms %= *this; @@ -1354,6 +1483,8 @@ namespace verbly { case comparison::string_is_not_like: case comparison::is_null: case comparison::is_not_null: + case comparison::field_equals: + case comparison::field_does_not_equal: { result += std::move(normalized); 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 { matches, does_not_match, hierarchally_matches, - does_not_hierarchally_match + does_not_hierarchally_match, + field_equals, + field_does_not_equal }; // Copy and move constructors @@ -72,6 +74,7 @@ namespace verbly { filter(field filterField, comparison filterType, bool filterValue); filter(field filterField, comparison filterType); filter(field joinOn, comparison filterType, filter joinCondition); + filter(field filterField, comparison filterType, field compareField); field getField() const; @@ -85,6 +88,8 @@ namespace verbly { bool getBooleanArgument() const; + field getCompareField() const; + // Group explicit filter(bool orlogic); @@ -129,6 +134,7 @@ namespace verbly { std::string stringValue; int intValue; bool boolValue; + field compareField; }; } singleton_; 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 { const field form::complexity = field::integerField(object::form, "complexity"); const field form::proper = field::booleanField(object::form, "proper"); - const field form::lemmas = field::joinField(object::form, "form_id", object::lemma); const field form::pronunciations = field::joinThrough(object::form, "form_id", object::pronunciation, "forms_pronunciations", "pronunciation_id"); + field form::words(inflection category) + { + return field::joinThroughWhere(object::form, "form_id", object::word, "lemmas_forms", "lemma_id", "category", static_cast(category)); + } + form::form(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) { 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 { // Relationships to other objects - static const field lemmas; + static field words(inflection category); static const field pronunciations; 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 { field frame::parts(int index) { - return field::joinWhere(object::frame, "frame_id", object::part, part::index, index); + return field::joinWhere(object::frame, "frame_id", object::part, "part_index", index); } 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 @@ -#include "lemma.h" -#include -#include "database.h" -#include "query.h" - -namespace verbly { - - const object lemma::objectType = object::lemma; - - const std::list lemma::select = {"lemma_id"}; - - const field lemma::id = field::integerField(object::lemma, "lemma_id"); - const field lemma::inflectionCategory = field::integerField(object::lemma, "category"); - - const field lemma::words = field::joinField(object::lemma, "lemma_id", object::word); - - field lemma::forms(inflection category) - { - return field::joinWhere(object::lemma, "form_id", object::form, inflectionCategory, static_cast(category)); - } - - lemma::lemma(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) - { - id_ = sqlite3_column_int(row, 0); - } - - const form& lemma::getBaseForm() const - { - if (!valid_) - { - throw std::domain_error("Bad access to uninitialized lemma"); - } - - if (!forms_.count(inflection::base)) - { - initializeForm(inflection::base); - } - - return forms_.at(inflection::base).front(); - } - - bool lemma::hasInflection(inflection category) const - { - return !getInflections(category).empty(); - } - - const std::vector& lemma::getInflections(inflection category) const - { - if (!valid_) - { - throw std::domain_error("Bad access to uninitialized lemma"); - } - - if (!forms_.count(category)) - { - initializeForm(category); - } - - return forms_.at(category); - } - - void lemma::initializeForm(inflection infl) const - { - forms_[infl] = db_->forms(form::lemmas %= ((inflectionCategory == infl) && *this), verbly::form::id, -1).all(); - } - -}; 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 @@ -#ifndef LEMMA_H_0A180D30 -#define LEMMA_H_0A180D30 - -#include -#include -#include -#include -#include "field.h" -#include "form.h" -#include "enums.h" -#include "filter.h" - -struct sqlite3_stmt; - -namespace verbly { - - class database; - - class lemma { - public: - - // Default constructor - - lemma() = default; - - // Construct from database - - lemma(const database& db, sqlite3_stmt* row); - - // Accessors - - operator bool() const - { - return valid_; - } - - int getId() const - { - if (!valid_) - { - throw std::domain_error("Bad access to uninitialized lemma"); - } - - return id_; - } - - const form& getBaseForm() const; - - bool hasInflection(inflection category) const; - - const std::vector& getInflections(inflection category) const; - - // Type info - - static const object objectType; - - static const std::list select; - - // Query fields - - static const field id; - - operator filter() const - { - if (!valid_) - { - throw std::domain_error("Bad access to uninitialized lemma"); - } - - return (id == id_); - } - - // Relationships to other objects - - static const field words; - - static field forms(inflection category); - - private: - - void initializeForm(inflection category) const; - - bool valid_ = false; - - int id_; - - mutable std::map> forms_; - - const database* db_; - - static const field inflectionCategory; - - }; - -}; - -#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 @@ #include "pronunciation.h" #include #include "form.h" -#include "lemma.h" #include "word.h" #include "util.h" @@ -20,6 +19,9 @@ namespace verbly { const field pronunciation::prerhyme = field::stringField(object::pronunciation, "prerhyme", true); const field pronunciation::rhyme = field::stringField(object::pronunciation, "rhyme", true); + const field pronunciation::rhymes_field::rhymeJoin = field::joinField(object::pronunciation, "rhyme", object::pronunciation); + const pronunciation::rhymes_field pronunciation::rhymes = {}; + pronunciation::pronunciation(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) { id_ = sqlite3_column_int(row, 0); @@ -39,31 +41,22 @@ namespace verbly { } } - filter pronunciation::rhymesWith(const pronunciation& arg) + filter pronunciation::rhymes_field::operator%=(filter joinCondition) const { - return (prerhyme != arg.getPrerhyme()) && (rhyme == arg.getRhyme()); - } - - /*filter pronunciation::rhymesWith(const class form& arg) - { - filter result; - - for (const pronunciation& p : arg.getPronunciations()) - { - result |= rhymesWith(p); - } - - return result; + return (rhymeJoin %= ( + joinCondition + && filter( + verbly::pronunciation::prerhyme, + filter::comparison::field_does_not_equal, + verbly::pronunciation::prerhyme))); } - filter pronunciation::rhymesWith(const lemma& arg) + pronunciation::rhymes_field::operator filter() const { - return rhymesWith(arg.getBaseForm()); + return (rhymeJoin %= filter( + verbly::pronunciation::prerhyme, + filter::comparison::field_does_not_equal, + verbly::pronunciation::prerhyme)); } - filter pronunciation::rhymesWith(const word& arg) - { - return rhymesWith(arg.getLemma()); - }*/ - }; 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; namespace verbly { class form; - class lemma; class word; class database; @@ -131,14 +130,26 @@ namespace verbly { return (id == id_); } - static filter rhymesWith(const pronunciation& arg); - static filter rhymesWith(const class form& arg); - static filter rhymesWith(const lemma& arg); - static filter rhymesWith(const word& arg); - // Relationships to other objects static const field forms; + + // Rhyming relationship + + class rhymes_field { + public: + + filter operator%=(filter joinCondition) const; + + operator filter() const; + + private: + + static const field rhymeJoin; + + }; + + static const rhymes_field rhymes; private: 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 { { throw std::logic_error("Cannot use invalid bindings"); } + + case binding::type::field: + { + throw std::logic_error("Compare field binding made it past statement generation"); + } } 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 @@ #include #include "filter.h" #include "util.h" -#include "notion.h" -#include "word.h" -#include "frame.h" -#include "part.h" -#include "lemma.h" -#include "form.h" -#include "pronunciation.h" #include "order.h" namespace verbly { @@ -17,7 +10,7 @@ namespace verbly { statement::statement( object context, filter queryFilter) : - statement(getTableForContext(context), queryFilter.compact().normalize(context)) + statement(context, getTableForContext(context), queryFilter.compact().normalize(context)) { } @@ -104,24 +97,29 @@ namespace verbly { queryStream << " WHERE "; queryStream << topCondition_.flatten().toSql(true, debug); } - + + queryStream << " GROUP BY "; + queryStream << topTable_; + queryStream << "."; + queryStream << select.front(); + queryStream << " ORDER BY "; - + switch (sortOrder.getType()) { case order::type::random: { queryStream << "RANDOM()"; - + break; } - + case order::type::field: { queryStream << topTable_; queryStream << "."; queryStream << sortOrder.getSortField().getColumn(); - + break; } } @@ -156,10 +154,12 @@ namespace verbly { } statement::statement( + object context, std::string tableName, filter clause, int nextTableId, int nextWithId) : + context_(context), nextTableId_(nextTableId), nextWithId_(nextWithId), topTable_(instantiateTable(std::move(tableName))), @@ -266,6 +266,16 @@ namespace verbly { return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_not_like, clause.getStringArgument()); } + case filter::comparison::field_equals: + { + return condition(topTable_, clause.getField().getColumn(), condition::comparison::equals, {"", clause.getCompareField().getColumn()}, clause.getCompareField().getObject()); + } + + case filter::comparison::field_does_not_equal: + { + return condition(topTable_, clause.getField().getColumn(), condition::comparison::does_not_equal, {"", clause.getCompareField().getColumn()}, clause.getCompareField().getObject()); + } + case filter::comparison::matches: case filter::comparison::does_not_match: case filter::comparison::hierarchally_matches: @@ -281,26 +291,29 @@ namespace verbly { { // First, figure out what table we need to join against. std::string joinTableName; + object joinContext = object::undefined; if (clause.getField().hasTable()) { joinTableName = clause.getField().getTable(); } else { - joinTableName = getTableForContext(clause.getField().getJoinObject()); + joinContext = clause.getField().getJoinObject(); + joinTableName = getTableForContext(joinContext); } - + filter joinCondition = clause.getJoinCondition(); - + // If this is a condition join, we need to add the field join // condition to the clause. if (clause.getField().getType() == field::type::join_where) { - joinCondition &= (clause.getField().getConditionField() == clause.getField().getConditionValue()); + joinCondition &= (field::integerField(joinTableName.c_str(), clause.getField().getConditionColumn()) == clause.getField().getConditionValue()); } // Recursively parse the subquery, and therefore obtain an // instantiated table to join against, as well as any joins or CTEs // that the subquery may require to function. statement joinStmt( + joinContext, joinTableName, std::move(joinCondition).normalize(clause.getField().getJoinObject()), nextTableId_, @@ -368,11 +381,13 @@ namespace verbly { } case field::type::join_through: + case field::type::join_through_where: { // Recursively parse the subquery, and therefore obtain an // instantiated table to join against, as well as any joins or CTEs // that the subquery may require to function. statement joinStmt( + clause.getField().getJoinObject(), getTableForContext(clause.getField().getJoinObject()), clause.getJoinCondition().normalize(clause.getField().getJoinObject()), nextTableId_, @@ -424,6 +439,17 @@ namespace verbly { std::list cteJoins = std::move(joinStmt.joins_); condition cteCondition = integrate(std::move(joinStmt), true); + // If this is a condition join, add the condition. + if (clause.getField().getType() == field::type::join_through_where) + { + cteCondition &= + condition( + throughTable, + clause.getField().getConditionColumn(), + condition::comparison::equals, + clause.getField().getConditionValue()); + } + withs_.emplace_back( std::move(withName), clause.getField(), @@ -453,7 +479,7 @@ namespace verbly { joins_.emplace_back( false, getTableForContext(clause.getField().getJoinObject()), - std::move(throughTable), + throughTable, clause.getField().getForeignJoinColumn(), std::move(joinTable), clause.getField().getForeignColumn()); @@ -461,7 +487,20 @@ namespace verbly { // Integrate the subquery's table mappings, joins, and CTEs into // this statement, and return the subquery condition as our // condition. - return integrate(std::move(joinStmt)); + condition resultCond = integrate(std::move(joinStmt)); + + // If this is a condition join, add the condition. + if (clause.getField().getType() == field::type::join_through_where) + { + resultCond &= + condition( + throughTable, + clause.getField().getConditionColumn(), + condition::comparison::equals, + clause.getField().getConditionValue()); + } + + return std::move(resultCond); } } @@ -491,6 +530,7 @@ namespace verbly { // Recursively parse the subquery in order to create the CTE. statement withStmt( + clause.getField().getObject(), getTableForContext(clause.getField().getObject()), clause.getJoinCondition().normalize(clause.getField().getObject()), nextTableId_, @@ -593,7 +633,7 @@ namespace verbly { nextTableId_ = subStmt.nextTableId_; nextWithId_ = subStmt.nextWithId_; - return subStmt.topCondition_; + return subStmt.topCondition_.resolveCompareFields(context_, topTable_); } std::ostream& operator<<(std::ostream& oss, const statement::join& j) @@ -637,6 +677,7 @@ namespace verbly { new(&singleton_.column_) std::string(other.singleton_.column_); singleton_.comparison_ = other.singleton_.comparison_; new(&singleton_.value_) binding(other.singleton_.value_); + singleton_.parentObject_ = other.singleton_.parentObject_; break; } @@ -673,6 +714,7 @@ namespace verbly { std::string tempColumn; condition::comparison tempComparison; binding tempBinding; + object tempParentObject; std::list tempChildren; bool tempOrlogic; @@ -689,6 +731,7 @@ namespace verbly { tempColumn = std::move(first.singleton_.column_); tempComparison = first.singleton_.comparison_; tempBinding = std::move(first.singleton_.value_); + tempParentObject = first.singleton_.parentObject_; break; } @@ -719,6 +762,7 @@ namespace verbly { new(&first.singleton_.column_) std::string(std::move(second.singleton_.column_)); first.singleton_.comparison_ = second.singleton_.comparison_; new(&first.singleton_.value_) binding(std::move(second.singleton_.value_)); + first.singleton_.parentObject_ = second.singleton_.parentObject_; break; } @@ -749,6 +793,7 @@ namespace verbly { new(&second.singleton_.column_) std::string(std::move(tempColumn)); second.singleton_.comparison_ = tempComparison; new(&second.singleton_.value_) binding(std::move(tempBinding)); + second.singleton_.parentObject_ = tempParentObject; break; } @@ -813,19 +858,23 @@ namespace verbly { } else { singleton_.comparison_ = comparison::is_not_null; } + + singleton_.parentObject_ = object::undefined; } statement::condition::condition( std::string table, std::string column, comparison comp, - binding value) : + binding value, + object parentObject) : type_(type::singleton) { new(&singleton_.table_) std::string(std::move(table)); new(&singleton_.column_) std::string(std::move(column)); singleton_.comparison_ = comp; new(&singleton_.value_) binding(std::move(value)); + singleton_.parentObject_ = parentObject; } std::string statement::condition::toSql(bool toplevel, bool debug) const @@ -845,14 +894,35 @@ namespace verbly { { if (debug) { - if (singleton_.value_.getType() == binding::type::string) + switch (singleton_.value_.getType()) { - return singleton_.table_ + "." + singleton_.column_ + " = \"" + singleton_.value_.getString() + "\""; - } else { - return singleton_.table_ + "." + singleton_.column_ + " = " + std::to_string(singleton_.value_.getInteger()); + case binding::type::string: + { + return singleton_.table_ + "." + singleton_.column_ + " = \"" + singleton_.value_.getString() + "\""; + } + + case binding::type::integer: + { + return singleton_.table_ + "." + singleton_.column_ + " = " + std::to_string(singleton_.value_.getInteger()); + } + + case binding::type::field: + { + return singleton_.table_ + "." + singleton_.column_ + " = " + singleton_.value_.getTable() + "." + singleton_.value_.getColumn(); + } + + case binding::type::invalid: + { + throw std::logic_error("Invalid binding in statement generation"); + } } } else { - return singleton_.table_ + "." + singleton_.column_ + " = ?"; + if (singleton_.value_.getType() == binding::type::field) + { + return singleton_.table_ + "." + singleton_.column_ + " = " + singleton_.value_.getTable() + "." + singleton_.value_.getColumn(); + } else { + return singleton_.table_ + "." + singleton_.column_ + " = ?"; + } } } @@ -860,14 +930,35 @@ namespace verbly { { if (debug) { - if (singleton_.value_.getType() == binding::type::string) + switch (singleton_.value_.getType()) { - return singleton_.table_ + "." + singleton_.column_ + " != \"" + singleton_.value_.getString() + "\""; - } else { - return singleton_.table_ + "." + singleton_.column_ + " != " + std::to_string(singleton_.value_.getInteger()); + case binding::type::string: + { + return singleton_.table_ + "." + singleton_.column_ + " != \"" + singleton_.value_.getString() + "\""; + } + + case binding::type::integer: + { + return singleton_.table_ + "." + singleton_.column_ + " != " + std::to_string(singleton_.value_.getInteger()); + } + + case binding::type::field: + { + return singleton_.table_ + "." + singleton_.column_ + " != " + singleton_.value_.getTable() + "." + singleton_.value_.getColumn(); + } + + case binding::type::invalid: + { + throw std::logic_error("Invalid binding in statement generation"); + } } } else { - return singleton_.table_ + "." + singleton_.column_ + " != ?"; + if (singleton_.value_.getType() == binding::type::field) + { + return singleton_.table_ + "." + singleton_.column_ + " != " + singleton_.value_.getTable() + "." + singleton_.value_.getColumn(); + } else { + return singleton_.table_ + "." + singleton_.column_ + " != ?"; + } } } @@ -959,7 +1050,7 @@ namespace verbly { return clauses.front(); } else { std::string result = implode(std::begin(clauses), std::end(clauses), group_.orlogic_ ? " OR " : " AND "); - + if (toplevel) { return result; @@ -982,24 +1073,29 @@ namespace verbly { case type::singleton: { - switch (singleton_.comparison_) + if (singleton_.value_.getType() == binding::type::field) { - case comparison::equals: - case comparison::does_not_equal: - case comparison::is_greater_than: - case comparison::is_at_most: - case comparison::is_less_than: - case comparison::is_at_least: - case comparison::is_like: - case comparison::is_not_like: + return {}; + } else { + switch (singleton_.comparison_) { - return {singleton_.value_}; - } + case comparison::equals: + case comparison::does_not_equal: + case comparison::is_greater_than: + case comparison::is_at_most: + case comparison::is_less_than: + case comparison::is_at_least: + case comparison::is_like: + case comparison::is_not_like: + { + return {singleton_.value_}; + } - case comparison::is_not_null: - case comparison::is_null: - { - return {}; + case comparison::is_not_null: + case comparison::is_null: + { + return {}; + } } } } @@ -1080,7 +1176,7 @@ namespace verbly { throw std::domain_error("Cannot get children of non-group condition"); } } - + statement::condition statement::condition::flatten() const { switch (type_) @@ -1090,15 +1186,15 @@ namespace verbly { { return *this; } - + case type::group: { condition result(group_.orlogic_); - + for (const condition& child : group_.children_) { condition newChild = child.flatten(); - + if ((newChild.type_ == type::group) && (newChild.group_.orlogic_ == group_.orlogic_)) { for (condition subChild : std::move(newChild.group_.children_)) @@ -1109,7 +1205,39 @@ namespace verbly { result += std::move(newChild); } } - + + return result; + } + } + } + + statement::condition statement::condition::resolveCompareFields(object context, std::string tableName) const + { + switch (type_) + { + case type::empty: + { + return *this; + } + + case type::singleton: + { + if ((singleton_.parentObject_ != object::undefined) && (singleton_.parentObject_ == context)) + { + return condition(singleton_.table_, singleton_.column_, singleton_.comparison_, {tableName, singleton_.value_.getColumn()}); + } else { + return *this; + } + } + + case type::group: + { + condition result(group_.orlogic_); + for (const condition& cond : group_.children_) + { + result += cond.resolveCompareFields(context, tableName); + } + return result; } } 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 { condition(std::string table, std::string column, bool isNull); - condition(std::string table, std::string column, comparison comp, binding value); + condition(std::string table, std::string column, comparison comp, binding value, object parentObject = object::undefined); // Group @@ -160,6 +160,8 @@ namespace verbly { condition flatten() const; + condition resolveCompareFields(object context, std::string tableName) const; + private: union { struct { @@ -167,6 +169,7 @@ namespace verbly { std::string column_; comparison comparison_; binding value_; + object parentObject_; } singleton_; struct { std::list children_; @@ -251,7 +254,6 @@ namespace verbly { : (context == object::word) ? "words" : (context == object::frame) ? "frames" : (context == object::part) ? "parts" - : (context == object::lemma) ? "lemmas_forms" : (context == object::form) ? "forms" : (context == object::pronunciation) ? "pronunciations" : throw std::domain_error("Provided context has no associated table"); @@ -259,7 +261,7 @@ namespace verbly { static const std::list getSelectForContext(object context); - statement(std::string tableName, filter clause, int nextTableId = 0, int nextWithId = 0); + statement(object context, std::string tableName, filter clause, int nextTableId = 0, int nextWithId = 0); condition parseFilter(filter queryFilter); @@ -272,6 +274,7 @@ namespace verbly { int nextTableId_; int nextWithId_; + object context_; std::map tables_; std::string topTable_; std::list 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 { { switch (type_) { - case type::word: return word_.word_.getInflections(word_.category_).front(); + case type::word: return word_.word_.getInflections(word_.category_).front().getText(); case type::literal: return literal_; case type::part: throw std::domain_error("Cannot compile incomplete token"); 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 @@ #include "word.h" #include "frame.h" #include "part.h" -#include "lemma.h" #include "form.h" #include "pronunciation.h" #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 { const field word::adjectivePosition = field::integerField(object::word, "position", true); const field word::notions = field::joinField(object::word, "notion_id", object::notion); - const field word::lemmas = field::joinField(object::word, "lemma_id", object::lemma); const field word::frames = field::joinField(object::word, "group_id", object::frame, true); const field word::antonyms = field::selfJoin(object::word, "word_id", "antonymy", "antonym_2_id", "antonym_1_id"); @@ -39,6 +38,11 @@ namespace verbly { const field word::regionalTerms = field::selfJoin(object::word, "word_id", "regionality", "domain_id", "term_id"); const field word::regionalDomains = field::selfJoin(object::word, "word_id", "regionality", "term_id", "domain_id"); + field word::forms(inflection category) + { + return field::joinThroughWhere(object::word, "lemma_id", object::form, "lemmas_forms", "form_id", "category", static_cast(category)); + } + word::word(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) { id_ = sqlite3_column_int(row, 0); @@ -78,21 +82,6 @@ namespace verbly { return notion_; } - const lemma& word::getLemma() const - { - if (!valid_) - { - throw std::domain_error("Bad access to uninitialized word"); - } - - if (!lemma_) - { - lemma_ = db_->lemmas(lemma::id == lemmaId_).first(); - } - - return lemma_; - } - bool word::hasFrames() const { if (!valid_) @@ -133,26 +122,50 @@ namespace verbly { return frames_; } - std::string word::getBaseForm() const + void word::initializeFrames() const { - return getLemma().getBaseForm().getText(); + initializedFrames_ = true; + frames_ = db_->frames(*this, {}, -1).all(); } - std::vector word::getInflections(inflection category) const + const form& word::getBaseForm() const { - std::vector result; - for (const form& infl : getLemma().getInflections(category)) + if (!valid_) { - result.push_back(infl.getText()); + throw std::domain_error("Bad access to uninitialized word"); } - return result; + if (!forms_.count(inflection::base)) + { + initializeForm(inflection::base); + } + + return forms_.at(inflection::base).front(); } - void word::initializeFrames() const + bool word::hasInflection(inflection category) const { - initializedFrames_ = true; - frames_ = db_->frames(*this, {}, -1).all(); + return !getInflections(category).empty(); + } + + const std::vector& word::getInflections(inflection category) const + { + if (!valid_) + { + throw std::domain_error("Bad access to uninitialized word"); + } + + if (!forms_.count(category)) + { + initializeForm(category); + } + + return forms_.at(category); + } + + void word::initializeForm(inflection infl) const + { + forms_[infl] = db_->forms(form::words(infl) %= *this, verbly::form::id, -1).all(); } }; 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 @@ #include "field.h" #include "filter.h" #include "notion.h" -#include "lemma.h" #include "frame.h" +#include "form.h" struct sqlite3_stmt; @@ -95,17 +95,17 @@ namespace verbly { const notion& getNotion() const; - const lemma& getLemma() const; - bool hasFrames() const; const std::vector& getFrames() const; - // Convenience accessors + const form& getBaseForm() const; + + bool hasInflection(inflection category) const; + + const std::vector& getInflections(inflection category) const; - std::string getBaseForm() const; - std::vector getInflections(inflection infl) const; // Type info @@ -127,9 +127,10 @@ namespace verbly { // Relationships with other objects static const field notions; - static const field lemmas; static const field frames; + static field forms(inflection category); + // Relationships with self static const field antonyms; @@ -154,6 +155,7 @@ namespace verbly { private: + void initializeForm(inflection category) const; void initializeFrames() const; bool valid_ = false; @@ -170,10 +172,9 @@ namespace verbly { const database* db_; mutable notion notion_; - mutable lemma lemma_; - mutable bool initializedFrames_ = false; mutable std::vector frames_; + mutable std::map> forms_; }; -- cgit 1.4.1