Navigation Menu

Skip to content

Commit

Permalink
[FEATURE] hstore_to_map() / map_to_hstore() expression functions
Browse files Browse the repository at this point in the history
  • Loading branch information
nirvn committed Sep 14, 2018
1 parent 68162a5 commit b7a0c8c
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 0 deletions.
9 changes: 9 additions & 0 deletions resources/function_help/json/hstore_to_map
@@ -0,0 +1,9 @@
{
"name": "hstore_to_map",
"type": "function",
"description": "Creates a map from a hstore-formatted string.",
"arguments": [
{"arg":"string", "description":"the input string"}],
"examples": [ { "expression":"hstore_to_map('qgis=>rocks')", "returns":"{ \"qgis\" : \"rocks\" }"}
]
}
9 changes: 9 additions & 0 deletions resources/function_help/json/map_to_hstore
@@ -0,0 +1,9 @@
{
"name": "map_to_hstore",
"type": "function",
"description": "Merge map elements into a hstore-formatted string.",
"arguments": [
{"arg":"map", "description":"the input map"}],
"examples": [ { "expression":"map_to_hstore(map('qgis','rocks'))", "returns":"\"qgis\"=>\"rocks\"}"}
]
}
91 changes: 91 additions & 0 deletions src/core/expression/qgsexpressionfunction.cpp
Expand Up @@ -4367,6 +4367,95 @@ static QVariant fcnMapToJson( const QVariantList &values, const QgsExpressionCon
return document.toJson( QJsonDocument::Compact );
}

static QVariant fcnHstoreToMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
if ( str.isEmpty() )
return QVariantMap();
str = str.trimmed();

QVariantMap map;
QList<QString> bits;
QList<QString> seps;
seps << "=>" << ",";
int i = 0;
while ( i < str.length() )
{
while ( i < str.length() && str.at( i ).isSpace() )
++i;
QString current = str.mid( i );
QString sep = seps.at( bits.length() );
if ( current.startsWith( '"' ) )
{
QRegularExpression re( "^\"((?:\\\\.|[^\"\\\\])*)\".*" );
QRegularExpressionMatch match = re.match( current );
bits << QString();
if ( match.hasMatch() )
{
bits[bits.length() - 1] = match.captured( 1 ).replace( QLatin1String( "\\\"" ), QLatin1String( "\"" ) ).replace( QLatin1String( "\\\\" ), QLatin1String( "\\" ) );
i += match.captured( 1 ).length() + 2;
while ( i < str.length() && str.at( i ).isSpace() )
++i;

if ( str.midRef( i ).startsWith( sep ) )
{
i += sep.length();
}
else if ( i < str.length() )
{
// hstore string format broken, end construction
i += current.length();
}
}
else
{
// hstore string format broken, end construction
i += current.length();
bits[bits.length() - 1] = current.trimmed();
}
}
else
{
int sepPos = current.indexOf( sep );
if ( sepPos < 0 )
{
i += current.length();
bits << current.trimmed();
}
else
{
i += sepPos + sep.length();
bits << current.left( sepPos ).trimmed();
}
}

if ( bits.length() == 2 )
{
if ( !bits.at( 0 ).isEmpty() && !bits.at( 1 ).isEmpty() )
map[ bits.at( 0 ) ] = bits.at( 1 );
bits.clear();
}
}

return map;
}

static QVariant fcnMapToHstore( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
QStringList list;

for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
{
QString key = it.key();
QString value = it.value().toString();
list << QString( "\"%1\"=>\"%2\"" ).arg( key.replace( "\\", "\\\\" ).replace( "\"", "\\\"" ),
value.replace( "\\", "\\\\" ).replace( "\"", "\\\"" ) );
}

return list.join( ',' );
}

static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QVariantMap result;
Expand Down Expand Up @@ -5034,6 +5123,8 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
//functions for maps
<< new QgsStaticExpressionFunction( QStringLiteral( "json_to_map" ), 1, fcnJsonToMap, QStringLiteral( "Maps" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "map_to_json" ), 1, fcnMapToJson, QStringLiteral( "Maps" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "hstore_to_map" ), 1, fcnHstoreToMap, QStringLiteral( "Maps" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "map_to_hstore" ), 1, fcnMapToHstore, QStringLiteral( "Maps" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "map" ), -1, fcnMap, QStringLiteral( "Maps" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "map_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapGet, QStringLiteral( "Maps" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "map_exist" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapExist, QStringLiteral( "Maps" ) )
Expand Down
9 changes: 9 additions & 0 deletions tests/src/core/testqgsexpression.cpp
Expand Up @@ -2888,6 +2888,15 @@ class TestQgsExpression: public QObject
QCOMPARE( QgsExpression( "json_to_map('{\"1\":\"one\",\"2\":\"two\",\"3\":\"three\"}')" ).evaluate( &context ), QVariant( concatExpected ) );
QCOMPARE( QgsExpression( "map_to_json(map('1','one','2','two','3','three'))" ).evaluate( &context ), QVariant( "{\"1\":\"one\",\"2\":\"two\",\"3\":\"three\"}" ) );

QCOMPARE( QgsExpression( "hstore_to_map('1=>one,2=>two,3=>three')" ).evaluate( &context ), QVariant( concatExpected ) );
QCOMPARE( QgsExpression( "map_to_hstore(map('1','one','2','two','3','three'))" ).evaluate( &context ), QVariant( "\"1\"=>\"one\",\"2\"=>\"two\",\"3\"=>\"three\"" ) );

QVariantMap hstoreExpected;
hstoreExpected[QStringLiteral( "test_quotes" )] = "test \"quote\" symbol";
hstoreExpected[QStringLiteral( "test_slashes" )] = "test \\slash symbol";
hstoreExpected[QStringLiteral( "test_mix" )] = "key with value in quotation marks";
QCOMPARE( QgsExpression( "hstore_to_map('\"test_quotes\"=>\"test \\\\\"quote\\\\\" symbol\",\"test_slashes\"=>\"test \\\\slash symbol\",test_mix=>\"key with value in quotation marks\"')" ).evaluate( &context ), QVariant( hstoreExpected ) );

QStringList keysExpected;
keysExpected << QStringLiteral( "1" ) << QStringLiteral( "2" );
QCOMPARE( QgsExpression( "map_akeys(\"map\")" ).evaluate( &context ), QVariant( keysExpected ) );
Expand Down

0 comments on commit b7a0c8c

Please sign in to comment.