1
0
Fork 0
mirror of https://github.com/KingDuckZ/dindexer.git synced 2025-07-02 14:04:22 +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 { auto Command::scan() -> scan_range {
return scan_range(scan_iterator(this, false), scan_iterator(this, true)); 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 } //namespace redis

View file

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

View file

@ -24,6 +24,7 @@
#include <type_traits> #include <type_traits>
#include <vector> #include <vector>
#include <cstddef> #include <cstddef>
#include <boost/utility/string_ref.hpp>
namespace redis { namespace redis {
template <typename V, typename ValueFetch> template <typename V, typename ValueFetch>
@ -42,7 +43,7 @@ namespace redis {
bool is_connected ( void ) const; bool is_connected ( void ) const;
RedisReplyType run ( const char* parCommand, long long parScanContext ); 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; } bool is_equal ( const ScanIteratorBaseClass& parOther ) const { return m_command == parOther.m_command; }
@ -52,9 +53,10 @@ namespace redis {
} //namespace implem } //namespace implem
template <typename V, typename ValueFetch> 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; friend class boost::iterator_core_access;
typedef implem::ScanIteratorBaseIterator<V, ValueFetch> base_iterator; typedef implem::ScanIteratorBaseIterator<V, ValueFetch> base_iterator;
define_has_method(scan_target, ScanTarget);
public: public:
typedef typename base_iterator::difference_type difference_type; typedef typename base_iterator::difference_type difference_type;
typedef typename base_iterator::value_type value_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::reference reference;
typedef typename base_iterator::iterator_category iterator_category; 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 ); 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: private:
define_has_method(scan_target, ScanTarget);
template <typename T> template <typename T>
RedisReplyType forward_scan_command ( typename std::enable_if<HasScanTargetMethod<T>::value, int>::type parDummy ); RedisReplyType forward_scan_command ( typename std::enable_if<HasScanTargetMethod<T>::value, int>::type parDummy );
template <typename T> template <typename T>
@ -77,7 +80,7 @@ namespace redis {
bool equal ( const ScanIterator& parOther ) const; bool equal ( const ScanIterator& parOther ) const;
const V& dereference ( void ) const; const V& dereference ( void ) const;
std::vector<RedisReplyType> m_reply; std::vector<value_type> m_reply;
long long m_scan_context; long long m_scan_context;
std::size_t m_curr_index; std::size_t m_curr_index;
}; };
@ -89,6 +92,22 @@ namespace redis {
static const T& make_value ( const RedisReplyType* parItem ); 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 } //namespace redis
#include "scan_iterator.inl" #include "scan_iterator.inl"

View file

@ -24,8 +24,31 @@ namespace redis {
} //namespace implem } //namespace implem
template <typename V, typename ValueFetch> template <typename V, typename ValueFetch>
template <typename Dummy, typename>
ScanIterator<V, ValueFetch>::ScanIterator (Command* parCommand, bool parEnd) : ScanIterator<V, ValueFetch>::ScanIterator (Command* parCommand, bool parEnd) :
implem::ScanIteratorBaseClass(parCommand), 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_reply(),
m_scan_context(0), m_scan_context(0),
m_curr_index(0) m_curr_index(0)
@ -50,10 +73,10 @@ namespace redis {
assert(not is_end()); assert(not is_end());
static_assert(ValueFetch::step > 0, "Can't have an increase step of 0"); static_assert(ValueFetch::step > 0, "Can't have an increase step of 0");
if (m_curr_index + ValueFetch::step < m_reply.size()) { if (m_curr_index + 1 < m_reply.size()) {
m_curr_index += ValueFetch::step; ++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_reply.clear();
m_curr_index = 0; m_curr_index = 0;
} }
@ -70,7 +93,15 @@ namespace redis {
new_context = get_integer_autoconv_if_str(array_reply[0]); new_context = get_integer_autoconv_if_str(array_reply[0]);
} while (new_context and array_reply.empty()); } 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_scan_context = new_context;
m_curr_index = 0; m_curr_index = 0;
} }
@ -95,7 +126,7 @@ namespace redis {
assert(not m_reply.empty()); assert(not m_reply.empty());
assert(m_curr_index < m_reply.size()); 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> template <typename V, typename ValueFetch>
@ -115,4 +146,15 @@ namespace redis {
assert(parItem); assert(parItem);
return get<T>(*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 } //namespace redis