susumu.yata
null+****@clear*****
Wed Aug 27 16:49:17 JST 2014
susumu.yata 2014-08-27 16:49:17 +0900 (Wed, 27 Aug 2014) New Revision: f888788885487ec40b228b7a2fabb396b779f11b https://github.com/groonga/grnxx/commit/f888788885487ec40b228b7a2fabb396b779f11b Message: Support expressions for BoolVector and add tests. Modified files: include/grnxx/datum.hpp include/grnxx/expression.hpp include/grnxx/types.hpp lib/grnxx/column.cpp lib/grnxx/expression.cpp test/test_column.cpp test/test_expression.cpp Modified: include/grnxx/datum.hpp (+6 -6) =================================================================== --- include/grnxx/datum.hpp 2014-08-27 14:57:10 +0900 (10e2d2f) +++ include/grnxx/datum.hpp 2014-08-27 16:49:17 +0900 (c3d703d) @@ -26,8 +26,8 @@ class Datum { : type_(TEXT_DATA), text_(value) {} Datum(Vector<Bool> value) - : type_(VECTOR_BOOL_DATA), - vector_bool_(value) {} + : type_(BOOL_VECTOR_DATA), + bool_vector_(value) {} // Return the data type. DataType type() const { @@ -50,8 +50,8 @@ class Datum { Text force_text() const { return text_; } - Vector<Bool> force_vector_bool() const { - return vector_bool_; + Vector<Bool> force_bool_vector() const { + return bool_vector_; } // Force the specified interpretation. @@ -71,7 +71,7 @@ class Datum { *value = text_; } void force(Vector<Bool> *value) const { - *value = vector_bool_; + *value = bool_vector_; } private: @@ -82,7 +82,7 @@ class Datum { Float float_; GeoPoint geo_point_; String text_; - Vector<Bool> vector_bool_; + Vector<Bool> bool_vector_; }; }; Modified: include/grnxx/expression.hpp (+6 -1) =================================================================== --- include/grnxx/expression.hpp 2014-08-27 14:57:10 +0900 (b4f8802) +++ include/grnxx/expression.hpp 2014-08-27 16:49:17 +0900 (3e5f9c2) @@ -57,7 +57,7 @@ enum OperatorType { MODULUS_OPERATOR, // For Int. // Array operators. -// SUBSCRIPT_OPERATOR, + SUBSCRIPT_OPERATOR, // -- Ternary operators -- }; @@ -314,6 +314,11 @@ class ExpressionBuilder { OperatorType operator_type, unique_ptr<Node> &&arg1, unique_ptr<Node> &&arg2); + // Create a subscript node. + unique_ptr<Node> create_subscript_node( + Error *error, + unique_ptr<Node> &&arg1, + unique_ptr<Node> &&arg2); }; } // namespace grnxx Modified: include/grnxx/types.hpp (+9 -7) =================================================================== --- include/grnxx/types.hpp 2014-08-27 14:57:10 +0900 (3866034) +++ include/grnxx/types.hpp 2014-08-27 16:49:17 +0900 (c9669cf) @@ -83,12 +83,12 @@ enum DataType { // Type: Vector. // Value: Vector of above data types. // Default: {}. - VECTOR_BOOL_DATA, - VECTOR_INT_DATA, - VECTOR_FLOAT_DATA, - VECTOR_GEO_POINT_DATA, - VECTOR_TEXT_DATA, - VECTOR_ROW_REF_DATA + BOOL_VECTOR_DATA, + INT_VECTOR_DATA, + FLOAT_VECTOR_DATA, + GEO_POINT_VECTOR_DATA, + TEXT_VECTOR_DATA, + ROW_REF_VECTOR_DATA }; using Bool = bool; @@ -289,6 +289,8 @@ inline Bool operator!=(Vector<Bool> lhs, Vector<Bool> rhs) { ((lhs.bits() ^ rhs.bits()) != 0); } +using BoolVector = Vector<Bool>; + // Type information. template <typename T> struct TypeTraits; template <> struct TypeTraits <Bool> { @@ -333,7 +335,7 @@ template <> struct TypeTraits <Text> { }; template <> struct TypeTraits <Vector<Bool>> { static DataType data_type() { - return VECTOR_BOOL_DATA; + return BOOL_VECTOR_DATA; } static Vector<Bool> default_value() { return Vector<Bool>(0, 0); Modified: lib/grnxx/column.cpp (+1 -1) =================================================================== --- lib/grnxx/column.cpp 2014-08-27 14:57:10 +0900 (fd3c573) +++ lib/grnxx/column.cpp 2014-08-27 16:49:17 +0900 (d8cab38) @@ -88,7 +88,7 @@ unique_ptr<Column> Column::create(Error *error, case TEXT_DATA: { return ColumnImpl<Text>::create(error, table, name, options); } - case VECTOR_BOOL_DATA: { + case BOOL_VECTOR_DATA: { return ColumnImpl<Vector<Bool>>::create(error, table, name, options); } default: { Modified: lib/grnxx/expression.cpp (+137 -0) =================================================================== --- lib/grnxx/expression.cpp 2014-08-27 14:57:10 +0900 (ec83b15) +++ lib/grnxx/expression.cpp 2014-08-27 16:49:17 +0900 (44db766) @@ -2182,6 +2182,108 @@ bool ModulusNode<Int>::evaluate(Error *error, return true; } +// ---- SubscriptOperator ---- + +template <typename T> +class SubscriptNode : public BinaryNode<T, Vector<T>, Int> { + public: + using Value = T; + using Arg1 = Vector<T>; + using Arg2 = Int; + + static unique_ptr<Node> create(Error *error, + unique_ptr<Node> &&arg1, + unique_ptr<Node> &&arg2) { + unique_ptr<Node> node( + new (nothrow) SubscriptNode(std::move(arg1), std::move(arg2))); + if (!node) { + GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed"); + } + return node; + } + + SubscriptNode(unique_ptr<Node> &&arg1, unique_ptr<Node> &&arg2) + : BinaryNode<Value, Arg1, Arg2>(std::move(arg1), std::move(arg2)) {} + + bool evaluate(Error *error, + ArrayCRef<Record> records, + ArrayRef<Value> results); +}; + +template <typename T> +bool SubscriptNode<T>::evaluate(Error *error, + ArrayCRef<Record> records, + ArrayRef<Value> results) { + if (!this->fill_arg1_values(error, records) || + !this->fill_arg2_values(error, records)) { + return false; + } + for (Int i = 0; i < records.size(); ++i) { + results.set(i, this->arg1_values_[i][this->arg2_values_[i]]); + } + return true; +} + +template <> +class SubscriptNode<Bool> : public BinaryNode<Bool, Vector<Bool>, Int> { + public: + using Value = Bool; + using Arg1 = Vector<Bool>; + using Arg2 = Int; + + static unique_ptr<Node> create(Error *error, + unique_ptr<Node> &&arg1, + unique_ptr<Node> &&arg2) { + unique_ptr<Node> node( + new (nothrow) SubscriptNode(std::move(arg1), std::move(arg2))); + if (!node) { + GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed"); + } + return node; + } + + SubscriptNode(unique_ptr<Node> &&arg1, unique_ptr<Node> &&arg2) + : BinaryNode<Value, Arg1, Arg2>(std::move(arg1), std::move(arg2)) {} + + bool filter(Error *error, + ArrayCRef<Record> input_records, + ArrayRef<Record> *output_records); + bool evaluate(Error *error, + ArrayCRef<Record> records, + ArrayRef<Value> results); +}; + +bool SubscriptNode<Bool>::filter(Error *error, + ArrayCRef<Record> input_records, + ArrayRef<Record> *output_records) { + if (!this->fill_arg1_values(error, input_records) || + !this->fill_arg2_values(error, input_records)) { + return false; + } + Int count = 0; + for (Int i = 0; i < input_records.size(); ++i) { + if (this->arg1_values_[i][this->arg2_values_[i]]) { + output_records->set(count, input_records.get(i)); + ++count; + } + } + *output_records = output_records->ref(0, count); + return true; +} + +bool SubscriptNode<Bool>::evaluate(Error *error, + ArrayCRef<Record> records, + ArrayRef<Value> results) { + if (!this->fill_arg1_values(error, records) || + !this->fill_arg2_values(error, records)) { + return false; + } + for (Int i = 0; i < records.size(); ++i) { + results.set(i, this->arg1_values_[i][this->arg2_values_[i]]); + } + return true; +} + } // namespace expression using namespace expression; @@ -2301,6 +2403,7 @@ GRNXX_INSTANTIATE_EXPRESSION_EVALUATE(Int); GRNXX_INSTANTIATE_EXPRESSION_EVALUATE(Float); GRNXX_INSTANTIATE_EXPRESSION_EVALUATE(GeoPoint); GRNXX_INSTANTIATE_EXPRESSION_EVALUATE(Text); +GRNXX_INSTANTIATE_EXPRESSION_EVALUATE(Vector<Bool>); #undef GRNXX_INSTANTIATE_EXPRESSION_EVALUATE template <typename T> @@ -2475,6 +2578,9 @@ unique_ptr<Node> ExpressionBuilder::create_datum_node( case TEXT_DATA: { return DatumNode<Text>::create(error, datum.force_text()); } + case BOOL_VECTOR_DATA: { + return DatumNode<Vector<Bool>>::create(error, datum.force_bool_vector()); + } default: { // TODO: Other types are not supported yet. GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not supported yet"); @@ -2527,6 +2633,9 @@ unique_ptr<Node> ExpressionBuilder::create_column_node( case TEXT_DATA: { return ColumnNode<Text>::create(error, column); } + case BOOL_VECTOR_DATA: { + return ColumnNode<Vector<Bool>>::create(error, column); + } default: { // TODO: Other types are not supported yet. GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not supported yet"); @@ -2731,6 +2840,10 @@ unique_ptr<Node> ExpressionBuilder::create_binary_node( } return ModulusNode<Int>::create(error, std::move(arg1), std::move(arg2)); } + case SUBSCRIPT_OPERATOR: { + return create_subscript_node( + error, std::move(arg1), std::move(arg2)); + } default: { // TODO: Not supported yet. GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not supported yet"); @@ -2769,6 +2882,10 @@ unique_ptr<Node> ExpressionBuilder::create_equality_test_node( return ComparisonNode<typename T:: template Comparer<Text>>::create( error, std::move(arg1), std::move(arg2)); } + case BOOL_VECTOR_DATA: { + return ComparisonNode<typename T:: template Comparer<Vector<Bool>>>::create( + error, std::move(arg1), std::move(arg2)); + } // TODO: Support other types. default: { GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not supported yet"); @@ -2870,4 +2987,24 @@ unique_ptr<Node> ExpressionBuilder::create_arithmetic_node( } } +unique_ptr<Node> ExpressionBuilder::create_subscript_node( + Error *error, + unique_ptr<Node> &&arg1, + unique_ptr<Node> &&arg2) { + if (arg2->data_type() != INT_DATA) { + GRNXX_ERROR_SET(error, INVALID_OPERAND, "Invalid data type"); + return nullptr; + } + switch (arg1->data_type()) { + case BOOL_VECTOR_DATA: { + return SubscriptNode<Bool>::create( + error, std::move(arg1), std::move(arg2)); + } + default: { + GRNXX_ERROR_SET(error, INVALID_OPERAND, "Invalid data type"); + return nullptr; + } + } +} + } // namespace grnxx Modified: test/test_column.cpp (+15 -15) =================================================================== --- test/test_column.cpp 2014-08-27 14:57:10 +0900 (1c970f9) +++ test/test_column.cpp 2014-08-27 16:49:17 +0900 (862ea70) @@ -87,14 +87,14 @@ void test_column() { // Create a column named "VectorBoolColumn". // The column stores Text values. - auto vector_bool_column = table->create_column(&error, "VectorBoolColumn", - grnxx::VECTOR_BOOL_DATA); - assert(vector_bool_column); - assert(vector_bool_column->table() == table); - assert(vector_bool_column->name() == "VectorBoolColumn"); - assert(vector_bool_column->data_type() == grnxx::VECTOR_BOOL_DATA); - assert(!vector_bool_column->has_key_attribute()); - assert(vector_bool_column->num_indexes() == 0); + auto bool_vector_column = table->create_column(&error, "VectorBoolColumn", + grnxx::BOOL_VECTOR_DATA); + assert(bool_vector_column); + assert(bool_vector_column->table() == table); + assert(bool_vector_column->name() == "VectorBoolColumn"); + assert(bool_vector_column->data_type() == grnxx::BOOL_VECTOR_DATA); + assert(!bool_vector_column->has_key_attribute()); + assert(bool_vector_column->num_indexes() == 0); grnxx::Datum datum; @@ -115,16 +115,16 @@ void test_column() { assert(datum.type() == grnxx::TEXT_DATA); assert(datum.force_text() == ""); - assert(vector_bool_column->get(&error, 1, &datum)); - assert(datum.type() == grnxx::VECTOR_BOOL_DATA); - assert(datum.force_vector_bool() == grnxx::Vector<grnxx::Bool>{}); + assert(bool_vector_column->get(&error, 1, &datum)); + assert(datum.type() == grnxx::BOOL_VECTOR_DATA); + assert(datum.force_bool_vector() == grnxx::Vector<grnxx::Bool>{}); // Set and get values. assert(bool_column->set(&error, 1, grnxx::Bool(true))); assert(int_column->set(&error, 1, grnxx::Int(123))); assert(float_column->set(&error, 1, grnxx::Float(0.25))); assert(text_column->set(&error, 1, grnxx::Text("Hello, world!"))); - assert(vector_bool_column->set(&error, 1, + assert(bool_vector_column->set(&error, 1, grnxx::Vector<grnxx::Bool>{ true, false, true })); assert(bool_column->get(&error, 1, &datum)); @@ -143,9 +143,9 @@ void test_column() { assert(datum.type() == grnxx::TEXT_DATA); assert(datum.force_text() == "Hello, world!"); - assert(vector_bool_column->get(&error, 1, &datum)); - assert(datum.type() == grnxx::VECTOR_BOOL_DATA); - assert((datum.force_vector_bool() == + assert(bool_vector_column->get(&error, 1, &datum)); + assert(datum.type() == grnxx::BOOL_VECTOR_DATA); + assert((datum.force_bool_vector() == grnxx::Vector<grnxx::Bool>{ true, false, true })); } Modified: test/test_expression.cpp (+117 -0) =================================================================== --- test/test_expression.cpp 2014-08-27 14:57:10 +0900 (e00eefc) +++ test/test_expression.cpp 2014-08-27 16:49:17 +0900 (e3b1b32) @@ -40,6 +40,8 @@ struct { grnxx::Array<grnxx::Text> text2_values; grnxx::Array<std::string> text_bodies; grnxx::Array<std::string> text2_bodies; + grnxx::Array<grnxx::Vector<grnxx::Bool>> bool_vector_values; + grnxx::Array<grnxx::Vector<grnxx::Bool>> bool_vector2_values; } test; void init_test() { @@ -78,6 +80,14 @@ void init_test() { assert(text_column); assert(text2_column); + data_type = grnxx::BOOL_VECTOR_DATA; + auto bool_vector_column = + test.table->create_column(&error, "BoolVector", data_type); + auto bool_vector2_column = + test.table->create_column(&error, "BoolVector2", data_type); + assert(bool_vector_column); + assert(bool_vector2_column); + // Generate random values. // Bool: true or false. // Int: [0, 100). @@ -97,6 +107,8 @@ void init_test() { assert(test.text2_values.resize(&error, NUM_ROWS + 1)); assert(test.text_bodies.resize(&error, NUM_ROWS + 1)); assert(test.text2_bodies.resize(&error, NUM_ROWS + 1)); + assert(test.bool_vector_values.resize(&error, NUM_ROWS + 1)); + assert(test.bool_vector2_values.resize(&error, NUM_ROWS + 1)); for (grnxx::Int i = 1; i <= NUM_ROWS; ++i) { test.bool_values.set(i, (mersenne_twister() & 1) != 0); test.bool2_values.set(i, (mersenne_twister() & 1) != 0); @@ -122,6 +134,13 @@ void init_test() { test.text2_bodies[i][j] = '0' + (mersenne_twister() % 10); } test.text2_values.set(i, grnxx::Text(test.text2_bodies[i].data(), length)); + + grnxx::uint64_t bits = mersenne_twister(); + grnxx::Int size = mersenne_twister() % 59; + test.bool_vector_values.set(i, grnxx::Vector<grnxx::Bool>(bits, size)); + bits = mersenne_twister(); + size = mersenne_twister() % 59; + test.bool_vector2_values.set(i, grnxx::Vector<grnxx::Bool>(bits, size)); } // Store generated values into columns. @@ -137,6 +156,10 @@ void init_test() { assert(float2_column->set(&error, row_id, test.float2_values[i])); assert(text_column->set(&error, row_id, test.text_values[i])); assert(text2_column->set(&error, row_id, test.text2_values[i])); + assert(bool_vector_column->set(&error, row_id, + test.bool_vector_values[i])); + assert(bool_vector2_column->set(&error, row_id, + test.bool_vector2_values[i])); } } @@ -227,6 +250,20 @@ void test_constant() { for (grnxx::Int i = 0; i < text_results.size(); ++i) { assert(text_results[i] == "ABC"); } + + // Test an expression ({ true, false, true }). + assert(builder->push_datum( + &error, grnxx::Vector<grnxx::Bool>({ true, false, true }))); + expression = builder->release(&error); + assert(expression); + + grnxx::Array<grnxx::Vector<grnxx::Bool>> bool_vector_results; + assert(expression->evaluate(&error, records, &bool_vector_results)); + assert(bool_vector_results.size() == test.table->num_rows()); + for (grnxx::Int i = 0; i < bool_vector_results.size(); ++i) { + assert(bool_vector_results[i] == + grnxx::Vector<grnxx::Bool>({ true, false, true })); + } } void test_column() { @@ -320,6 +357,24 @@ void test_column() { assert(text_results[i] == test.text_values[row_id]); } + // Test an expression (BoolVector). + assert(builder->push_column(&error, "BoolVector")); + expression = builder->release(&error); + assert(expression); + + records.clear(); + cursor = test.table->create_cursor(&error); + assert(cursor); + assert(cursor->read_all(&error, &records) == test.table->num_rows()); + + grnxx::Array<grnxx::Vector<grnxx::Bool>> bool_vector_results; + assert(expression->evaluate(&error, records, &bool_vector_results)); + assert(bool_vector_results.size() == test.table->num_rows()); + for (grnxx::Int i = 0; i < bool_vector_results.size(); ++i) { + grnxx::Int row_id = records.get_row_id(i); + assert(bool_vector_results[i] == test.bool_vector_values[row_id]); + } + // Test and expression (_id). assert(builder->push_column(&error, "_id")); expression = builder->release(&error); @@ -822,6 +877,37 @@ void test_equal() { } } assert(records.size() == count); + + // Test an expression (BoolVector == BoolVector2). + assert(builder->push_column(&error, "BoolVector")); + assert(builder->push_column(&error, "BoolVector2")); + assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR)); + expression = builder->release(&error); + assert(expression); + + records.clear(); + cursor = test.table->create_cursor(&error); + assert(cursor); + assert(cursor->read_all(&error, &records) == test.table->num_rows()); + + results.clear(); + assert(expression->evaluate(&error, records, &results)); + assert(results.size() == test.table->num_rows()); + for (grnxx::Int i = 0; i < results.size(); ++i) { + grnxx::Int row_id = records.get_row_id(i); + assert(results[i] == (test.bool_vector_values[row_id] == + test.bool_vector2_values[row_id])); + } + + assert(expression->filter(&error, &records)); + count = 0; + for (grnxx::Int i = 1; i < test.bool_vector_values.size(); ++i) { + if (test.bool_vector_values[i] == test.bool_vector2_values[i]) { + assert(records.get_row_id(count) == i); + ++count; + } + } + assert(records.size() == count); } void test_not_equal() { @@ -954,6 +1040,37 @@ void test_not_equal() { } } assert(records.size() == count); + + // Test an expression (BoolVector != BoolVector2). + assert(builder->push_column(&error, "BoolVector")); + assert(builder->push_column(&error, "BoolVector2")); + assert(builder->push_operator(&error, grnxx::NOT_EQUAL_OPERATOR)); + expression = builder->release(&error); + assert(expression); + + records.clear(); + cursor = test.table->create_cursor(&error); + assert(cursor); + assert(cursor->read_all(&error, &records) == test.table->num_rows()); + + results.clear(); + assert(expression->evaluate(&error, records, &results)); + assert(results.size() == test.table->num_rows()); + for (grnxx::Int i = 0; i < results.size(); ++i) { + grnxx::Int row_id = records.get_row_id(i); + assert(results[i] == (test.bool_vector_values[row_id] != + test.bool_vector2_values[row_id])); + } + + assert(expression->filter(&error, &records)); + count = 0; + for (grnxx::Int i = 1; i < test.bool_vector_values.size(); ++i) { + if (test.bool_vector_values[i] != test.bool_vector2_values[i]) { + assert(records.get_row_id(count) == i); + ++count; + } + } + assert(records.size() == count); } void test_less() { -------------- next part -------------- HTML����������������������������... Download