1

I'm trying to query results from a multi index container where the value type is a struct of three elements. The first value is given, but the second and third have to be greater or less than a query parameter.

After searching around, I found that a custom Key extractor has to be implemented, and some of the links here suggest the same, but I am unable to implement it:

Can anyone help me get it working?

Below is my struct and multiindex implementation:

#define RANKFILTERVIEW 0

struct TPQ {
    int UID;
    int Value;
    int Rank;
    TPQ():UID(0),Value(0),Rank(0)
    { }
    TPQ(int _T, int _V, int _R):UID(_T),Value(_V),Rank(_R)
    { }
};

typedef bip::allocator<
    TPQ,bip::managed_shared_memory::segment_manager
> shared_struct_allocator;

typedef bmi::multi_index_container<
    TPQ,
    bmi::indexed_by<
        bmi::ordered_unique<bmi::tag<struct Composite>,
            bmi::composite_key<TPQ,
                bmi::member<TPQ, int,&TPQ::UID>,
                bmi::member<TPQ, int,&TPQ::Value>,
                bmi::member<TPQ, int,&TPQ::Rank>
        > >
    >,
    shared_struct_allocator
> Rank_Set;

typedef nth_index<Rank_Set, RANKFILTERVIEW>::type Rank_view;

int main()
{
    bip::managed_shared_memory segment(bip::open_only,"RANKSOTRE");

    int UID =52478;

    std::pair<Rank_Set*, std::size_t> RankOrderRecord=segment.find<Rank_Set>("RANKDATARECORD");

    /// Here I want the result as stated below.
    auto range = RankOrderRecord.first->get<Composite>().equal_range(boost::make_tuple(UID,_2>500,_3>5));

}

I have a set of instruction that sorting or re-arranging shall be ignored.

Dev Null
  • 4,731
  • 1
  • 30
  • 46
  • "I have a set of instruction that sorting or re-arranging shall be ignored." - what do you mean? You mean, you are supposed to actually use the index...? – sehe May 29 '18 at 08:07

1 Answers1

2

You almost got it. Indeed, in order indices you can query by partial key. Since the fields are regular integral types it's pretty easy to come up with a good lower and upper bound:

auto range = boost::make_iterator_range(
    view.lower_bound(boost::make_tuple(UID, 501, 6)),
    view.lower_bound(boost::make_tuple(UID+1)));

Here's a full demo:

Live On Coliru

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/composite_key.hpp>
#include <boost/multi_index/ordered_index.hpp>

#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_mapped_file.hpp> // for Coliru

#include <boost/range/iterator_range.hpp>

#include <iostream>

namespace bmi = boost::multi_index;
namespace bip = boost::interprocess;

struct TPQ {
    int UID   = 0;
    int Value = 0;
    int Rank  = 0;

    TPQ() = default;
    TPQ(int _T, int _V, int _R) : UID(_T), Value(_V), Rank(_R) {}
};

static inline std::ostream& operator<<(std::ostream& os, TPQ const& tpq) {
    return os << "{ UID: " << tpq.UID
              << ", Value: " << tpq.Value
              << ", Rank: " << tpq.Rank << "}";
}

using shared_struct_allocator = bip::allocator<TPQ, bip::managed_mapped_file::segment_manager>;

using Rank_Set = bmi::multi_index_container<
    TPQ,
    bmi::indexed_by<
        bmi::ordered_unique<
            bmi::tag<struct RankFilterView>,
            bmi::composite_key<TPQ,
                bmi::member<TPQ, int, &TPQ::UID>, 
                bmi::member<TPQ, int, &TPQ::Value>,
                bmi::member<TPQ, int, &TPQ::Rank>
            >
        >
    >,
    shared_struct_allocator>;

using Rank_view = bmi::index<Rank_Set, RankFilterView>::type;

int main() {
    bip::managed_mapped_file segment(bip::open_or_create, "RANKSOTRE", 10*1024);
    auto& table = *segment.find_or_construct<Rank_Set>("RANKDATARECORD")(segment.get_segment_manager());

    table.insert({
        {52478, 501, 6}, // Match!
        {52478, 500, 6}, // - Value too small
        {52479, 0,   0}, // - UID too high
        {52478, 502, 6}, // Match!
        {52478, 502, 7}, // Match!
        {52478, 501, 5}, // - Rank too small
        {52477, 502, 7}, // - UID too small
        {52478, 999, 9}, // Match!
    });

    int UID = 52478;
    Rank_view& view = table.get<RankFilterView>(); 

    auto range = boost::make_iterator_range(
        view.lower_bound(boost::make_tuple(UID, 501, 6)),
        view.upper_bound(UID));

    for (TPQ const& tpq : range) {
        std::cout << tpq << "\n";
    }

}

Which will only print, as expected:

{ UID: 52478, Value: 501, Rank: 6}
{ UID: 52478, Value: 502, Rank: 6}
{ UID: 52478, Value: 502, Rank: 7}
{ UID: 52478, Value: 999, Rank: 9}
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Rather than `view.lower_bound(boost::make_tuple(UID+1))` you can use `view.upper_bound(boost::make_tuple(UID))`, which does not depend on the integralness of `UID`. – Joaquín M López Muñoz May 29 '18 at 10:20
  • 1
    Also, you can dispense with `make_tuple` and write `view.upper_bound(UID)` directly. – Joaquín M López Muñoz May 29 '18 at 10:21
  • I'm surprised you don't need to define `RankFilterView` (only declare it). Have you got an explanation for why that is OK? – Caleth May 29 '18 at 10:37
  • @JoaquínMLópezMuñoz mmm. I've gone wrong with the `upper_bound` approach in the past. I'm not absolutely sure that was with Boost MultiIndex, so it's probably one of these deformities that takes a while to "shed". (Updated the answer after testing :)) – sehe May 29 '18 at 11:26
  • @Caleth Yup. It's only a forward declare. As long as no operation requires the type to be complete (such as `sizeof(RankFilterView)`) there's no need to define it. This is a pretty common idiom for tag types – sehe May 29 '18 at 11:26
  • Thanks @sehe for your answer . Let me apply it in my code and get back to you. Lot of thanks – Shailendra kumar May 30 '18 at 06:58
  • @sehe as i was not getting the desired result using this solution hence i changed your coliru link a bit and am sharing it again . The issue here is I want entries with rank greater than zero but it selects entries where the first two element are a match. Here is the [link](http://coliru.stacked-crooked.com/a/f87b5938a61e73d8) – Shailendra kumar May 31 '18 at 06:25
  • You'll have to filter manually. All you can expect from an ordered index is ordered ranges. This is not a SQL database and SQL database would also use several indexes to satisfy such a query – sehe May 31 '18 at 06:45
  • How would you use this equal_range to get Ranks from 5 to 6? I've tried but I'm unsure how to make it work. – mojo1mojo2 Jan 26 '23 at 05:06
  • @mojo1mojo2 That's... a weird question ("How would you use this hammer to tighten these screws?"). `equal_range` says it all! Unless you have an equivalence predicate that makes 5..6 equivalent on the index, the question doesn't fly. However, `equal_range(x)` is just `[lower_bound(x), upper_bound(x))` so you can in principle use those: http://coliru.stacked-crooked.com/a/d0f4914a72e68deb – sehe Jan 26 '23 at 17:04
  • @mojo1mojo2 Also note, since your question seems pretty unrelated to this question, keep in mind that Boost Multi-Index has added the concept of _ranked index_ since recently. Depending on what "rank" actually means in your application, this might be exactly what you want: https://www.boost.org/doc/libs/develop/libs/multi_index/doc/reference/rnk_indices.html – sehe Jan 26 '23 at 17:07
  • @sehe Thank you! I wasn't sure if it made sense so I wanted to see if it was possible. – mojo1mojo2 Feb 01 '23 at 00:25