Commit c3963bc4 by Maarten L. Hekkelman

Fixed linked update (regression)

parent 23bd51ac
......@@ -718,11 +718,13 @@ struct ConditionImpl
virtual void prepare(const Category& c) {}
virtual bool test(const Category& c, const Row& r) const = 0;
virtual void str(std::ostream& os) const = 0;
};
struct AllConditionImpl : public ConditionImpl
{
virtual bool test(const Category& c, const Row& r) const { return true; }
virtual void str(std::ostream& os) const { os << "*"; }
};
struct orConditionImpl;
......@@ -789,11 +791,20 @@ class Condition
std::swap(mPrepared, rhs.mPrepared);
}
friend std::ostream& operator<<(std::ostream& os, const Condition& cond);
private:
detail::ConditionImpl* mImpl;
bool mPrepared = false;
};
inline std::ostream& operator<<(std::ostream& os, const Condition& cond)
{
if (cond.mImpl)
cond.mImpl->str(os);
return os;
}
namespace detail
{
......@@ -808,7 +819,12 @@ struct KeyIsEmptyConditionImpl : public ConditionImpl
{
return r[mItemIx].empty();
}
virtual void str(std::ostream& os) const
{
os << mItemTag << " IS NULL";
}
std::string mItemTag;
size_t mItemIx;
};
......@@ -816,8 +832,8 @@ struct KeyIsEmptyConditionImpl : public ConditionImpl
struct KeyCompareConditionImpl : public ConditionImpl
{
template<typename COMP>
KeyCompareConditionImpl(const std::string& ItemTag, COMP&& comp)
: mItemTag(ItemTag), mComp(std::move(comp)) {}
KeyCompareConditionImpl(const std::string& ItemTag, COMP&& comp, const std::string& s)
: mItemTag(ItemTag), mComp(std::move(comp)), mStr(s) {}
virtual void prepare(const Category& c);
......@@ -826,10 +842,16 @@ struct KeyCompareConditionImpl : public ConditionImpl
return mComp(c, r, mCaseInsensitive);
}
virtual void str(std::ostream& os) const
{
os << mItemTag << (mCaseInsensitive ? "^ " : " ") << mStr;
}
std::string mItemTag;
size_t mItemIx;
bool mCaseInsensitive = false;
std::function<bool(const Category&, const Row&, bool)> mComp;
std::string mStr;
};
struct KeyMatchesConditionImpl : public ConditionImpl
......@@ -843,6 +865,11 @@ struct KeyMatchesConditionImpl : public ConditionImpl
{
return std::regex_match(r[mItemIx].as<std::string>(), mRx);
}
virtual void str(std::ostream& os) const
{
os << mItemTag << " =~ expression";
}
std::string mItemTag;
size_t mItemIx;
......@@ -858,6 +885,10 @@ struct anyIsConditionImpl : public ConditionImpl
: mValue(value) {}
virtual bool test(const Category& c, const Row& r) const;
virtual void str(std::ostream& os) const
{
os << "<any> == " << mValue;
}
valueType mValue;
};
......@@ -868,6 +899,10 @@ struct anyMatchesConditionImpl : public ConditionImpl
: mRx(rx) {}
virtual bool test(const Category& c, const Row& r) const;
virtual void str(std::ostream& os) const
{
os << "<any> =~ expression";
}
std::regex mRx;
};
......@@ -880,6 +915,11 @@ struct allConditionImpl : public ConditionImpl
{
return true;
}
virtual void str(std::ostream& os) const
{
os << "*";
}
};
struct andConditionImpl : public ConditionImpl
......@@ -908,6 +948,15 @@ struct andConditionImpl : public ConditionImpl
return mA->test(c, r) and mB->test(c, r);
}
virtual void str(std::ostream& os) const
{
os << '(';
mA->str(os);
os << ") AND (";
mB->str(os);
os << ')';
}
ConditionImpl* mA;
ConditionImpl* mB;
};
......@@ -937,7 +986,16 @@ struct orConditionImpl : public ConditionImpl
{
return mA->test(c, r) or mB->test(c, r);
}
virtual void str(std::ostream& os) const
{
os << '(';
mA->str(os);
os << ") OR (";
mB->str(os);
os << ')';
}
ConditionImpl* mA;
ConditionImpl* mB;
};
......@@ -964,7 +1022,14 @@ struct notConditionImpl : public ConditionImpl
{
return not mA->test(c, r);
}
virtual void str(std::ostream& os) const
{
os << "NOT (";
mA->str(os);
os << ')';
}
ConditionImpl* mA;
};
......@@ -1004,8 +1069,11 @@ struct Key
template<typename T>
Condition operator==(const Key& key, const T& v)
{
std::ostringstream s;
s << "== " << v;
return Condition(new detail::KeyCompareConditionImpl(key.mItemTag, [tag = key.mItemTag, v](const Category& c, const Row& r, bool icase)
{ return r[tag].template compare<T>(v, icase) == 0; }));
{ return r[tag].template compare<T>(v, icase) == 0; }, s.str()));
}
// inline Condition operator==(const Key& key, const detail::ItemReference& v)
......@@ -1037,29 +1105,41 @@ inline Condition operator!=(const Key& key, const char* v)
template<typename T>
Condition operator>(const Key& key, const T& v)
{
std::ostringstream s;
s << ">" << v;
return Condition(new detail::KeyCompareConditionImpl(key.mItemTag, [tag = key.mItemTag, v](const Category& c, const Row& r, bool icase)
{ return r[tag].template compare<T>(v, icase) > 0; }));
{ return r[tag].template compare<T>(v, icase) > 0; }, s.str()));
}
template<typename T>
Condition operator>=(const Key& key, const T& v)
{
std::ostringstream s;
s << ">=" << v;
return Condition(new detail::KeyCompareConditionImpl(key.mItemTag, [tag = key.mItemTag, v](const Category& c, const Row& r, bool icase)
{ return r[tag].template compare<T>(v, icase) >= 0; }));
{ return r[tag].template compare<T>(v, icase) >= 0; }, s.str()));
}
template<typename T>
Condition operator<(const Key& key, const T& v)
{
std::ostringstream s;
s << "<" << v;
return Condition(new detail::KeyCompareConditionImpl(key.mItemTag, [tag = key.mItemTag, v](const Category& c, const Row& r, bool icase)
{ return r[tag].template compare<T>(v, icase) < 0; }));
{ return r[tag].template compare<T>(v, icase) < 0; }, s.str()));
}
template<typename T>
Condition operator<=(const Key& key, const T& v)
{
std::ostringstream s;
s << "<=" << v;
return Condition(new detail::KeyCompareConditionImpl(key.mItemTag, [tag = key.mItemTag, v](const Category& c, const Row& r, bool icase)
{ return r[tag].template compare<T>(v, icase) <= 0; }));
{ return r[tag].template compare<T>(v, icase) <= 0; }, s.str()));
}
template<>
......
......@@ -2519,7 +2519,7 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
std::string pk = linked->mParentKeys[ix];
std::string ck = linked->mChildKeys[ix];
// TODO add code to *NOT* test mandatory fiels for Empty
// TODO add code to *NOT* test mandatory fields for Empty
if (pk == iv->mTag)
{
......@@ -2529,16 +2529,19 @@ void Row::assign(size_t column, const std::string& value, bool skipUpdateLinked)
else
{
const char* value = (*this)[pk].c_str();
cond = std::move(cond) && ((Key(ck) == value) or Key(ck) == Empty());
if (*value == 0)
cond = std::move(cond) && Key(ck) == Empty();
else
cond = std::move(cond) && ((Key(ck) == value) or Key(ck) == Empty());
}
}
// if (cif::VERBOSE > 2)
// {
// std::std::cerr << "Parent: " << linked->mParentCategory << " Child: " << linked->mChildCategory << std::std::endl
// << cond << std::std::endl;
// }
if (cif::VERBOSE >= 2)
{
std::cerr << "Parent: " << linked->mParentCategory << " Child: " << linked->mChildCategory << std::endl
<< cond << std::endl;
}
auto rows = childCat->find(std::move(cond));
for (auto& cr: rows)
......
......@@ -894,86 +894,110 @@ void DictParser::linkItems()
std::map<std::tuple<std::string,std::string>,size_t> linkIndex;
std::map<std::tuple<std::string,std::string>,int> linkGroupIds;
std::vector<std::tuple<std::vector<std::string>,std::vector<std::string>>> linkKeys;
for (auto gl: dict["pdbx_item_linked_group_list"])
auto addLink = [&](size_t ix, const std::string& pk, const std::string& ck)
{
std::string child, parent;
int link_group_id;
cif::tie(child, parent, link_group_id) = gl.get("child_name", "parent_name", "link_group_id");
auto civ = mValidator.getValidatorForItem(child);
if (civ == nullptr)
error("in pdbx_item_linked_group_list, item '" + child + "' is not specified");
auto piv = mValidator.getValidatorForItem(parent);
if (piv == nullptr)
error("in pdbx_item_linked_group_list, item '" + parent + "' is not specified");
auto key = std::make_tuple(piv->mCategory->mName, civ->mCategory->mName);
if (not linkIndex.count(key))
auto&& [pkeys, ckeys] = linkKeys.at(ix);
bool found = false;
for (size_t i = 0; i < pkeys.size(); ++i)
{
linkIndex[key] = linkKeys.size();
linkKeys.push_back({});
if (pkeys[i] == pk and ckeys[i] == ck)
{
found = true;
break;
}
}
linkGroupIds[key] = link_group_id;
if (not found)
{
pkeys.push_back(pk);
ckeys.push_back(ck);
}
size_t ix = linkIndex.at(key);
std::get<0>(linkKeys.at(ix)).push_back(piv->mTag);
std::get<1>(linkKeys.at(ix)).push_back(civ->mTag);
}
};
// for links recorded in categories but not in pdbx_item_linked_group_list
for (auto li: mImpl->mLinkedItems)
{
std::string child, parent;
std::tie(child, parent) = li;
auto civ = mValidator.getValidatorForItem(child);
if (civ == nullptr)
error("in pdbx_item_linked_group_list, item '" + child + "' is not specified");
auto piv = mValidator.getValidatorForItem(parent);
if (piv == nullptr)
error("in pdbx_item_linked_group_list, item '" + parent + "' is not specified");
auto& linkedGroupList = dict["pdbx_item_linked_group_list"];
auto key = std::make_tuple(piv->mCategory->mName, civ->mCategory->mName);
if (not linkIndex.count(key))
if (linkedGroupList.empty())
{
// for links recorded in categories but not in pdbx_item_linked_group_list
for (auto li: mImpl->mLinkedItems)
{
linkIndex[key] = linkKeys.size();
linkKeys.push_back({});
std::string child, parent;
std::tie(child, parent) = li;
auto civ = mValidator.getValidatorForItem(child);
if (civ == nullptr)
error("in pdbx_item_linked_group_list, item '" + child + "' is not specified");
auto piv = mValidator.getValidatorForItem(parent);
if (piv == nullptr)
error("in pdbx_item_linked_group_list, item '" + parent + "' is not specified");
auto key = std::make_tuple(piv->mCategory->mName, civ->mCategory->mName);
if (linkIndex.count(key))
{
linkIndex[key] = linkKeys.size();
linkKeys.push_back({});
}
size_t ix = linkIndex.at(key);
addLink(ix, piv->mTag, civ->mTag);
}
size_t ix = linkIndex.at(key);
std::get<0>(linkKeys.at(ix)).push_back(piv->mTag);
std::get<1>(linkKeys.at(ix)).push_back(civ->mTag);
}
auto& linkedGroup = dict["pdbx_item_linked_group"];
// now store the links in the validator
for (auto& kv: linkIndex)
else
{
ValidateLink link = {};
std::tie(link.mParentCategory, link.mChildCategory) = kv.first;
for (auto gl: linkedGroupList)
{
std::string child, parent;
int link_group_id;
cif::tie(child, parent, link_group_id) = gl.get("child_name", "parent_name", "link_group_id");
auto civ = mValidator.getValidatorForItem(child);
if (civ == nullptr)
error("in pdbx_item_linked_group_list, item '" + child + "' is not specified");
auto piv = mValidator.getValidatorForItem(parent);
if (piv == nullptr)
error("in pdbx_item_linked_group_list, item '" + parent + "' is not specified");
auto key = std::make_tuple(piv->mCategory->mName, civ->mCategory->mName);
if (not linkIndex.count(key))
{
linkIndex[key] = linkKeys.size();
linkKeys.push_back({});
if (linkGroupIds.count(kv.first))
link.mLinkGroupID = linkGroupIds[kv.first];
std::tie(link.mParentKeys, link.mChildKeys) = linkKeys[kv.second];
linkGroupIds[key] = link_group_id;
}
size_t ix = linkIndex.at(key);
addLink(ix, piv->mTag, civ->mTag);
}
// look up the label
for (auto r: linkedGroup.find(cif::Key("category_id") == link.mChildCategory and cif::Key("link_group_id") == link.mLinkGroupID))
auto& linkedGroup = dict["pdbx_item_linked_group"];
// now store the links in the validator
for (auto& kv: linkIndex)
{
link.mLinkGroupLabel = r["label"].as<std::string>();
break;
}
ValidateLink link = {};
std::tie(link.mParentCategory, link.mChildCategory) = kv.first;
if (linkGroupIds.count(kv.first))
link.mLinkGroupID = linkGroupIds[kv.first];
std::tie(link.mParentKeys, link.mChildKeys) = linkKeys[kv.second];
// look up the label
for (auto r: linkedGroup.find(cif::Key("category_id") == link.mChildCategory and cif::Key("link_group_id") == link.mLinkGroupID))
{
link.mLinkGroupLabel = r["label"].as<std::string>();
break;
}
mValidator.addLinkValidator(std::move(link));
mValidator.addLinkValidator(std::move(link));
}
}
// now make sure the itemType is specified for all itemValidators
for (auto& cv: mValidator.mCategoryValidators)
......
......@@ -52,6 +52,8 @@ cif::File operator""_cf(const char* text, size_t length)
BOOST_AUTO_TEST_CASE(ut1)
{
cif::VERBOSE = 1;
// using namespace mmcif;
auto f = R"(data_TEST
......@@ -403,4 +405,436 @@ mies Mies
BOOST_CHECK(cat1.size() == 2);
}
\ No newline at end of file
}
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(d3)
{
const char dict[] = R"(
data_test_dict.dic
_datablock.id test_dict.dic
_datablock.description
;
A test dictionary
;
_dictionary.title test_dict.dic
_dictionary.datablock_id test_dict.dic
_dictionary.version 1.0
loop_
_item_type_list.code
_item_type_list.primitive_code
_item_type_list.construct
code char
'[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
text char
'[][ \n\t()_,.;:"&<>/\{}'`~!@#$%?+=*A-Za-z0-9|^-]*'
int numb
'[+-]?[0-9]+'
save_cat_1
_category.description 'A simple test category'
_category.id cat_1
_category.mandatory_code no
_category_key.name '_cat_1.id'
save_
save__cat_1.id
_item.name '_cat_1.id'
_item.category_id cat_1
_item.mandatory_code yes
_item_linked.child_name '_cat_2.parent_id'
_item_linked.parent_name '_cat_1.id'
_item_type.code code
save_
save__cat_1.name1
_item.name '_cat_1.name1'
_item.category_id cat_1
_item.mandatory_code yes
_item_type.code text
save_
save__cat_1.name2
_item.name '_cat_1.name2'
_item.category_id cat_1
_item.mandatory_code no
_item_linked.child_name '_cat_2.name2'
_item_linked.parent_name '_cat_1.name2'
_item_type.code text
save_
save_cat_2
_category.description 'A second simple test category'
_category.id cat_2
_category.mandatory_code no
_category_key.name '_cat_2.id'
save_
save__cat_2.id
_item.name '_cat_2.id'
_item.category_id cat_2
_item.mandatory_code yes
_item_type.code int
save_
save__cat_2.parent_id
_item.name '_cat_2.parent_id'
_item.category_id cat_2
_item.mandatory_code yes
_item_type.code code
save_
save__cat_2.name2
_item.name '_cat_2.name2'
_item.category_id cat_2
_item.mandatory_code no
_item_type.code text
save_
save__cat_2.desc
_item.name '_cat_2.desc'
_item.category_id cat_2
_item.mandatory_code yes
_item_type.code text
save_
)";
struct membuf : public std::streambuf
{
membuf(char* text, size_t length)
{
this->setg(text, text, text + length);
}
} buffer(const_cast<char*>(dict), sizeof(dict) - 1);
std::istream is_dict(&buffer);
cif::File f;
f.loadDictionary(is_dict);
// --------------------------------------------------------------------
const char data[] = R"(
data_test
loop_
_cat_1.id
_cat_1.name1
_cat_1.name2
1 Aap aap
2 Noot noot
3 Mies mies
loop_
_cat_2.id
_cat_2.parent_id
_cat_2.name2
_cat_2.desc
1 1 aap 'Een dier'
2 1 . 'Een andere aap'
3 2 noot 'walnoot bijvoorbeeld'
4 2 n2 hazelnoot
)";
struct data_membuf : public std::streambuf
{
data_membuf(char* text, size_t length)
{
this->setg(text, text, text + length);
}
} data_buffer(const_cast<char*>(data), sizeof(data) - 1);
std::istream is_data(&data_buffer);
f.load(is_data);
auto& cat1 = f.firstDatablock()["cat_1"];
auto& cat2 = f.firstDatablock()["cat_2"];
// check a rename in parent and child
for (auto r: cat1.find(cif::Key("id") == 1))
{
r["id"] = 10;
break;
}
BOOST_CHECK(cat1.size() == 3);
BOOST_CHECK(cat2.size() == 4);
BOOST_CHECK(cat1.find(cif::Key("id") == 1).size() == 0);
BOOST_CHECK(cat1.find(cif::Key("id") == 10).size() == 1);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 1).size() == 0);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 10).size() == 2);
// check a rename in parent and child, this time only one child should be renamed
for (auto r: cat1.find(cif::Key("id") == 2))
{
r["id"] = 20;
break;
}
BOOST_CHECK(cat1.size() == 3);
BOOST_CHECK(cat2.size() == 4);
BOOST_CHECK(cat1.find(cif::Key("id") == 2).size() == 0);
BOOST_CHECK(cat1.find(cif::Key("id") == 20).size() == 1);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 2).size() == 1);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 20).size() == 1);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 2 and cif::Key("name2") == "noot").size() == 0);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 2 and cif::Key("name2") == "n2").size() == 1);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 20 and cif::Key("name2") == "noot").size() == 1);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 20 and cif::Key("name2") == "n2").size() == 0);
// // --------------------------------------------------------------------
// cat1.erase(cif::Key("id") == 10);
// BOOST_CHECK(cat1.size() == 2);
// BOOST_CHECK(cat2.size() == 2);
// cat1.erase(cif::Key("id") == 20);
// BOOST_CHECK(cat1.size() == 1);
// BOOST_CHECK(cat2.size() == 1);
}
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(d4)
{
const char dict[] = R"(
data_test_dict.dic
_datablock.id test_dict.dic
_datablock.description
;
A test dictionary
;
_dictionary.title test_dict.dic
_dictionary.datablock_id test_dict.dic
_dictionary.version 1.0
loop_
_item_type_list.code
_item_type_list.primitive_code
_item_type_list.construct
code char
'[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
text char
'[][ \n\t()_,.;:"&<>/\{}'`~!@#$%?+=*A-Za-z0-9|^-]*'
int numb
'[+-]?[0-9]+'
save_cat_1
_category.description 'A simple test category'
_category.id cat_1
_category.mandatory_code no
_category_key.name '_cat_1.id'
save_
save__cat_1.id
_item.name '_cat_1.id'
_item.category_id cat_1
_item.mandatory_code yes
_item_linked.child_name '_cat_2.parent_id'
_item_linked.parent_name '_cat_1.id'
_item_type.code int
save_
save__cat_1.id2
_item.name '_cat_1.id2'
_item.category_id cat_1
_item.mandatory_code no
_item_linked.child_name '_cat_2.parent_id2'
_item_linked.parent_name '_cat_1.id2'
_item_type.code code
save_
save__cat_1.id3
_item.name '_cat_1.id3'
_item.category_id cat_1
_item.mandatory_code no
_item_linked.child_name '_cat_2.parent_id3'
_item_linked.parent_name '_cat_1.id3'
_item_type.code text
save_
save_cat_2
_category.description 'A second simple test category'
_category.id cat_2
_category.mandatory_code no
_category_key.name '_cat_2.id'
save_
save__cat_2.id
_item.name '_cat_2.id'
_item.category_id cat_2
_item.mandatory_code yes
_item_type.code int
save_
save__cat_2.parent_id
_item.name '_cat_2.parent_id'
_item.category_id cat_2
_item.mandatory_code yes
_item_type.code int
save_
save__cat_2.parent_id2
_item.name '_cat_2.parent_id2'
_item.category_id cat_2
_item.mandatory_code no
_item_type.code code
save_
save__cat_2.parent_id3
_item.name '_cat_2.parent_id3'
_item.category_id cat_2
_item.mandatory_code no
_item_type.code code
save_
)";
struct membuf : public std::streambuf
{
membuf(char* text, size_t length)
{
this->setg(text, text, text + length);
}
} buffer(const_cast<char*>(dict), sizeof(dict) - 1);
std::istream is_dict(&buffer);
cif::File f;
f.loadDictionary(is_dict);
// --------------------------------------------------------------------
const char data[] = R"(
data_test
loop_
_cat_1.id
_cat_1.id2
_cat_1.id3
1 aap aap
2 . noot
3 mies .
4 . .
loop_
_cat_2.id
_cat_2.parent_id
_cat_2.parent_id2
_cat_2.parent_id3
1 1 aap aap
2 1 . x
3 1 aap .
4 2 noot noot
5 2 . noot
6 2 noot .
7 2 . .
8 3 mies mies
9 3 . mies
10 3 mies .
11 4 roos roos
12 4 . roos
13 4 roos .
)";
struct data_membuf : public std::streambuf
{
data_membuf(char* text, size_t length)
{
this->setg(text, text, text + length);
}
} data_buffer(const_cast<char*>(data), sizeof(data) - 1);
std::istream is_data(&data_buffer);
f.load(is_data);
auto& cat1 = f.firstDatablock()["cat_1"];
auto& cat2 = f.firstDatablock()["cat_2"];
// check a rename in parent and child
for (auto r: cat1.find(cif::Key("id") == 1))
{
r["id"] = 10;
break;
}
BOOST_CHECK(cat1.size() == 4);
BOOST_CHECK(cat2.size() == 13);
BOOST_CHECK(cat1.find(cif::Key("id") == 1).size() == 0);
BOOST_CHECK(cat1.find(cif::Key("id") == 10).size() == 1);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 1).size() == 1);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 10).size() == 2);
for (auto r: cat1.find(cif::Key("id") == 2))
{
r["id"] = 20;
break;
}
BOOST_CHECK(cat1.size() == 4);
BOOST_CHECK(cat2.size() == 13);
BOOST_CHECK(cat1.find(cif::Key("id") == 2).size() == 0);
BOOST_CHECK(cat1.find(cif::Key("id") == 20).size() == 1);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 2).size() == 2);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 20).size() == 2);
for (auto r: cat1.find(cif::Key("id") == 3))
{
r["id"] = 30;
break;
}
BOOST_CHECK(cat1.size() == 4);
BOOST_CHECK(cat2.size() == 13);
BOOST_CHECK(cat1.find(cif::Key("id") == 3).size() == 0);
BOOST_CHECK(cat1.find(cif::Key("id") == 30).size() == 1);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 3).size() == 2);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 30).size() == 1);
for (auto r: cat1.find(cif::Key("id") == 4))
{
r["id"] = 40;
break;
}
BOOST_CHECK(cat1.size() == 4);
BOOST_CHECK(cat2.size() == 13);
BOOST_CHECK(cat1.find(cif::Key("id") == 4).size() == 0);
BOOST_CHECK(cat1.find(cif::Key("id") == 10).size() == 1);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 4).size() == 3);
BOOST_CHECK(cat2.find(cif::Key("parent_id") == 40).size() == 0);
f.write(std::cout, {});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment