Запрос нескольких индексов в Boost Multi-Index

Я пишу программу, которая сохраняет GenericOrder (содержащий количество, цену, путь и отметку времени) как shared_ptr.
Я прочитал документацию Boost и мне удалось определить MultiIndexOrderContainer с использованием трех индексов: путь, отметка времени и цена.
Но я не нахожу способа перебирать определенные ордера с использованием нескольких индексов одновременно.

#include <memory>

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

using namespace ::boost;
using namespace ::boost::multi_index;

enum class Way
{
    UNDEFINED,
    BUY,
    SELL
};

template <typename QuantityType, typename PriceType>
struct GenericOrder
{
    explicit GenericOrder(const Way way, const QuantityType& quantity, const PriceType& price, const long long& timestamp)
        : way_(way), quantity_(quantity), price_(price), timestamp_(timestamp)
    {
    }

    ~GenericOrder() = default;
    GenericOrder(const GenericOrder&) = delete;
    GenericOrder& operator=(const GenericOrder&) = delete;

    Way way_;
    QuantityType quantity_;
    PriceType price_;
    long long timestamp_ = -1;
};

// Aliases
using QuantityType = int;
using PriceType = int;
using OrderType = GenericOrder<QuantityType, PriceType>;
using PointerType = std::shared_ptr<OrderType>;

struct way {};
struct timestamp {};
struct price {};

using MultiIndexOrderContainer = multi_index_container<PointerType,
    indexed_by<
    ordered_non_unique<tag<way>, member<OrderType, decltype(OrderType::way_), &OrderType::way_ >>,
    ordered_non_unique<tag<timestamp>, member<OrderType, decltype(OrderType::timestamp_), &OrderType::timestamp_ >>,
    ordered_non_unique<tag<price>, member<OrderType, decltype(OrderType::price_), &OrderType::price_>>
    >
>;

int main()
{
    MultiIndexOrderContainer c;

    // Inserting some orders
    c.insert(std::make_shared<OrderType>(Way::BUY, 10, 15, 0));
    c.insert(std::make_shared<OrderType>(Way::BUY, 10, 14, 1));
    c.insert(std::make_shared<OrderType>(Way::BUY, 10, 13, 2));
    c.insert(std::make_shared<OrderType>(Way::SELL, 10, 16, 3));
    c.insert(std::make_shared<OrderType>(Way::SELL, 10, 17, 4));
    c.insert(std::make_shared<OrderType>(Way::SELL, 10, 18, 5));

    return 0;
}

Я хотел бы повторить:

  1. Заказы на покупку с определенной ценой, отсортированные по отметке времени
  2. Самая дешевая цена ордера из ордеров на продажу
  3. Самая дорогая цена заказа из заказов на покупку

Как я могу этого добиться?


person Richard Dally    schedule 01.08.2015    source источник
comment
Вы можете использовать отсортированный индекс контейнера multi_index с boost: :filter_iterator.   -  person Jarod42    schedule 02.08.2015
comment
@ Jarod42, это может показаться хорошей идеей, но временная сложность линейна. Я использую мультииндексы для повышения производительности.   -  person Richard Dally    schedule 06.08.2015


Ответы (1)


Boost.MultiIndex не предоставляет какого-либо специального механизма для смешивания ордеров, вызванных разными индексами: с предлагаемой вами структурой вы в основном выполняете запрос первого параметра, а затем выполняете линейное сканирование возвращаемого диапазона.

С другой стороны, если ваши запросы всегда имеют форму (attr1, attr2, attr3), вы можете ускорить их, используя составные ключи. В вашем конкретном случае вы можете иметь три запроса, которые вам нужны, с составным ключом (way_,price_,timestamp_)

Жить на Coliru

#include <memory>
#include <iostream>

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

using namespace ::boost;
using namespace ::boost::multi_index;

enum class Way
{
    UNDEFINED,
    BUY,
    SELL
};

template <typename QuantityType, typename PriceType>
struct GenericOrder
{
    explicit GenericOrder(const Way way, const QuantityType& quantity, const PriceType& price, const long long& timestamp)
        : way_(way), quantity_(quantity), price_(price), timestamp_(timestamp)
    {
    }

    ~GenericOrder() = default;
    GenericOrder(const GenericOrder&) = delete;
    GenericOrder& operator=(const GenericOrder&) = delete;

    Way way_;
    QuantityType quantity_;
    PriceType price_;
    long long timestamp_ = -1;
};

template <typename QuantityType, typename PriceType>
std::ostream& operator<<(std::ostream& os,const GenericOrder<QuantityType,PriceType>& o)
{
    switch(o.way_){
        case Way::UNDEFINED: os<<"UNDEFINED, ";break;
        case Way::BUY: os<<"BUY, ";break;
        case Way::SELL: os<<"SELL, ";break;
    }
    return os<<o.price_<<", "<<o.timestamp_<<"\n";
}

// Aliases
using QuantityType = int;
using PriceType = int;
using OrderType = GenericOrder<QuantityType, PriceType>;
using PointerType = std::shared_ptr<OrderType>;

struct way {};
struct timestamp {};
struct price {};

using MultiIndexOrderContainer = multi_index_container<PointerType,
    indexed_by<
        ordered_non_unique<
            composite_key<
                OrderType,
                member<OrderType, decltype(OrderType::way_), &OrderType::way_ >,
                member<OrderType, decltype(OrderType::price_), &OrderType::price_>,
                member<OrderType, decltype(OrderType::timestamp_), &OrderType::timestamp_ >
            >
        >
    >
>;

int main()
{
    MultiIndexOrderContainer c;

    // Inserting some orders
    c.insert(std::make_shared<OrderType>(Way::BUY, 10, 15, 0));
    c.insert(std::make_shared<OrderType>(Way::BUY, 10, 14, 1));
    c.insert(std::make_shared<OrderType>(Way::BUY, 10, 13, 2));
    c.insert(std::make_shared<OrderType>(Way::BUY, 10, 15, 1));
    c.insert(std::make_shared<OrderType>(Way::SELL, 10, 16, 3));
    c.insert(std::make_shared<OrderType>(Way::SELL, 10, 17, 4));
    c.insert(std::make_shared<OrderType>(Way::SELL, 10, 18, 5));

    std::cout<<"Buying orders for 15, sorted by timestamp\n";
    auto p=c.equal_range(std::make_tuple(Way::BUY,15));
    while(p.first!=p.second)std::cout<<**p.first++;

    std::cout<<"Cheapest selling order\n";
    std::cout<<**c.lower_bound(Way::SELL);

    std::cout<<"Costliest buying order\n";
    std::cout<<**--c.upper_bound(Way::BUY);

return 0;
}
person Joaquín M López Muñoz    schedule 02.08.2015
comment
Я создаю оболочки для использования этих функций и изучаю хешированные индексы поверх композитного ключа. Как вы думаете, это может быть совместимо? - person Richard Dally; 06.08.2015
comment
При использовании составных ключей с хэшированными индексами у вас есть ограничение, а именно то, что частичные запросы не поддерживаются. Например, если ваш составной ключ включен (attr1, attr2, attr3), вы не можете запросить (attr1, attr2) (с упорядоченными индексами вы можете), только для всего кортежа. - person Joaquín M López Muñoz; 07.08.2015
comment
Итак, чтобы воспользоваться составным ключом и хешированными индексами, единственный способ — дублировать индексы (один составной, один хешированный)? - person Richard Dally; 07.08.2015
comment
Все зависит от ваших шаблонов запросов. Если вы ищете, скажем, только (attr1,attr2) и (attr1,attr2,attr3), два хешированных индекса, один с композитным_ключом(атр1,атр2), а другой с композитным_ключом(атрибут1,атр2), могут сделать. Конечно, когда паттернов слишком много, добавлять все больше и больше конкретных индексов нецелесообразно... Как всегда, измеряйте и принимайте соответствующие решения. - person Joaquín M López Muñoz; 07.08.2015