diff options
author | Uladzimir Makouski <uladzimir.makouski@mongodb.com> | 2021-09-14 08:20:34 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-09-14 08:47:54 +0000 |
commit | 8d97a7a71bde56723e295286a3b9425f220ebc11 (patch) | |
tree | fd2ca0b4ead8ce4b88aabc6a5a646799595f6e8a /src/mongo/db/exec | |
parent | 847d36b96a855c3c8088a3394d50ce902e0e1f85 (diff) | |
download | mongo-8d97a7a71bde56723e295286a3b9425f220ebc11.tar.gz |
Revert "SERVER-59631 Fixed the `compareValue` implementation for the values of `ValueSetType` and `ValueMapType` type in SBE"
This reverts commit 013ea42551ce36cc0b134e0ac52e221a681f0ecd.
Diffstat (limited to 'src/mongo/db/exec')
-rw-r--r-- | src/mongo/db/exec/sbe/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/values/value.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/values/value.h | 228 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/values/value_test.cpp | 266 |
4 files changed, 5 insertions, 500 deletions
diff --git a/src/mongo/db/exec/sbe/SConscript b/src/mongo/db/exec/sbe/SConscript index f7ab8543480..67d77dad963 100644 --- a/src/mongo/db/exec/sbe/SConscript +++ b/src/mongo/db/exec/sbe/SConscript @@ -161,7 +161,6 @@ env.CppUnitTest( 'sbe_test.cpp', 'sbe_unique_test.cpp', 'values/value_serialize_for_sorter_test.cpp', - "values/value_test.cpp", 'values/write_value_to_stream_test.cpp' ], LIBDEPS=[ diff --git a/src/mongo/db/exec/sbe/values/value.cpp b/src/mongo/db/exec/sbe/values/value.cpp index 25bd5813b55..4e6630919f5 100644 --- a/src/mongo/db/exec/sbe/values/value.cpp +++ b/src/mongo/db/exec/sbe/values/value.cpp @@ -1041,7 +1041,7 @@ std::pair<TypeTags, Value> compareValue(TypeTags lhsTag, if (lhsTag == TypeTags::ArraySet && rhsTag == TypeTags::ArraySet) { auto lhsArr = getArraySetView(lhsValue); auto rhsArr = getArraySetView(rhsValue); - if (*lhsArr == *rhsArr) { + if (lhsArr->values() == rhsArr->values()) { return {TypeTags::NumberInt32, bitcastFrom<int32_t>(0)}; } return {TypeTags::Nothing, 0}; @@ -1343,14 +1343,6 @@ std::pair<TypeTags, Value> arrayToSet(TypeTags tag, Value val, CollatorInterface guard.reset(); return {setTag, setVal}; } - -bool operator==(const ArraySet& lhs, const ArraySet& rhs) { - return lhs.values() == rhs.values(); -} - -bool operator!=(const ArraySet& lhs, const ArraySet& rhs) { - return !(lhs == rhs); -} } // namespace value } // namespace sbe } // namespace mongo diff --git a/src/mongo/db/exec/sbe/values/value.h b/src/mongo/db/exec/sbe/values/value.h index b9fb1a56430..c2e2a512ad2 100644 --- a/src/mongo/db/exec/sbe/values/value.h +++ b/src/mongo/db/exec/sbe/values/value.h @@ -415,225 +415,9 @@ private: const CollatorInterface* _collator; }; -/** - * 'DeepEqualityHashSet' is a wrapper around 'absl::flat_hash_set' that provides a "truly" deep - * equality comparison function between its instances. The equality operator in the underlying - * 'absl::flat_hash_set' type doesn't use the provided equality functor. Instead, it relies on the - * default comparison function for the key type, which is not preferrable in our usage scenarios. - * This is the main reason for having the 'DeepEqualityHashSet' wrapper type. - */ -template <class T, - class Hash = absl::container_internal::hash_default_hash<T>, - class Eq = absl::container_internal::hash_default_eq<T>, - class Allocator = std::allocator<T>> -class DeepEqualityHashSet { -public: - using SetType = absl::flat_hash_set<T, Hash, Eq, Allocator>; - using iterator = typename SetType::iterator; - using const_iterator = typename SetType::const_iterator; - - explicit DeepEqualityHashSet(size_t bucket_count, const Hash& hash, const Eq& eq) - : _values(bucket_count, hash, eq) {} - - Hash hash_function() const { - return _values.hash_function(); - } - Eq key_eq() const { - return _values.key_eq(); - } - - size_t size() const { - return _values.size(); - } - - void reserve(size_t n) { - _values.reserve(n); - } - - std::pair<iterator, bool> insert(const T& value) { - return _values.insert(value); - } - - bool contains(const T& key) const { - return _values.contains(key); - } - - iterator find(const T& key) { - return _values.find(key); - } - - const_iterator find(const T& key) const { - return _values.find(key); - } - - size_t count(const T& key) const { - return _values.count(key); - } - - iterator begin() { - return _values.begin(); - } - iterator end() { - return _values.end(); - } - - const_iterator begin() const { - return _values.begin(); - } - const_iterator end() const { - return _values.end(); - } - - template <class T1, class Hash1, class Eq1, class Allocator1> - friend bool operator==(const DeepEqualityHashSet<T1, Hash1, Eq1, Allocator1>& lhs, - const DeepEqualityHashSet<T1, Hash1, Eq1, Allocator1>& rhs) { - using SetTp = typename DeepEqualityHashSet<T1, Hash1, Eq1, Allocator1>::SetType; - const SetTp* inner = &lhs._values; - const SetTp* outer = &rhs._values; - if (outer->size() != inner->size()) { - return false; - } - - if (outer->capacity() > inner->capacity()) { - std::swap(inner, outer); - } - - for (const auto& e : *outer) { - // The equality check in the 'absl::flat_hash_set' type doesn't use the provided - // equality functor. Instead, it relies on the default comparison function for the key - // type, which is not preferrable in our usage scenarios. This is the main reason for - // having the 'DeepEqualityHashSet' wrapper type. - if (!inner->contains(e)) { - return false; - } - } - return true; - } - - template <class T1, class Hash1, class Eq1, class Allocator1> - friend bool operator!=(const DeepEqualityHashSet<T1, Hash1, Eq1, Allocator1>& lhs, - const DeepEqualityHashSet<T1, Hash1, Eq1, Allocator1>& rhs) { - return !(lhs == rhs); - } - -private: - SetType _values; -}; - -/** - * 'DeepEqualityHashMap' is a wrapper around 'absl::flat_hash_map' that provides a "truly" deep - * equality comparison function between its instances. The equality operator in the underlying - * 'absl::flat_hash_map' type doesn't use the provided equality functor. Instead, it relies on the - * default comparison function for the key type, which is not preferrable in our usage scenarios. - * This is the main reason for having the 'DeepEqualityHashMap' wrapper type. - */ -template <class K, - class V, - class Hash = absl::container_internal::hash_default_hash<K>, - class Eq = absl::container_internal::hash_default_eq<K>, - class Allocator = std::allocator<std::pair<const K, V>>> -class DeepEqualityHashMap { -public: - using MapType = absl::flat_hash_map<K, V, ValueHash, ValueEq, Allocator>; - using iterator = typename MapType::iterator; - using const_iterator = typename MapType::const_iterator; - - explicit DeepEqualityHashMap(size_t bucket_count, const Hash& hash, const Eq& eq) - : _values(bucket_count, hash, eq) {} - - Hash hash_function() const { - return _values.hash_function(); - } - Eq key_eq() const { - return _values.key_eq(); - } - - size_t size() const { - return _values.size(); - } - - void reserve(size_t n) { - _values.reserve(n); - } - - std::pair<iterator, bool> insert_or_assign(const K& key, const V& val) { - return _values.insert_or_assign(key, val); - } - - auto& operator[](const K& key) { - return _values[key]; - } - - bool contains(const K& key) const { - return _values.contains(key); - } - - iterator find(const K& key) { - return _values.find(key); - } - - const_iterator find(const K& key) const { - return _values.find(key); - } - - size_t count(const K& key) const { - return _values.count(key); - } - - iterator begin() { - return _values.begin(); - } - iterator end() { - return _values.end(); - } - - const_iterator begin() const { - return _values.begin(); - } - const_iterator end() const { - return _values.end(); - } - - template <class K1, class V1, class Hash1, class Eq1, class Allocator1> - friend bool operator==(const DeepEqualityHashMap<K1, V1, Hash1, Eq1, Allocator1>& lhs, - const DeepEqualityHashMap<K1, V1, Hash1, Eq1, Allocator1>& rhs) { - using MapTp = typename DeepEqualityHashMap<K1, V1, Hash1, Eq1, Allocator1>::MapType; - const MapTp* inner = &lhs._values; - const MapTp* outer = &rhs._values; - if (outer->size() != inner->size()) { - return false; - } - - if (outer->capacity() > inner->capacity()) { - std::swap(inner, outer); - } - - for (const auto& e : *outer) { - // The equality check in the 'absl::flat_hash_map' type doesn't use the provided - // equality functor. Instead, it relies on the default comparison function for the key - // type, which is not preferrable in our usage scenarios. This is the main reason for - // having the 'DeepEqualityHashMap' wrapper type. - auto it = inner->find(e.first); - if (it == inner->end() || it->second != e.second) { - return false; - } - } - return true; - } - - template <class K1, class V1, class Hash1, class Eq1, class Allocator1> - friend bool operator!=(const DeepEqualityHashMap<K1, V1, Hash1, Eq1, Allocator1>& lhs, - const DeepEqualityHashMap<K1, V1, Hash1, Eq1, Allocator1>& rhs) { - return !(lhs == rhs); - } - -private: - MapType _values; -}; - template <typename T> -using ValueMapType = DeepEqualityHashMap<std::pair<TypeTags, Value>, T, ValueHash, ValueEq>; -using ValueSetType = DeepEqualityHashSet<std::pair<TypeTags, Value>, ValueHash, ValueEq>; +using ValueMapType = absl::flat_hash_map<std::pair<TypeTags, Value>, T, ValueHash, ValueEq>; +using ValueSetType = absl::flat_hash_set<std::pair<TypeTags, Value>, ValueHash, ValueEq>; /** * This is the SBE representation of objects/documents. It is a relatively simple structure of @@ -793,7 +577,6 @@ private: class ArraySet { public: using iterator = ValueSetType::iterator; - using const_iterator = ValueSetType::const_iterator; explicit ArraySet(const CollatorInterface* collator = nullptr) : _values(0, ValueHash(collator), ValueEq(collator)) {} @@ -819,7 +602,7 @@ public: void push_back(TypeTags tag, Value val); - auto& values() const noexcept { + auto& values() noexcept { return _values; } @@ -840,9 +623,6 @@ private: ValueSetType _values; }; -bool operator==(const ArraySet& lhs, const ArraySet& rhs); -bool operator!=(const ArraySet& lhs, const ArraySet& rhs); - /** * Implements a wrapper of PCRE regular expression. * Storing the pattern and the options allows for copying of the sbe::value::PcreRegex expression, @@ -1603,7 +1383,7 @@ private: // ArraySet ArraySet* _arraySet{nullptr}; - ArraySet::const_iterator _iter; + ArraySet::iterator _iter; // bsonArray const char* _arrayCurrent{nullptr}; diff --git a/src/mongo/db/exec/sbe/values/value_test.cpp b/src/mongo/db/exec/sbe/values/value_test.cpp deleted file mode 100644 index 7f0b0d7b7eb..00000000000 --- a/src/mongo/db/exec/sbe/values/value_test.cpp +++ /dev/null @@ -1,266 +0,0 @@ -/** - * Copyright (C) 2021-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#include "mongo/db/exec/sbe/values/value.h" -#include "mongo/db/query/collation/collator_interface_mock.h" -#include "mongo/db/query/sbe_stage_builder_test_fixture.h" -#include "mongo/unittest/unittest.h" - -namespace mongo::sbe { - -class SbeValueTest : public SbeStageBuilderTestFixture {}; - -TEST_F(SbeValueTest, CompareTwoArraySets) { - using ValueFnType = void(value::ArraySet*); - using AssertFnType = void(value::TypeTags, value::Value, value::TypeTags, value::Value); - - auto arraySetComparisonTestGenFn = [](std::function<ValueFnType> lhsValueGenFn, - std::function<ValueFnType> rhsValueGenFn, - std::function<AssertFnType> assertFn) { - auto [lhsTag, lhsVal] = value::makeNewArraySet(); - value::ValueGuard lhsGuard{lhsTag, lhsVal}; - auto lhsView = value::getArraySetView(lhsVal); - lhsValueGenFn(lhsView); - - auto [rhsTag, rhsVal] = value::makeNewArraySet(); - value::ValueGuard rhsGuard{rhsTag, rhsVal}; - auto rhsView = value::getArraySetView(rhsVal); - rhsValueGenFn(rhsView); - - assertFn(lhsTag, lhsVal, rhsTag, rhsVal); - }; - - auto arraySetEqualityComparisonTestGenFn = [&](std::function<ValueFnType> lhsValueGenFn, - std::function<ValueFnType> rhsValueGenFn) { - arraySetComparisonTestGenFn(lhsValueGenFn, - rhsValueGenFn, - [&](value::TypeTags lhsTag, - value::Value lhsVal, - value::TypeTags rhsTag, - value::Value rhsVal) { - ASSERT(valueEquals(lhsTag, lhsVal, rhsTag, rhsVal)) - << "lhs array set: " << std::make_pair(lhsTag, lhsVal) - << "rhs array set: " << std::make_pair(rhsTag, rhsVal); - }); - }; - - auto arraySetInequalityComparisonTestGenFn = [&](std::function<ValueFnType> lhsValueGenFn, - std::function<ValueFnType> rhsValueGenFn) { - arraySetComparisonTestGenFn(lhsValueGenFn, - rhsValueGenFn, - [&](value::TypeTags lhsTag, - value::Value lhsVal, - value::TypeTags rhsTag, - value::Value rhsVal) { - ASSERT(!valueEquals(lhsTag, lhsVal, rhsTag, rhsVal)) - << "lhs array set: " << std::make_pair(lhsTag, lhsVal) - << "rhs array set: " << std::make_pair(rhsTag, rhsVal); - }); - }; - - auto addShortStringFn = [](value::ArraySet* set) { - auto [rhsItemTag, rhsItemVal] = value::makeSmallString("abc"_sd); - set->push_back(rhsItemTag, rhsItemVal); - }; - auto addLongStringFn = [](value::ArraySet* set) { - auto [rhsItemTag, rhsItemVal] = value::makeNewString("a long enough string"_sd); - set->push_back(rhsItemTag, rhsItemVal); - }; - auto addArrayFn = [](value::ArraySet* set) { - auto bsonArr = BSON_ARRAY(1 << 2 << 3); - auto [rhsItemTag, rhsItemVal] = value::copyValue( - value::TypeTags::bsonArray, value::bitcastFrom<const char*>(bsonArr.objdata())); - set->push_back(rhsItemTag, rhsItemVal); - }; - auto addObjectFn = [](value::ArraySet* set) { - auto bsonObj = BSON("c" << 1); - auto [rhsItemTag, rhsItemVal] = value::copyValue( - value::TypeTags::bsonObject, value::bitcastFrom<const char*>(bsonObj.objdata())); - set->push_back(rhsItemTag, rhsItemVal); - }; - auto addLongStringMultipleTimesFn = [&](value::ArraySet* set) { - auto initSize = set->size(); - addLongStringFn(set); - addLongStringFn(set); - addLongStringFn(set); - ASSERT(set->size() == initSize + 1) - << "set: " << set << " should be of size " << initSize + 1; - }; - auto addMultipleDecimalFn = [](value::ArraySet* set) { - auto initSize = set->size(); - auto [rhsItemTag1, rhsItemVal1] = value::makeCopyDecimal(Decimal128{"3.14"}); - set->push_back(rhsItemTag1, rhsItemVal1); - auto [rhsItemTag2, rhsItemVal2] = value::makeCopyDecimal(Decimal128{"2.71"}); - set->push_back(rhsItemTag2, rhsItemVal2); - auto [rhsItemTag3, rhsItemVal3] = value::makeCopyDecimal(Decimal128{"3.14"}); - set->push_back(rhsItemTag3, rhsItemVal3); - ASSERT(set->size() == initSize + 2) - << "set: " << set << " should be of size " << initSize + 2; - }; - - // Compare ArraySets with single element of different (and mostly complex) types. - arraySetEqualityComparisonTestGenFn(addShortStringFn, addShortStringFn); - arraySetEqualityComparisonTestGenFn(addLongStringFn, addLongStringFn); - arraySetEqualityComparisonTestGenFn(addArrayFn, addArrayFn); - arraySetEqualityComparisonTestGenFn(addObjectFn, addObjectFn); - arraySetEqualityComparisonTestGenFn(addMultipleDecimalFn, addMultipleDecimalFn); - // Check whether adding a single complex type multiple times doesn't break the equality. - arraySetEqualityComparisonTestGenFn(addLongStringMultipleTimesFn, addLongStringMultipleTimesFn); - // Check whether the insertion into ArraySet is order agnostic. - arraySetEqualityComparisonTestGenFn( - [&](value::ArraySet* set) { - addArrayFn(set); - addMultipleDecimalFn(set); - addObjectFn(set); - addLongStringFn(set); - }, - [&](value::ArraySet* set) { - addObjectFn(set); - addLongStringFn(set); - addArrayFn(set); - addMultipleDecimalFn(set); - }); - - // Check inequal ArraySets are actually not equal. - arraySetInequalityComparisonTestGenFn(addShortStringFn, addLongStringFn); - arraySetInequalityComparisonTestGenFn(addArrayFn, addObjectFn); - arraySetInequalityComparisonTestGenFn(addMultipleDecimalFn, addObjectFn); -} - -TEST_F(SbeValueTest, CompareTwoValueMapTypes) { - using MapType = value::ValueMapType<size_t>; - using ValueFnType = void(MapType*); - using AssertFnType = void(const MapType&, const MapType&); - - auto arraySetComparisonTestGenFn = [](std::function<ValueFnType> lhsValueGenFn, - std::function<ValueFnType> rhsValueGenFn, - std::function<AssertFnType> assertFn) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kToLowerString); - - MapType lhsView{0, value::ValueHash(&collator), value::ValueEq(&collator)}; - lhsValueGenFn(&lhsView); - - MapType rhsView{0, value::ValueHash(&collator), value::ValueEq(&collator)}; - rhsValueGenFn(&rhsView); - - assertFn(lhsView, rhsView); - }; - - auto arraySetEqualityComparisonTestGenFn = [&](std::function<ValueFnType> lhsValueGenFn, - std::function<ValueFnType> rhsValueGenFn) { - arraySetComparisonTestGenFn( - lhsValueGenFn, rhsValueGenFn, [&](const MapType& lhs, const MapType& rhs) { - ASSERT(lhs == rhs); - }); - }; - - auto arraySetInequalityComparisonTestGenFn = [&](std::function<ValueFnType> lhsValueGenFn, - std::function<ValueFnType> rhsValueGenFn) { - arraySetComparisonTestGenFn( - lhsValueGenFn, rhsValueGenFn, [&](const MapType& lhs, const MapType& rhs) { - ASSERT(lhs != rhs); - }); - }; - - auto addShortStringKeyFn = [](MapType* set) { - auto [rhsItemTag, rhsItemVal] = value::makeSmallString("abc"_sd); - (*set)[{rhsItemTag, rhsItemVal}] = 1; - }; - auto addLongStringKeyFn1 = [](MapType* set) { - auto [rhsItemTag, rhsItemVal] = value::makeNewString("a long enough string"_sd); - (*set)[{rhsItemTag, rhsItemVal}] = 2; - }; - auto addLongStringKeyFn2 = [](MapType* set) { - auto [rhsItemTag, rhsItemVal] = value::makeNewString("a long enough string"_sd); - (*set)[{rhsItemTag, rhsItemVal}] = 12; - }; - auto addArrayKeyFn = [](MapType* set) { - auto bsonArr = BSON_ARRAY(1 << 2 << 3); - auto [rhsItemTag, rhsItemVal] = value::copyValue( - value::TypeTags::bsonArray, value::bitcastFrom<const char*>(bsonArr.objdata())); - (*set)[{rhsItemTag, rhsItemVal}] = 3; - }; - auto addObjectKeyFn = [](MapType* set) { - auto bsonObj = BSON("c" << 1); - auto [rhsItemTag, rhsItemVal] = value::copyValue( - value::TypeTags::bsonObject, value::bitcastFrom<const char*>(bsonObj.objdata())); - (*set)[{rhsItemTag, rhsItemVal}] = 4; - }; - auto addLongStringMultipleTimesKeyFn = [&](MapType* set) { - auto initSize = set->size(); - addLongStringKeyFn1(set); - addLongStringKeyFn1(set); - addLongStringKeyFn1(set); - ASSERT(set->size() == initSize + 1) - << "set: " << set << " should be of size " << initSize + 1; - }; - auto addMultipleDecimalKeyFn = [](MapType* set) { - auto initSize = set->size(); - auto [rhsItemTag1, rhsItemVal1] = value::makeCopyDecimal(Decimal128{"3.14"}); - (*set)[{rhsItemTag1, rhsItemVal1}] = 5; - auto [rhsItemTag2, rhsItemVal2] = value::makeCopyDecimal(Decimal128{"2.71"}); - (*set)[{rhsItemTag2, rhsItemVal2}] = 6; - auto [rhsItemTag3, rhsItemVal3] = value::makeCopyDecimal(Decimal128{"3.14"}); - (*set)[{rhsItemTag3, rhsItemVal3}] = 7; - ASSERT(set->size() == initSize + 2) - << "set: " << set << " should be of size " << initSize + 2; - }; - - // Compare MapTypes with single element of different (and mostly complex) types. - arraySetEqualityComparisonTestGenFn(addShortStringKeyFn, addShortStringKeyFn); - arraySetEqualityComparisonTestGenFn(addLongStringKeyFn1, addLongStringKeyFn1); - arraySetEqualityComparisonTestGenFn(addArrayKeyFn, addArrayKeyFn); - arraySetEqualityComparisonTestGenFn(addObjectKeyFn, addObjectKeyFn); - arraySetEqualityComparisonTestGenFn(addMultipleDecimalKeyFn, addMultipleDecimalKeyFn); - // Check whether adding a single complex type multiple times doesn't break the equality. - arraySetEqualityComparisonTestGenFn(addLongStringMultipleTimesKeyFn, - addLongStringMultipleTimesKeyFn); - // Check whether the insertion into MapType is order agnostic. - arraySetEqualityComparisonTestGenFn( - [&](MapType* set) { - addArrayKeyFn(set); - addMultipleDecimalKeyFn(set); - addObjectKeyFn(set); - addLongStringKeyFn1(set); - }, - [&](MapType* set) { - addObjectKeyFn(set); - addLongStringKeyFn1(set); - addArrayKeyFn(set); - addMultipleDecimalKeyFn(set); - }); - - // Check inequal MapTypes are actually not equal. - arraySetInequalityComparisonTestGenFn(addShortStringKeyFn, addLongStringKeyFn1); - arraySetInequalityComparisonTestGenFn(addLongStringKeyFn1, addLongStringKeyFn2); - arraySetInequalityComparisonTestGenFn(addArrayKeyFn, addObjectKeyFn); - arraySetInequalityComparisonTestGenFn(addMultipleDecimalKeyFn, addObjectKeyFn); -} - -} // namespace mongo::sbe |