From 63ff5b0d90894a2656252cc292e6b45f9f2fd00c Mon Sep 17 00:00:00 2001 From: Igor Malinovskiy Date: Fri, 16 Jul 2021 16:22:41 +0300 Subject: [PATCH] Add support for regex in NS separators Fix #3669 --- src/app/models/connectionconf.cpp | 10 +++++ src/app/models/connectionconf.h | 4 ++ src/app/models/treeoperations.cpp | 5 ++- src/app/models/treeoperations.h | 2 +- .../connections-tree/items/keyitem.cpp | 14 +++++-- .../connections-tree/items/namespaceitem.cpp | 14 ++++--- .../connections-tree/items/namespaceitem.h | 3 +- .../connections-tree/keysrendering.cpp | 39 ++++++++++++++----- src/modules/connections-tree/keysrendering.h | 2 +- src/modules/connections-tree/operations.h | 2 +- src/qml/ConnectionSettignsDialog.qml | 26 +++++++++---- 11 files changed, 88 insertions(+), 33 deletions(-) diff --git a/src/app/models/connectionconf.cpp b/src/app/models/connectionconf.cpp index 1fd7507f8..c2bef3aa0 100644 --- a/src/app/models/connectionconf.cpp +++ b/src/app/models/connectionconf.cpp @@ -39,6 +39,16 @@ void ServerConfig::setNamespaceSeparator(QString ns) return setParam("namespace_separator", ns); } +bool ServerConfig::namespaceSeparatorIsRegex() const +{ + return param("namespace_separator_is_regex", false); +} + +void ServerConfig::setNamespaceSeparatorIsRegex(bool v) +{ + return setParam("namespace_separator_is_regex", v); +} + uint ServerConfig::databaseScanLimit() const { return param("db_scan_limit", DEFAULT_DB_SCAN_LIMIT); diff --git a/src/app/models/connectionconf.h b/src/app/models/connectionconf.h index 2fb43dff8..059450d68 100644 --- a/src/app/models/connectionconf.h +++ b/src/app/models/connectionconf.h @@ -32,6 +32,7 @@ class ServerConfig : public RedisClient::ConnectionConfig /* Advanced settings */ Q_PROPERTY(QString keysPattern READ keysPattern WRITE setKeysPattern) Q_PROPERTY(QString namespaceSeparator READ namespaceSeparator WRITE setNamespaceSeparator) + Q_PROPERTY(bool namespaceSeparatorIsRegex READ namespaceSeparatorIsRegex WRITE setNamespaceSeparatorIsRegex) Q_PROPERTY(uint executeTimeout READ executeTimeout WRITE setExecutionTimeout) Q_PROPERTY(uint connectionTimeout READ connectionTimeout WRITE setConnectionTimeout) Q_PROPERTY(bool overrideClusterHost READ overrideClusterHost WRITE setClusterHostOverride) @@ -59,6 +60,9 @@ class ServerConfig : public RedisClient::ConnectionConfig QString namespaceSeparator() const; void setNamespaceSeparator(QString); + bool namespaceSeparatorIsRegex() const; + void setNamespaceSeparatorIsRegex(bool v); + bool luaKeysLoading() const; void setLuaKeysLoading(bool); diff --git a/src/app/models/treeoperations.cpp b/src/app/models/treeoperations.cpp index 51771e71e..6d089a35e 100644 --- a/src/app/models/treeoperations.cpp +++ b/src/app/models/treeoperations.cpp @@ -252,8 +252,9 @@ void TreeOperations::resetConnection() { QtConcurrent::run([oldConnection]() { oldConnection->disconnect(); }); } -QString TreeOperations::getNamespaceSeparator() { - return m_config.namespaceSeparator(); +QRegExp TreeOperations::getNamespaceSeparator() { + return QRegExp(m_config.namespaceSeparator(), Qt::CaseSensitive, + m_config.namespaceSeparatorIsRegex()? QRegExp::RegExp : QRegExp::FixedString); } QString TreeOperations::defaultFilter() { return m_config.keysPattern(); } diff --git a/src/app/models/treeoperations.h b/src/app/models/treeoperations.h index b10ce1c68..c5ff7b945 100644 --- a/src/app/models/treeoperations.h +++ b/src/app/models/treeoperations.h @@ -35,7 +35,7 @@ class TreeOperations : public QObject, void resetConnection() override; - QString getNamespaceSeparator() override; + QRegExp getNamespaceSeparator() override; QString defaultFilter() override; diff --git a/src/modules/connections-tree/items/keyitem.cpp b/src/modules/connections-tree/items/keyitem.cpp index 7cc2a0b48..29d4d3c41 100644 --- a/src/modules/connections-tree/items/keyitem.cpp +++ b/src/modules/connections-tree/items/keyitem.cpp @@ -40,9 +40,17 @@ QString KeyItem::getDisplayName() const { m_shortRendering) { auto parent = parentTreeItemToNs(m_parent); - title = printableString(getFullPath().mid( - parent->getFullPath().size() + - parent->operations()->getNamespaceSeparator().size())); + auto nsRx = parent->operations()->getNamespaceSeparator(); + int searchFrom = parent->getFullPath().size() > 0 ? parent->getFullPath().size() - 1 : 0; + int nsRxPos = QString::fromUtf8(getFullPath()).indexOf(nsRx, searchFrom); + + int nsSize = 0; + + if (nsRxPos >= 0) { + nsSize = nsRx.matchedLength(); + } + + title = printableString(getFullPath().mid(parent->getFullPath().size() + nsSize)); } else { title = printableString(getFullPath(), true); } diff --git a/src/modules/connections-tree/items/namespaceitem.cpp b/src/modules/connections-tree/items/namespaceitem.cpp index 31cdbaa06..40d5f7d5e 100644 --- a/src/modules/connections-tree/items/namespaceitem.cpp +++ b/src/modules/connections-tree/items/namespaceitem.cpp @@ -14,9 +14,10 @@ using namespace ConnectionsTree; NamespaceItem::NamespaceItem(const QByteArray &fullPath, QSharedPointer operations, QWeakPointer parent, Model &model, - uint dbIndex, QRegExp filter) + uint dbIndex, QRegExp filter, QString lastNsSeparator) : AbstractNamespaceItem(model, parent, operations, dbIndex, filter), m_fullPath(fullPath), + m_lastNsSeparator(lastNsSeparator), m_removed(false) {} QString NamespaceItem::getDisplayName() const { @@ -32,10 +33,11 @@ QString NamespaceItem::getDisplayName() const { } QByteArray NamespaceItem::getName() const { - qsizetype pos = m_fullPath.lastIndexOf(m_operations->getNamespaceSeparator()); + auto rx = m_operations->getNamespaceSeparator(); + qsizetype pos = QString::fromUtf8(m_fullPath).lastIndexOf(rx); if (pos >= 0) { - return m_fullPath.mid(pos + m_operations->getNamespaceSeparator().size()); + return m_fullPath.mid(pos + rx.matchedLength()); } else { return m_fullPath; } @@ -78,7 +80,7 @@ void NamespaceItem::load() { QString nsFilter = QString("%1%2*") .arg(QString::fromUtf8(m_fullPath)) - .arg(m_operations->getNamespaceSeparator()); + .arg(m_lastNsSeparator); if (!m_filter.isEmpty()) { if (m_filter.pattern().startsWith(nsFilter.chopped(1))) { @@ -86,7 +88,7 @@ void NamespaceItem::load() { } else { nsFilter = QString("%1%2%3") .arg(QString::fromUtf8(m_fullPath)) - .arg(m_operations->getNamespaceSeparator()) + .arg(m_lastNsSeparator) .arg(m_filter.pattern()); } } @@ -142,7 +144,7 @@ QHash> NamespaceItem::eventHandlers() { }, QString("%1%2") .arg(QString::fromUtf8(getFullPath())) - .arg(m_operations->getNamespaceSeparator())); + .arg(m_lastNsSeparator)); }); events.insert("reload", [this]() { reload(); }); diff --git a/src/modules/connections-tree/items/namespaceitem.h b/src/modules/connections-tree/items/namespaceitem.h index ed2cf3da1..48faf0011 100644 --- a/src/modules/connections-tree/items/namespaceitem.h +++ b/src/modules/connections-tree/items/namespaceitem.h @@ -15,7 +15,7 @@ class NamespaceItem : public AbstractNamespaceItem { NamespaceItem(const QByteArray& fullPath, QSharedPointer operations, QWeakPointer parent, Model& model, uint dbIndex, - QRegExp filter); + QRegExp filter, QString lastNsSeparator); QString getDisplayName() const override; @@ -38,6 +38,7 @@ class NamespaceItem : public AbstractNamespaceItem { private: QByteArray m_fullPath; + QString m_lastNsSeparator; bool m_removed; }; } // namespace ConnectionsTree diff --git a/src/modules/connections-tree/keysrendering.cpp b/src/modules/connections-tree/keysrendering.cpp index 328eb9cef..9e02a3010 100644 --- a/src/modules/connections-tree/keysrendering.cpp +++ b/src/modules/connections-tree/keysrendering.cpp @@ -25,8 +25,20 @@ void KeysTreeRenderer::renderKeys(QSharedPointer operations, int unprocessedPartStart = 0; if (parent->getFullPath().size() > 0 || parent->type() == "namespace") { - unprocessedPartStart = - parent->getFullPath().size() + settings.nsSeparator.length(); + int nsLength = 0; + + if (keys.size() > 0) { + QString firstKey = QString::fromUtf8(keys[0]); + int res = firstKey.indexOf(settings.nsSeparator, parent->getFullPath().size()); + + qDebug() << "NSs regex pos:" << res; + + nsLength = settings.nsSeparator.matchedLength(); + } + + unprocessedPartStart = + parent->getFullPath().size() + nsLength; + } QByteArray rawKey; @@ -70,10 +82,17 @@ void KeysTreeRenderer::renderLazily( QWeakPointer currentParent = parent.staticCast().toWeakRef(); - int indexOfNaspaceSeparator = - (settings.nsSeparator.isEmpty()) - ? -1 - : notProcessedKeyPart.indexOf(settings.nsSeparator); + int indexOfNaspaceSeparator = -1; + auto nsSeparator = settings.nsSeparator; + int nsSeparatorLength = nsSeparator.pattern().size(); + + if (!nsSeparator.isEmpty() && nsSeparator.patternSyntax() == QRegExp::RegExp) { + QString keyPart = QString::fromUtf8(notProcessedKeyPart); + indexOfNaspaceSeparator = keyPart.indexOf(nsSeparator); + + qDebug() << "NSs regex pos:" << indexOfNaspaceSeparator << nsSeparator.cap(); + nsSeparatorLength = nsSeparator.matchedLength(); + } if (indexOfNaspaceSeparator == -1) { QSharedPointer newKey( @@ -94,7 +113,8 @@ void KeysTreeRenderer::renderLazily( QByteArray namespaceFullPath = fullKey.mid(0, nsPos); // Single namespaced key - if (nextKey.isEmpty() || nextKey.indexOf(namespaceFullPath) == -1) { + if (nsSeparator.patternSyntax() != QRegExp::RegExp + && (nextKey.isEmpty() || nextKey.indexOf(namespaceFullPath) == -1)) { QSharedPointer newKey( new KeyItem(fullKey, currentParent, parent->model())); parent->append(newKey); @@ -103,7 +123,7 @@ void KeysTreeRenderer::renderLazily( namespaceItem = QSharedPointer( new NamespaceItem(namespaceFullPath, m_operations, currentParent, - parent->model(), settings.dbIndex, settings.filter)); + parent->model(), settings.dbIndex, settings.filter, nsSeparator.cap())); if (expandedNamespaces.contains(namespaceFullPath)) { namespaceItem->setExpanded(true); @@ -113,7 +133,6 @@ void KeysTreeRenderer::renderLazily( } renderLazily(namespaceItem, - notProcessedKeyPart.mid(indexOfNaspaceSeparator + - settings.nsSeparator.length()), + notProcessedKeyPart.mid(indexOfNaspaceSeparator + nsSeparatorLength), fullKey, m_operations, settings, expandedNamespaces, level + 1, nextKey); } diff --git a/src/modules/connections-tree/keysrendering.h b/src/modules/connections-tree/keysrendering.h index 7309c847d..8fce4295b 100644 --- a/src/modules/connections-tree/keysrendering.h +++ b/src/modules/connections-tree/keysrendering.h @@ -16,7 +16,7 @@ namespace ConnectionsTree { public: struct RenderingSettigns { QRegExp filter; - QString nsSeparator; + QRegExp nsSeparator; uint dbIndex; bool sortKeys; }; diff --git a/src/modules/connections-tree/operations.h b/src/modules/connections-tree/operations.h index 7e17edd88..4bbe041b2 100644 --- a/src/modules/connections-tree/operations.h +++ b/src/modules/connections-tree/operations.h @@ -57,7 +57,7 @@ class Operations { * @brief getNamespaceSeparator * @return */ - virtual QString getNamespaceSeparator() = 0; + virtual QRegExp getNamespaceSeparator() = 0; virtual QString defaultFilter() = 0; diff --git a/src/qml/ConnectionSettignsDialog.qml b/src/qml/ConnectionSettignsDialog.qml index b400d791a..a9825f514 100644 --- a/src/qml/ConnectionSettignsDialog.qml +++ b/src/qml/ConnectionSettignsDialog.qml @@ -761,14 +761,24 @@ Dialog { BetterLabel { text: qsTranslate("RDM","Namespace Separator:") } - BetterTextField - { - id: namespaceSeparator - Layout.fillWidth: true - objectName: "rdm_advanced_settings_namespace_separator_field" - placeholderText: qsTranslate("RDM","Separator used for namespace extraction from keys") - text: root.settings ? root.settings.namespaceSeparator : "" - onTextChanged: if (root.settings) { root.settings.namespaceSeparator = text } + RowLayout { + BetterTextField + { + id: namespaceSeparator + Layout.fillWidth: true + objectName: "rdm_advanced_settings_namespace_separator_field" + placeholderText: qsTranslate("RDM","Separator used for namespace extraction from keys") + text: root.settings ? root.settings.namespaceSeparator : "" + onTextChanged: if (root.settings) { root.settings.namespaceSeparator = text } + } + + BetterCheckbox { + id: nsRegex + Layout.fillWidth: true + checked: root.settings ? root.settings.namespaceSeparatorIsRegex : false + onCheckedChanged: if (root.settings) { root.settings.namespaceSeparatorIsRegex = checked } + text: qsTranslate("RDM","Regex") + } } SettingsGroupTitle {