1
0
Fork 0
mirror of https://github.com/KingDuckZ/dindexer.git synced 2025-02-17 11:45:50 +00:00

Implement HSCAN Redis command.

This commit is contained in:
King_DuckZ 2016-06-13 15:14:10 +01:00
parent e0a82cce2d
commit c7eeddabf1
5 changed files with 80 additions and 11 deletions

View file

@ -129,4 +129,8 @@ namespace redis {
auto Command::scan() -> scan_range {
return scan_range(scan_iterator(this, false), scan_iterator(this, true));
}
auto Command::hscan (boost::string_ref parKey) -> hscan_range {
return hscan_range(hscan_iterator(this, parKey, false), hscan_iterator(this, parKey, true));
}
} //namespace redis

View file

@ -30,6 +30,7 @@
#include <vector>
#include <utility>
#include <boost/range/iterator_range_core.hpp>
#include <boost/utility/string_ref.hpp>
struct redisContext;
@ -38,6 +39,8 @@ namespace redis {
public:
typedef ScanIterator<std::string, ScanSingleValues<std::string>> scan_iterator;
typedef boost::iterator_range<scan_iterator> scan_range;
typedef ScanIterator<std::pair<std::string, std::string>, ScanPairs<std::pair<std::string, std::string>>> hscan_iterator;
typedef boost::iterator_range<hscan_iterator> hscan_range;
Command ( std::string&& parAddress, uint16_t parPort );
~Command ( void ) noexcept;
@ -52,6 +55,7 @@ namespace redis {
//Single Redis command wrappers
scan_range scan ( void );
hscan_range hscan ( boost::string_ref parKey );
private:
using RedisConnection = std::unique_ptr<redisContext, void(*)(redisContext*)>;

View file

@ -39,7 +39,7 @@ namespace redis {
return m_command->run(parCommand, boost::lexical_cast<std::string>(parScanContext));
}
RedisReplyType ScanIteratorBaseClass::run (const char* parCommand, const std::string& parParameter, long long parScanContext) {
RedisReplyType ScanIteratorBaseClass::run (const char* parCommand, const boost::string_ref& parParameter, long long parScanContext) {
return m_command->run(parCommand, parParameter, boost::lexical_cast<std::string>(parScanContext));
}
} //namespace implem

View file

@ -24,6 +24,7 @@
#include <type_traits>
#include <vector>
#include <cstddef>
#include <boost/utility/string_ref.hpp>
namespace redis {
template <typename V, typename ValueFetch>
@ -42,7 +43,7 @@ namespace redis {
bool is_connected ( void ) const;
RedisReplyType run ( const char* parCommand, long long parScanContext );
RedisReplyType run ( const char* parCommand, const std::string& parParameter, long long parScanContext );
RedisReplyType run ( const char* parCommand, const boost::string_ref& parParameter, long long parScanContext );
bool is_equal ( const ScanIteratorBaseClass& parOther ) const { return m_command == parOther.m_command; }
@ -52,9 +53,10 @@ namespace redis {
} //namespace implem
template <typename V, typename ValueFetch>
class ScanIterator : private implem::ScanIteratorBaseClass, public implem::ScanIteratorBaseIterator<V, ValueFetch> {
class ScanIterator : private implem::ScanIteratorBaseClass, public implem::ScanIteratorBaseIterator<V, ValueFetch>, private ValueFetch {
friend class boost::iterator_core_access;
typedef implem::ScanIteratorBaseIterator<V, ValueFetch> base_iterator;
define_has_method(scan_target, ScanTarget);
public:
typedef typename base_iterator::difference_type difference_type;
typedef typename base_iterator::value_type value_type;
@ -62,11 +64,12 @@ namespace redis {
typedef typename base_iterator::reference reference;
typedef typename base_iterator::iterator_category iterator_category;
template <typename Dummy=ValueFetch, typename=typename std::enable_if<not HasScanTargetMethod<Dummy>::value>::type>
ScanIterator ( Command* parCommand, bool parEnd );
template <typename Dummy=ValueFetch, typename=typename std::enable_if<HasScanTargetMethod<Dummy>::value>::type>
ScanIterator ( Command* parCommand, boost::string_ref parKey, bool parEnd );
private:
define_has_method(scan_target, ScanTarget);
template <typename T>
RedisReplyType forward_scan_command ( typename std::enable_if<HasScanTargetMethod<T>::value, int>::type parDummy );
template <typename T>
@ -77,7 +80,7 @@ namespace redis {
bool equal ( const ScanIterator& parOther ) const;
const V& dereference ( void ) const;
std::vector<RedisReplyType> m_reply;
std::vector<value_type> m_reply;
long long m_scan_context;
std::size_t m_curr_index;
};
@ -89,6 +92,22 @@ namespace redis {
static const T& make_value ( const RedisReplyType* parItem );
};
template <typename P, typename A=decltype(P().first), typename B=decltype(P().second)>
struct ScanPairs {
typedef P PairType;
explicit ScanPairs ( boost::string_ref parScanTarget ) : m_scan_target(parScanTarget) {}
static constexpr const char* command ( void ) { return "HSCAN"; }
static constexpr const std::size_t step = 2;
static PairType make_value ( const RedisReplyType* parItem );
boost::string_ref scan_target ( void );
private:
boost::string_ref m_scan_target;
};
} //namespace redis
#include "scan_iterator.inl"

View file

@ -24,8 +24,31 @@ namespace redis {
} //namespace implem
template <typename V, typename ValueFetch>
template <typename Dummy, typename>
ScanIterator<V, ValueFetch>::ScanIterator (Command* parCommand, bool parEnd) :
implem::ScanIteratorBaseClass(parCommand),
implem::ScanIteratorBaseIterator<V, ValueFetch>(),
ValueFetch(),
m_reply(),
m_scan_context(0),
m_curr_index(0)
{
if (not parEnd) {
m_curr_index = 1; //Some arbitrary value so is_end()==false
assert(not is_end());
this->increment();
}
else {
assert(is_end());
}
}
template <typename V, typename ValueFetch>
template <typename Dummy, typename>
ScanIterator<V, ValueFetch>::ScanIterator (Command* parCommand, boost::string_ref parKey, bool parEnd) :
implem::ScanIteratorBaseClass(parCommand),
implem::ScanIteratorBaseIterator<V, ValueFetch>(),
ValueFetch(parKey),
m_reply(),
m_scan_context(0),
m_curr_index(0)
@ -50,10 +73,10 @@ namespace redis {
assert(not is_end());
static_assert(ValueFetch::step > 0, "Can't have an increase step of 0");
if (m_curr_index + ValueFetch::step < m_reply.size()) {
m_curr_index += ValueFetch::step;
if (m_curr_index + 1 < m_reply.size()) {
++m_curr_index;
}
else if (m_curr_index + ValueFetch::step == m_reply.size() and not m_scan_context) {
else if (m_curr_index + 1 == m_reply.size() and not m_scan_context) {
m_reply.clear();
m_curr_index = 0;
}
@ -70,7 +93,15 @@ namespace redis {
new_context = get_integer_autoconv_if_str(array_reply[0]);
} while (new_context and array_reply.empty());
m_reply = get_array(array_reply[1]);
const auto variant_array = get_array(array_reply[1]);
assert(variant_array.size() % ValueFetch::step == 0);
const std::size_t expected_reply_count = variant_array.size() / ValueFetch::step;
m_reply.clear();
m_reply.reserve(expected_reply_count);
for (std::size_t z = 0; z < variant_array.size(); z += ValueFetch::step) {
m_reply.push_back(ValueFetch::make_value(variant_array.data() + z));
}
assert(expected_reply_count == m_reply.size());
m_scan_context = new_context;
m_curr_index = 0;
}
@ -95,7 +126,7 @@ namespace redis {
assert(not m_reply.empty());
assert(m_curr_index < m_reply.size());
return ValueFetch::make_value(m_reply.data() + m_curr_index);
return m_reply[m_curr_index];
}
template <typename V, typename ValueFetch>
@ -115,4 +146,15 @@ namespace redis {
assert(parItem);
return get<T>(*parItem);
}
template <typename P, typename A, typename B>
P ScanPairs<P, A, B>::make_value (const RedisReplyType* parItem) {
assert(parItem);
return PairType(get<A>(parItem[0]), get<B>(parItem[1]));
}
template <typename P, typename A, typename B>
boost::string_ref ScanPairs<P, A, B>::scan_target() {
return m_scan_target;
}
} //namespace redis