collection has-many-to-many resolve progress
This commit is contained in:
parent
73abd61eba
commit
4d886f0343
|
|
@ -121,8 +121,10 @@ int main() {
|
||||||
|
|
||||||
using namespace matador::query::meta;
|
using namespace matador::query::meta;
|
||||||
using namespace matador::utils;
|
using namespace matador::utils;
|
||||||
|
|
||||||
const auto payloads = PAYLOAD.as( "payloads" );
|
const auto payloads = PAYLOAD.as( "payloads" );
|
||||||
const auto tasks = TASK.as( "tasks" );
|
const auto tasks = TASK.as( "tasks" );
|
||||||
|
|
||||||
const auto stmt1 = query:: query::select( { tasks.payload, payloads.id } )
|
const auto stmt1 = query:: query::select( { tasks.payload, payloads.id } )
|
||||||
.from( tasks, payloads )
|
.from( tasks, payloads )
|
||||||
.where ( payloads.id == tasks.payload )
|
.where ( payloads.id == tasks.payload )
|
||||||
|
|
|
||||||
|
|
@ -96,18 +96,42 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class CollectionType>
|
template<class CollectionType>
|
||||||
void on_has_many_to_many(const char *, CollectionType &, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {
|
void on_has_many_to_many(const char *id, CollectionType &, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &/*attr*/) {
|
||||||
const auto it = schema_.find(typeid(typename CollectionType::value_type::value_type));
|
const auto it = schema_.find(id);
|
||||||
if (it == schema_.end()) {
|
if (it == schema_.end()) {
|
||||||
throw query_builder_exception{query_build_error::UnknownType};
|
throw query_builder_exception{query_build_error::UnknownType};
|
||||||
}
|
}
|
||||||
if (!it->second.node().info().has_primary_key()) {
|
|
||||||
throw query_builder_exception{query_build_error::MissingPrimaryKey};
|
auto producer = std::make_unique<query_collection_resolver_producer<typename CollectionType::value_type>>(
|
||||||
|
schema_,
|
||||||
|
it->second.table(),
|
||||||
|
inverse_join_column,
|
||||||
|
root_type_,
|
||||||
|
join_column);
|
||||||
|
const object::collection_composite_key key{root_type_, typeid(typename CollectionType::value_type), inverse_join_column};
|
||||||
|
schema_.collection_resolver_producers_[key] = std::move(producer);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class CollectionType>
|
||||||
|
void on_has_many_to_many(const char *id, CollectionType &, const utils::foreign_attributes &/*attr*/) {
|
||||||
|
const auto it = schema_.find(id);
|
||||||
|
if (it == schema_.end()) {
|
||||||
|
throw query_builder_exception{query_build_error::UnknownType};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object::join_columns_collector collector;
|
||||||
|
const auto jc = collector.collect<typename CollectionType::value_type::value_type>();
|
||||||
|
|
||||||
|
auto producer = std::make_unique<query_collection_resolver_producer<typename CollectionType::value_type>>(
|
||||||
|
schema_,
|
||||||
|
it->second.table(),
|
||||||
|
jc.join_column,
|
||||||
|
root_type_,
|
||||||
|
jc.inverse_join_column);
|
||||||
|
const object::collection_composite_key key{root_type_, typeid(typename CollectionType::value_type), jc.join_column};
|
||||||
|
schema_.collection_resolver_producers_[key] = std::move(producer);
|
||||||
|
|
||||||
}
|
}
|
||||||
template<class ContainerType>
|
|
||||||
static void on_has_many_to_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
basic_schema& schema_;
|
basic_schema& schema_;
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
#include "matador/object/object_proxy.hpp"
|
#include "matador/object/object_proxy.hpp"
|
||||||
#include "matador/object/object_ptr.hpp"
|
#include "matador/object/object_ptr.hpp"
|
||||||
#include "matador/object/collection_proxy.hpp"
|
#include "matador/object/collection_proxy.hpp"
|
||||||
|
#include "matador/object/join_columns_collector.hpp"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
|
@ -112,26 +113,15 @@ public:
|
||||||
on_foreign_key(x, attr);
|
on_foreign_key(x, attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class ContainerType>
|
|
||||||
void on_has_many_to_many(const char *, ContainerType &, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &attr) {
|
|
||||||
if (attr.fetch() == utils::fetch_type::Lazy) {
|
|
||||||
|
|
||||||
} else {}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class ContainerType>
|
|
||||||
void on_has_many_to_many(const char *, ContainerType &, const utils::foreign_attributes &attr) {
|
|
||||||
if (attr.fetch() == utils::fetch_type::Lazy) {
|
|
||||||
|
|
||||||
} else {}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class CollectionType>
|
template<class CollectionType>
|
||||||
void on_has_many(const char * /*id*/, CollectionType &cont, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t<object::is_object_ptr<typename CollectionType::value_type>::value> * = nullptr);
|
void on_has_many(const char * /*id*/, CollectionType &cont, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t<object::is_object_ptr<typename CollectionType::value_type>::value> * = nullptr);
|
||||||
|
|
||||||
template<class CollectionType>
|
template<class CollectionType>
|
||||||
void on_has_many(const char * /*id*/, CollectionType &, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/, std::enable_if_t<!object::is_object_ptr<typename CollectionType::value_type>::value> * = nullptr) {
|
void on_has_many(const char * /*id*/, CollectionType &, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/, std::enable_if_t<!object::is_object_ptr<typename CollectionType::value_type>::value> * = nullptr) {
|
||||||
}
|
}
|
||||||
|
template<class CollectionType>
|
||||||
|
void on_has_many_to_many(const char *id, CollectionType &, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &attr);
|
||||||
|
template<class CollectionType>
|
||||||
|
void on_has_many_to_many(const char *, CollectionType &, const utils::foreign_attributes &attr);
|
||||||
|
|
||||||
template<class Type>
|
template<class Type>
|
||||||
void bind(const Type &obj) {
|
void bind(const Type &obj) {
|
||||||
|
|
@ -186,7 +176,7 @@ protected:
|
||||||
std::unordered_set<object::collection_composite_key, object::collection_composite_key_hash> initialized_collections_;
|
std::unordered_set<object::collection_composite_key, object::collection_composite_key_hash> initialized_collections_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class CollectionType>
|
template <class CollectionType>
|
||||||
void query_result_impl::on_has_many(const char *, CollectionType &cont, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t<object::is_object_ptr<typename CollectionType::value_type>::value> *) {
|
void query_result_impl::on_has_many(const char *, CollectionType &cont, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t<object::is_object_ptr<typename CollectionType::value_type>::value> *) {
|
||||||
using value_type = typename CollectionType::value_type::value_type;
|
using value_type = typename CollectionType::value_type::value_type;
|
||||||
auto object_resolver = resolver_->object_resolver<value_type>();
|
auto object_resolver = resolver_->object_resolver<value_type>();
|
||||||
|
|
@ -212,6 +202,59 @@ void query_result_impl::on_has_many(const char *, CollectionType &cont, const ch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class CollectionType>
|
||||||
|
void query_result_impl::on_has_many_to_many(const char *id, CollectionType &cont, const char *join_column, const char *, const utils::foreign_attributes &attr) {
|
||||||
|
using value_type = typename CollectionType::value_type::value_type;
|
||||||
|
auto object_resolver = resolver_->object_resolver<value_type>();
|
||||||
|
auto resolver = resolver_->collection_resolver<typename CollectionType::value_type>(result_type_, join_column);
|
||||||
|
if (attr.fetch() == utils::fetch_type::Lazy) {
|
||||||
|
cont.reset(std::make_shared<object::collection_proxy<typename CollectionType::value_type>>(resolver, current_pk_));
|
||||||
|
} else {
|
||||||
|
if (initialized_collections_.insert({result_type_, typeid(typename CollectionType::value_type), std::string{id}}).second) {
|
||||||
|
cont.reset(std::make_shared<object::collection_proxy<typename CollectionType::value_type>>(resolver, std::vector<typename CollectionType::value_type>()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto ti = std::type_index(typeid(value_type));
|
||||||
|
type_stack_.push(ti);
|
||||||
|
auto obj = std::make_shared<typename CollectionType::value_type::value_type>();
|
||||||
|
access::process(*this, *obj);
|
||||||
|
type_stack_.pop();
|
||||||
|
auto ptr = typename CollectionType::value_type(std::make_shared<object::object_proxy<value_type>>(object_resolver, obj));
|
||||||
|
const auto pk = ptr.primary_key();
|
||||||
|
if (ptr.primary_key().is_valid()) {
|
||||||
|
cont.push_back(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class CollectionType>
|
||||||
|
void query_result_impl::on_has_many_to_many(const char *id, CollectionType &cont, const utils::foreign_attributes &attr) {
|
||||||
|
using value_type = typename CollectionType::value_type::value_type;
|
||||||
|
object::join_columns_collector collector;
|
||||||
|
const auto jc = collector.collect<typename CollectionType::value_type::value_type>();
|
||||||
|
|
||||||
|
auto object_resolver = resolver_->object_resolver<value_type>();
|
||||||
|
auto resolver = resolver_->collection_resolver<typename CollectionType::value_type>(result_type_, jc.inverse_join_column);
|
||||||
|
if (attr.fetch() == utils::fetch_type::Lazy) {
|
||||||
|
cont.reset(std::make_shared<object::collection_proxy<typename CollectionType::value_type>>(resolver, current_pk_));
|
||||||
|
} else {
|
||||||
|
if (initialized_collections_.insert({result_type_, typeid(typename CollectionType::value_type), std::string{id}}).second) {
|
||||||
|
cont.reset(std::make_shared<object::collection_proxy<typename CollectionType::value_type>>(resolver, std::vector<typename CollectionType::value_type>()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto ti = std::type_index(typeid(value_type));
|
||||||
|
type_stack_.push(ti);
|
||||||
|
auto obj = std::make_shared<typename CollectionType::value_type::value_type>();
|
||||||
|
access::process(*this, *obj);
|
||||||
|
type_stack_.pop();
|
||||||
|
auto ptr = typename CollectionType::value_type(std::make_shared<object::object_proxy<value_type>>(object_resolver, obj));
|
||||||
|
const auto pk = ptr.primary_key();
|
||||||
|
if (ptr.primary_key().is_valid()) {
|
||||||
|
cont.push_back(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<class Type>
|
template<class Type>
|
||||||
bool query_result_impl::fetch(Type &obj) {
|
bool query_result_impl::fetch(Type &obj) {
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
|
||||||
|
|
@ -187,10 +187,7 @@ utils::result<object::object_ptr<Type>, utils::error> statement::fetch_one() {
|
||||||
});
|
});
|
||||||
auto first = records.begin();
|
auto first = records.begin();
|
||||||
if (first == records.end()) {
|
if (first == records.end()) {
|
||||||
return utils::failure(utils::error{
|
return utils::failure(utils::error{error_code::FETCH_FAILED,"Failed to find entity."});
|
||||||
error_code::FETCH_FAILED,
|
|
||||||
"Failed to find entity."
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return utils::ok(first.optr());
|
return utils::ok(first.optr());
|
||||||
|
|
@ -198,6 +195,7 @@ utils::result<object::object_ptr<Type>, utils::error> statement::fetch_one() {
|
||||||
|
|
||||||
template<class Type>
|
template<class Type>
|
||||||
utils::result<std::shared_ptr<Type>, utils::error> statement::fetch_one_raw() {
|
utils::result<std::shared_ptr<Type>, utils::error> statement::fetch_one_raw() {
|
||||||
|
std::cout << statement_proxy_->sql() << std::endl;
|
||||||
auto result = statement_proxy_->fetch(*bindings_);
|
auto result = statement_proxy_->fetch(*bindings_);
|
||||||
if (!result.is_ok()) {
|
if (!result.is_ok()) {
|
||||||
return utils::failure(result.err());
|
return utils::failure(result.err());
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ connection::connection(const std::string& dns, const logger_ptr &sql_logger)
|
||||||
connection::connection(const std::string &dns,
|
connection::connection(const std::string &dns,
|
||||||
const std::shared_ptr<resolver_service> &resolver,
|
const std::shared_ptr<resolver_service> &resolver,
|
||||||
const logger_ptr &sql_logger)
|
const logger_ptr &sql_logger)
|
||||||
: connection(connection_info::parse(dns), std::move(resolver), sql_logger)
|
: connection(connection_info::parse(dns), resolver, sql_logger)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
connection::connection(const connection &x) {
|
connection::connection(const connection &x) {
|
||||||
|
|
@ -165,6 +165,7 @@ utils::result<bool, utils::error> connection::exists(const std::string &table_na
|
||||||
|
|
||||||
utils::result<size_t, utils::error> connection::execute(const std::string &sql) const {
|
utils::result<size_t, utils::error> connection::execute(const std::string &sql) const {
|
||||||
logger_->on_execute(sql);
|
logger_->on_execute(sql);
|
||||||
|
std::cout << sql << std::endl;
|
||||||
return connection_->execute(sql);
|
return connection_->execute(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -203,6 +204,7 @@ bool has_unknown_columns(const std::vector<object::attribute> &columns) {
|
||||||
|
|
||||||
utils::result<std::unique_ptr<query_result_impl>, utils::error> connection::fetch(const query_context &ctx) const {
|
utils::result<std::unique_ptr<query_result_impl>, utils::error> connection::fetch(const query_context &ctx) const {
|
||||||
logger_->on_fetch(ctx.sql);
|
logger_->on_fetch(ctx.sql);
|
||||||
|
std::cout << ctx.sql << std::endl;
|
||||||
return connection_->fetch(ctx);
|
return connection_->fetch(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -821,6 +821,10 @@ TEST_CASE_METHOD(QueryFixture, "Test load entity with eager has many to many rel
|
||||||
.and_then( [this] { return repo.attach<recipe>("recipes"); } )
|
.and_then( [this] { return repo.attach<recipe>("recipes"); } )
|
||||||
.and_then([this] {return repo.create(db); });
|
.and_then([this] {return repo.create(db); });
|
||||||
|
|
||||||
|
REQUIRE(result.is_ok());
|
||||||
|
|
||||||
|
repo.initialize_executor(db);
|
||||||
|
|
||||||
REQUIRE(db.exists(RECIPE.table_name()));
|
REQUIRE(db.exists(RECIPE.table_name()));
|
||||||
REQUIRE(db.exists(INGREDIENT.table_name()));
|
REQUIRE(db.exists(INGREDIENT.table_name()));
|
||||||
REQUIRE(db.exists(RECIPE_INGREDIENT.table_name()));
|
REQUIRE(db.exists(RECIPE_INGREDIENT.table_name()));
|
||||||
|
|
@ -845,9 +849,9 @@ TEST_CASE_METHOD(QueryFixture, "Test load entity with eager has many to many rel
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<recipe> recipes{
|
std::vector<recipe> recipes{
|
||||||
{7, "Apple Crumble"},
|
{8, "Apple Crumble"},
|
||||||
{8, "Beans Chili"},
|
{9, "Beans Chili"},
|
||||||
{9, "Fruit Salad"}
|
{10, "Fruit Salad"}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const auto &r: recipes) {
|
for (const auto &r: recipes) {
|
||||||
|
|
@ -860,14 +864,14 @@ TEST_CASE_METHOD(QueryFixture, "Test load entity with eager has many to many rel
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<int, int>> recipe_ingredients {
|
std::vector<std::pair<int, int>> recipe_ingredients {
|
||||||
{ 7, 1 },
|
{ 8, 1 },
|
||||||
{ 7, 4 },
|
{ 8, 4 },
|
||||||
{ 7, 5 },
|
{ 8, 5 },
|
||||||
{ 8, 6 },
|
{ 9, 6 },
|
||||||
{ 8, 7 },
|
{ 9, 7 },
|
||||||
{ 9, 1 },
|
{ 10, 1 },
|
||||||
{ 9, 2 },
|
{ 10, 2 },
|
||||||
{ 9, 3 }
|
{ 10, 3 }
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const auto & [recipe_id, ingredient_id]: recipe_ingredients) {
|
for (const auto & [recipe_id, ingredient_id]: recipe_ingredients) {
|
||||||
|
|
@ -883,15 +887,31 @@ TEST_CASE_METHOD(QueryFixture, "Test load entity with eager has many to many rel
|
||||||
const auto ri= RECIPE_INGREDIENT.as("ri");
|
const auto ri= RECIPE_INGREDIENT.as("ri");
|
||||||
const auto i = INGREDIENT.as("i");
|
const auto i = INGREDIENT.as("i");
|
||||||
|
|
||||||
auto ingredients_result = query::select({r.id, r.name, ri.ingredient_id, i.name})
|
auto recipes_result = query::select({r.id, r.name })
|
||||||
.from(r)
|
.from(r)
|
||||||
.join_left(ri).on(r.id == ri.recipe_id)
|
.order_by({r.id}).asc()
|
||||||
.join_left(i).on(ri.ingredient_id == i.id)
|
.fetch_all<recipe>(db);
|
||||||
|
REQUIRE(recipes_result.is_ok());
|
||||||
|
|
||||||
|
for (const auto &reps : *recipes_result) {
|
||||||
|
REQUIRE(!reps->ingredients.empty());
|
||||||
|
for (const auto &ingr : reps->ingredients) {
|
||||||
|
std::cout << ingr->name << " (" << reps->name << ")" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ingredients_result = query::select({i.id, i.name, ri.recipe_id, r.name})
|
||||||
|
.from(i)
|
||||||
|
.join_left(ri).on(i.id == ri.ingredient_id)
|
||||||
|
.join_left(r).on(ri.recipe_id == r.id)
|
||||||
.order_by({i.id, r.id}).asc()
|
.order_by({i.id, r.id}).asc()
|
||||||
.fetch_all<ingredient>(db);
|
.fetch_all<ingredient>(db);
|
||||||
REQUIRE(ingredients_result.is_ok());
|
REQUIRE(ingredients_result.is_ok());
|
||||||
|
|
||||||
for (const auto &ing : *ingredients_result) {
|
for (const auto &ingr : *ingredients_result) {
|
||||||
REQUIRE(!ing->recipes.empty());
|
REQUIRE(!ingr->recipes.empty());
|
||||||
|
for (const auto &recipe : ingr->recipes) {
|
||||||
|
std::cout << recipe->name << " (" << ingr->name << ")" << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
#include "matador/utils/foreign_attributes.hpp"
|
#include "matador/utils/foreign_attributes.hpp"
|
||||||
|
|
||||||
#include "matador/object/object_ptr.hpp"
|
#include "matador/object/object_ptr.hpp"
|
||||||
|
#include "matador/object/collection.hpp"
|
||||||
#include "matador/object/many_to_many_relation.hpp"
|
#include "matador/object/many_to_many_relation.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
@ -15,7 +16,7 @@ struct recipe;
|
||||||
struct ingredient {
|
struct ingredient {
|
||||||
unsigned int id{};
|
unsigned int id{};
|
||||||
std::string name;
|
std::string name;
|
||||||
std::vector<object::object_ptr<recipe> > recipes{};
|
object::collection<object::object_ptr<recipe> > recipes{};
|
||||||
|
|
||||||
ingredient() = default;
|
ingredient() = default;
|
||||||
|
|
||||||
|
|
@ -35,7 +36,7 @@ struct ingredient {
|
||||||
struct recipe {
|
struct recipe {
|
||||||
unsigned int id{};
|
unsigned int id{};
|
||||||
std::string name;
|
std::string name;
|
||||||
std::vector<object::object_ptr<ingredient> > ingredients{};
|
object::collection<object::object_ptr<ingredient> > ingredients{};
|
||||||
|
|
||||||
recipe() = default;
|
recipe() = default;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue