You can get your cake and eat it by selecting ordered (rather than hashed) indexes in BMI.
There is a nice property of ordered composite indexes that allows you to query partial keys, so you only need to define a composite index to be able to query on the main index:
typedef boost::multi_index_container< Record, indexed_by< ordered_non_unique< tag<CompositeKeyTag>, composite_key<Record, member<RecordKey, MainKey, &RecordKey::mainKey>, member<RecordKey, SecondaryKey, &RecordKey::secondaryKey> > > > > RecordContainer;
Now you can request mainKey :
range = index.equal_range(10);
Or composite:
range = index.equal_range(boost::make_tuple(10,30));
Background:
Here's the full demo of Live On Coliru :
#include <boost/multi_index_container.hpp> #include <boost/multi_index/composite_key.hpp> #include <boost/multi_index/hashed_index.hpp> #include <boost/multi_index/ordered_index.hpp> #include <boost/multi_index/member.hpp> #include <boost/range/iterator_range.hpp> #include <iostream> using MainKey = uint64_t; using SecondaryKey = uint64_t; using Data = std::string; struct RecordKey { MainKey mainKey; SecondaryKey secondaryKey; RecordKey( const MainKey mainKey, SecondaryKey secondaryKey): mainKey( mainKey), secondaryKey( secondaryKey) {} }; struct Record: public RecordKey { Data data; Record( const MainKey mainKey = 0, const SecondaryKey secondaryKey = 0, const Data& data = 0): RecordKey( mainKey, secondaryKey), data( data) {} friend std::ostream& operator<<(std::ostream& os, Record const& r) { return os << " Record[" << r.mainKey << ", " << r.secondaryKey << ", " << r.data << "]"; } }; struct MainKeyTag {}; struct SecondaryKeyTag {}; struct CompositeKeyTag {}; using boost::multi_index_container; using namespace boost::multi_index; typedef boost::multi_index_container< Record, indexed_by< ordered_non_unique< tag<CompositeKeyTag>, composite_key<Record, member<RecordKey, MainKey, &RecordKey::mainKey>, member<RecordKey, SecondaryKey, &RecordKey::secondaryKey> > > > > RecordContainer; int main() { RecordContainer records; records.insert(Record(10, 20, "12")); records.insert(Record(10, 30, "13")); records.insert(Record(10, 30, "13 - need not be unique!")); records.insert(Record(30, 40, "34")); records.insert(Record(30, 50, "35")); records.insert(Record(50, 60, "56")); records.insert(Record(50, 70, "57")); records.insert(Record(70, 80, "78")); std::cout << "\nAll records:\n----------------------------------------------------------------------\n"; for (auto const& r : records) std::cout << r << "\n"; { std::cout << "\nAll records with (main) == (10):\n----------------------------------------------------------------------\n"; auto& index = records.get<0>(); auto range = index.equal_range(10); for (auto const& r : boost::make_iterator_range(range.first, range.second)) std::cout << r << "\n"; } { std::cout << "\nAll records with (main,secondary) == (10,30):\n----------------------------------------------------------------------\n"; auto& index = records.get<0>(); auto range = index.equal_range(boost::make_tuple(10,30)); for (auto const& r : boost::make_iterator_range(range.first, range.second)) std::cout << r << "\n"; } }
Conclusion:
All records: ---------------------------------------------------------------------- Record[10, 20, 12] Record[10, 30, 13] Record[10, 30, 13 - need not be unique!] Record[30, 40, 34] Record[30, 50, 35] Record[50, 60, 56] Record[50, 70, 57] Record[70, 80, 78] All records with (main) == (10): ---------------------------------------------------------------------- Record[10, 20, 12] Record[10, 30, 13] Record[10, 30, 13 - need not be unique!] All records with (main,secondary) == (10,30): ---------------------------------------------------------------------- Record[10, 30, 13] Record[10, 30, 13 - need not be unique!]