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::utils;
|
||||
|
||||
const auto payloads = PAYLOAD.as( "payloads" );
|
||||
const auto tasks = TASK.as( "tasks" );
|
||||
|
||||
const auto stmt1 = query:: query::select( { tasks.payload, payloads.id } )
|
||||
.from( tasks, payloads )
|
||||
.where ( payloads.id == tasks.payload )
|
||||
|
|
|
|||
|
|
@ -96,18 +96,42 @@ public:
|
|||
}
|
||||
|
||||
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*/) {
|
||||
const auto it = schema_.find(typeid(typename CollectionType::value_type::value_type));
|
||||
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(id);
|
||||
if (it == schema_.end()) {
|
||||
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:
|
||||
basic_schema& schema_;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include "matador/object/object_proxy.hpp"
|
||||
#include "matador/object/object_ptr.hpp"
|
||||
#include "matador/object/collection_proxy.hpp"
|
||||
#include "matador/object/join_columns_collector.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <stack>
|
||||
|
|
@ -112,26 +113,15 @@ public:
|
|||
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>
|
||||
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>
|
||||
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>
|
||||
void bind(const Type &obj) {
|
||||
|
|
@ -186,7 +176,7 @@ protected:
|
|||
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> *) {
|
||||
using value_type = typename CollectionType::value_type::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>
|
||||
bool query_result_impl::fetch(Type &obj) {
|
||||
bool first = true;
|
||||
|
|
|
|||
|
|
@ -187,10 +187,7 @@ utils::result<object::object_ptr<Type>, utils::error> statement::fetch_one() {
|
|||
});
|
||||
auto first = records.begin();
|
||||
if (first == records.end()) {
|
||||
return utils::failure(utils::error{
|
||||
error_code::FETCH_FAILED,
|
||||
"Failed to find entity."
|
||||
});
|
||||
return utils::failure(utils::error{error_code::FETCH_FAILED,"Failed to find entity."});
|
||||
}
|
||||
|
||||
return utils::ok(first.optr());
|
||||
|
|
@ -198,6 +195,7 @@ utils::result<object::object_ptr<Type>, utils::error> statement::fetch_one() {
|
|||
|
||||
template<class Type>
|
||||
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_);
|
||||
if (!result.is_ok()) {
|
||||
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,
|
||||
const std::shared_ptr<resolver_service> &resolver,
|
||||
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) {
|
||||
|
|
@ -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 {
|
||||
logger_->on_execute(sql);
|
||||
std::cout << sql << std::endl;
|
||||
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 {
|
||||
logger_->on_fetch(ctx.sql);
|
||||
std::cout << ctx.sql << std::endl;
|
||||
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.create(db); });
|
||||
|
||||
REQUIRE(result.is_ok());
|
||||
|
||||
repo.initialize_executor(db);
|
||||
|
||||
REQUIRE(db.exists(RECIPE.table_name()));
|
||||
REQUIRE(db.exists(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{
|
||||
{7, "Apple Crumble"},
|
||||
{8, "Beans Chili"},
|
||||
{9, "Fruit Salad"}
|
||||
{8, "Apple Crumble"},
|
||||
{9, "Beans Chili"},
|
||||
{10, "Fruit Salad"}
|
||||
};
|
||||
|
||||
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 {
|
||||
{ 7, 1 },
|
||||
{ 7, 4 },
|
||||
{ 7, 5 },
|
||||
{ 8, 6 },
|
||||
{ 8, 7 },
|
||||
{ 9, 1 },
|
||||
{ 9, 2 },
|
||||
{ 9, 3 }
|
||||
{ 8, 1 },
|
||||
{ 8, 4 },
|
||||
{ 8, 5 },
|
||||
{ 9, 6 },
|
||||
{ 9, 7 },
|
||||
{ 10, 1 },
|
||||
{ 10, 2 },
|
||||
{ 10, 3 }
|
||||
};
|
||||
|
||||
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 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)
|
||||
.join_left(ri).on(r.id == ri.recipe_id)
|
||||
.join_left(i).on(ri.ingredient_id == i.id)
|
||||
.order_by({r.id}).asc()
|
||||
.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()
|
||||
.fetch_all<ingredient>(db);
|
||||
REQUIRE(ingredients_result.is_ok());
|
||||
|
||||
for (const auto &ing : *ingredients_result) {
|
||||
REQUIRE(!ing->recipes.empty());
|
||||
for (const auto &ingr : *ingredients_result) {
|
||||
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/object/object_ptr.hpp"
|
||||
#include "matador/object/collection.hpp"
|
||||
#include "matador/object/many_to_many_relation.hpp"
|
||||
|
||||
#include <string>
|
||||
|
|
@ -15,7 +16,7 @@ struct recipe;
|
|||
struct ingredient {
|
||||
unsigned int id{};
|
||||
std::string name;
|
||||
std::vector<object::object_ptr<recipe> > recipes{};
|
||||
object::collection<object::object_ptr<recipe> > recipes{};
|
||||
|
||||
ingredient() = default;
|
||||
|
||||
|
|
@ -35,7 +36,7 @@ struct ingredient {
|
|||
struct recipe {
|
||||
unsigned int id{};
|
||||
std::string name;
|
||||
std::vector<object::object_ptr<ingredient> > ingredients{};
|
||||
object::collection<object::object_ptr<ingredient> > ingredients{};
|
||||
|
||||
recipe() = default;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue