mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
More square brackets
This commit is contained in:
parent
3490217209
commit
06d5f924f6
@ -60,7 +60,8 @@ Abstract base class for all nodes that can appear in an expression.
|
||||
ntFunction,
|
||||
ntLiteral,
|
||||
ntColumnRef,
|
||||
ntCondition
|
||||
ntCondition,
|
||||
ntIndexOperator,
|
||||
};
|
||||
|
||||
|
||||
|
@ -179,6 +179,63 @@ Returns a the name of this operator without the operands.
|
||||
I.e. "AND", "OR", ...
|
||||
%End
|
||||
|
||||
};
|
||||
|
||||
class QgsExpressionNodeIndexOperator : QgsExpressionNode
|
||||
{
|
||||
%Docstring
|
||||
A indexing expression operator, which allows use of square brackets [] to reference map and array items.
|
||||
|
||||
.. versionadded:: 3.6
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgsexpressionnodeimpl.h"
|
||||
%End
|
||||
public:
|
||||
|
||||
QgsExpressionNodeIndexOperator( QgsExpressionNode *container /Transfer/, QgsExpressionNode *index /Transfer/ );
|
||||
%Docstring
|
||||
Constructor for QgsExpressionNodeIndexOperator.
|
||||
%End
|
||||
~QgsExpressionNodeIndexOperator();
|
||||
|
||||
QgsExpressionNode *container() const;
|
||||
%Docstring
|
||||
Returns the container node, representing an array or map value.
|
||||
|
||||
.. seealso:: :py:func:`index`
|
||||
%End
|
||||
|
||||
QgsExpressionNode *index() const;
|
||||
%Docstring
|
||||
Returns the index node, representing an array element index or map key.
|
||||
|
||||
.. seealso:: :py:func:`container`
|
||||
%End
|
||||
|
||||
virtual QgsExpressionNode::NodeType nodeType() const;
|
||||
|
||||
virtual bool prepareNode( QgsExpression *parent, const QgsExpressionContext *context );
|
||||
|
||||
virtual QVariant evalNode( QgsExpression *parent, const QgsExpressionContext *context );
|
||||
|
||||
virtual QString dump() const;
|
||||
|
||||
|
||||
virtual QSet<QString> referencedColumns() const;
|
||||
|
||||
virtual QSet<QString> referencedVariables() const;
|
||||
|
||||
virtual QSet<QString> referencedFunctions() const;
|
||||
|
||||
virtual bool needsGeometry() const;
|
||||
|
||||
virtual QgsExpressionNode *clone() const /Factory/;
|
||||
|
||||
virtual bool isStatic( QgsExpression *parent, const QgsExpressionContext *context ) const;
|
||||
|
||||
|
||||
};
|
||||
|
||||
class QgsExpressionNodeInOperator : QgsExpressionNode
|
||||
|
15
resources/function_help/json/op_index
Normal file
15
resources/function_help/json/op_index
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "[]",
|
||||
"type": "operator",
|
||||
"description": "Index operator. Returns an element from an array or map value.",
|
||||
"arguments": [
|
||||
{ "arg": "index", "description": "array index or map key value" }
|
||||
],
|
||||
"examples": [
|
||||
{ "expression":"array(1,2,3)[0]", "returns":"1"},
|
||||
{ "expression":"array(1,2,3)[2]", "returns":"3"},
|
||||
{ "expression":"array(1,2,3)[-1]", "returns":"3"},
|
||||
{ "expression":"map('a',1,'b',2)['a']", "returns":"1"},
|
||||
{ "expression":"map('a',1,'b',2)['b']", "returns":"2"}
|
||||
]
|
||||
}
|
@ -79,7 +79,8 @@ class CORE_EXPORT QgsExpressionNode SIP_ABSTRACT
|
||||
ntFunction, //!< \see QgsExpression::Node::NodeFunction
|
||||
ntLiteral, //!< \see QgsExpression::Node::NodeLiteral
|
||||
ntColumnRef, //!< \see QgsExpression::Node::NodeColumnRef
|
||||
ntCondition //!< \see QgsExpression::Node::NodeCondition
|
||||
ntCondition, //!< \see QgsExpression::Node::NodeCondition
|
||||
ntIndexOperator, //!< Index operator
|
||||
};
|
||||
|
||||
|
||||
|
@ -1548,3 +1548,97 @@ QString QgsExpressionNodeBinaryOperator::text() const
|
||||
return BINARY_OPERATOR_TEXT[mOp];
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
QVariant QgsExpressionNodeIndexOperator::evalNode( QgsExpression *parent, const QgsExpressionContext *context )
|
||||
{
|
||||
const QVariant container = mContainer->eval( parent, context );
|
||||
ENSURE_NO_EVAL_ERROR;
|
||||
const QVariant index = mIndex->eval( parent, context );
|
||||
ENSURE_NO_EVAL_ERROR;
|
||||
|
||||
switch ( container.type() )
|
||||
{
|
||||
case QVariant::Map:
|
||||
return QgsExpressionUtils::getMapValue( container, parent ).value( index.toString() );
|
||||
|
||||
case QVariant::List:
|
||||
case QVariant::StringList:
|
||||
{
|
||||
const QVariantList list = QgsExpressionUtils::getListValue( container, parent );
|
||||
qlonglong pos = QgsExpressionUtils::getIntValue( index, parent );
|
||||
if ( pos >= list.length() || pos < -list.length() )
|
||||
{
|
||||
return QVariant();
|
||||
}
|
||||
if ( pos < 0 )
|
||||
{
|
||||
// negative indices are from back of list
|
||||
pos += list.length();
|
||||
}
|
||||
|
||||
return list.at( pos );
|
||||
}
|
||||
|
||||
default:
|
||||
parent->setEvalErrorString( tr( "[] can only be used with map or array values, not %1" ).arg( QMetaType::typeName( container.type() ) ) );
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
QgsExpressionNode::NodeType QgsExpressionNodeIndexOperator::nodeType() const
|
||||
{
|
||||
return ntIndexOperator;
|
||||
}
|
||||
|
||||
bool QgsExpressionNodeIndexOperator::prepareNode( QgsExpression *parent, const QgsExpressionContext *context )
|
||||
{
|
||||
bool resC = mContainer->prepare( parent, context );
|
||||
bool resV = mIndex->prepare( parent, context );
|
||||
return resC && resV;
|
||||
}
|
||||
|
||||
QString QgsExpressionNodeIndexOperator::dump() const
|
||||
{
|
||||
return QStringLiteral( "%1[%2]" ).arg( mContainer->dump(), mIndex->dump() );
|
||||
}
|
||||
|
||||
QSet<QString> QgsExpressionNodeIndexOperator::referencedColumns() const
|
||||
{
|
||||
return mContainer->referencedColumns() + mIndex->referencedColumns();
|
||||
}
|
||||
|
||||
QSet<QString> QgsExpressionNodeIndexOperator::referencedVariables() const
|
||||
{
|
||||
return mContainer->referencedVariables() + mIndex->referencedVariables();
|
||||
}
|
||||
|
||||
QSet<QString> QgsExpressionNodeIndexOperator::referencedFunctions() const
|
||||
{
|
||||
return mContainer->referencedFunctions() + mIndex->referencedFunctions();
|
||||
}
|
||||
|
||||
QList<const QgsExpressionNode *> QgsExpressionNodeIndexOperator::nodes() const
|
||||
{
|
||||
QList<const QgsExpressionNode *> lst;
|
||||
lst << this;
|
||||
lst += mContainer->nodes() + mIndex->nodes();
|
||||
return lst;
|
||||
}
|
||||
|
||||
bool QgsExpressionNodeIndexOperator::needsGeometry() const
|
||||
{
|
||||
return mContainer->needsGeometry() || mIndex->needsGeometry();
|
||||
}
|
||||
|
||||
QgsExpressionNode *QgsExpressionNodeIndexOperator::clone() const
|
||||
{
|
||||
QgsExpressionNodeIndexOperator *copy = new QgsExpressionNodeIndexOperator( mContainer->clone(), mIndex->clone() );
|
||||
cloneTo( copy );
|
||||
return copy;
|
||||
}
|
||||
|
||||
bool QgsExpressionNodeIndexOperator::isStatic( QgsExpression *parent, const QgsExpressionContext *context ) const
|
||||
{
|
||||
return mContainer->isStatic( parent, context ) && mIndex->isStatic( parent, context );
|
||||
}
|
||||
|
@ -206,6 +206,57 @@ class CORE_EXPORT QgsExpressionNodeBinaryOperator : public QgsExpressionNode
|
||||
static const char *BINARY_OPERATOR_TEXT[];
|
||||
};
|
||||
|
||||
/**
|
||||
* A indexing expression operator, which allows use of square brackets [] to reference map and array items.
|
||||
* \ingroup core
|
||||
* \since QGIS 3.6
|
||||
*/
|
||||
class CORE_EXPORT QgsExpressionNodeIndexOperator : public QgsExpressionNode
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for QgsExpressionNodeIndexOperator.
|
||||
*/
|
||||
QgsExpressionNodeIndexOperator( QgsExpressionNode *container SIP_TRANSFER, QgsExpressionNode *index SIP_TRANSFER )
|
||||
: mContainer( container )
|
||||
, mIndex( index )
|
||||
{}
|
||||
~QgsExpressionNodeIndexOperator() override { delete mContainer; delete mIndex; }
|
||||
|
||||
/**
|
||||
* Returns the container node, representing an array or map value.
|
||||
* \see index()
|
||||
*/
|
||||
QgsExpressionNode *container() const { return mContainer; }
|
||||
|
||||
/**
|
||||
* Returns the index node, representing an array element index or map key.
|
||||
* \see container()
|
||||
*/
|
||||
QgsExpressionNode *index() const { return mIndex; }
|
||||
|
||||
QgsExpressionNode::NodeType nodeType() const override;
|
||||
bool prepareNode( QgsExpression *parent, const QgsExpressionContext *context ) override;
|
||||
QVariant evalNode( QgsExpression *parent, const QgsExpressionContext *context ) override;
|
||||
QString dump() const override;
|
||||
|
||||
QSet<QString> referencedColumns() const override;
|
||||
QSet<QString> referencedVariables() const override;
|
||||
QSet<QString> referencedFunctions() const override;
|
||||
QList<const QgsExpressionNode *> nodes( ) const override; SIP_SKIP
|
||||
|
||||
bool needsGeometry() const override;
|
||||
QgsExpressionNode *clone() const override SIP_FACTORY;
|
||||
bool isStatic( QgsExpression *parent, const QgsExpressionContext *context ) const override;
|
||||
|
||||
private:
|
||||
|
||||
QgsExpressionNode *mContainer = nullptr;
|
||||
QgsExpressionNode *mIndex = nullptr;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* An expression node for value IN or NOT IN clauses.
|
||||
* \ingroup core
|
||||
|
@ -115,7 +115,7 @@ void addParserLocation(YYLTYPE* yyloc, QgsExpressionNode *node)
|
||||
//
|
||||
|
||||
// operator tokens
|
||||
%token <b_op> OR AND EQ NE LE GE LT GT REGEXP LIKE IS PLUS MINUS MUL DIV INTDIV MOD CONCAT POW SQUARE_BRAKET_OPENING SQUARE_BRAKET_CLOSING
|
||||
%token <b_op> OR AND EQ NE LE GE LT GT REGEXP LIKE IS PLUS MINUS MUL DIV INTDIV MOD CONCAT POW
|
||||
%token <u_op> NOT
|
||||
%token IN
|
||||
|
||||
@ -166,6 +166,7 @@ void addParserLocation(YYLTYPE* yyloc, QgsExpressionNode *node)
|
||||
%right UMINUS // fictitious symbol (for unary minus)
|
||||
|
||||
%left COMMA
|
||||
%left '['
|
||||
|
||||
%destructor { delete $$; } <node>
|
||||
%destructor { delete $$; } <nodelist>
|
||||
@ -284,15 +285,8 @@ expression:
|
||||
| expression IN '(' exp_list ')' { $$ = new QgsExpressionNodeInOperator($1, $4, false); }
|
||||
| expression NOT IN '(' exp_list ')' { $$ = new QgsExpressionNodeInOperator($1, $5, true); }
|
||||
|
||||
| expression '[' expression ']'
|
||||
{
|
||||
QgsExpressionNode::NodeList* args = new QgsExpressionNode::NodeList();
|
||||
args->append( $1 );
|
||||
args->append( $3 );
|
||||
QgsExpressionNodeFunction *f = new QgsExpressionNodeFunction( QgsExpression::functionIndex( "map_get" ), args );
|
||||
$$ = f;
|
||||
}
|
||||
|
||||
| expression '[' expression ']' { $$ = new QgsExpressionNodeIndexOperator( $1, $3 ); }
|
||||
|
||||
| PLUS expression %prec UMINUS { $$ = $2; }
|
||||
| MINUS expression %prec UMINUS { $$ = new QgsExpressionNodeUnaryOperator( QgsExpressionNodeUnaryOperator::uoMinus, $2); }
|
||||
|
||||
|
@ -401,6 +401,9 @@ QgsSqlExpressionCompiler::Result QgsSqlExpressionCompiler::compileNode( const Qg
|
||||
|
||||
case QgsExpressionNode::ntCondition:
|
||||
break;
|
||||
|
||||
case QgsExpressionNode::ntIndexOperator:
|
||||
break;
|
||||
}
|
||||
|
||||
return Fail;
|
||||
|
@ -97,7 +97,8 @@ QgsExpressionBuilderWidget::QgsExpressionBuilderWidget( QWidget *parent )
|
||||
connect( mShowHelpButton, &QPushButton::clicked, this, [ = ]()
|
||||
{
|
||||
functionsplit->setSizes( QList<int>( {mOperationListGroup->width() - mHelpAndValuesWidget->minimumWidth(),
|
||||
mHelpAndValuesWidget->minimumWidth()} ) );
|
||||
mHelpAndValuesWidget->minimumWidth()
|
||||
} ) );
|
||||
mShowHelpButton->setEnabled( false );
|
||||
} );
|
||||
connect( functionsplit, &QSplitter::splitterMoved, this, [ = ]( int, int )
|
||||
@ -608,6 +609,7 @@ void QgsExpressionBuilderWidget::updateFunctionTree()
|
||||
registerItem( QStringLiteral( "Operators" ), QStringLiteral( "<>" ), QStringLiteral( " <> " ) );
|
||||
registerItem( QStringLiteral( "Operators" ), QStringLiteral( "<=" ), QStringLiteral( " <= " ) );
|
||||
registerItem( QStringLiteral( "Operators" ), QStringLiteral( ">=" ), QStringLiteral( " >= " ) );
|
||||
registerItem( QStringLiteral( "Operators" ), QStringLiteral( "[]" ), QStringLiteral( "[ ]" ) );
|
||||
registerItem( QStringLiteral( "Operators" ), QStringLiteral( "||" ), QStringLiteral( " || " ) );
|
||||
registerItem( QStringLiteral( "Operators" ), QStringLiteral( "IN" ), QStringLiteral( " IN " ) );
|
||||
registerItem( QStringLiteral( "Operators" ), QStringLiteral( "LIKE" ), QStringLiteral( " LIKE " ) );
|
||||
@ -957,6 +959,10 @@ void QgsExpressionBuilderWidget::createMarkers( const QgsExpressionNode *inNode
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QgsExpressionNode::NodeType::ntIndexOperator:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,6 +79,7 @@ QgsSqlExpressionCompiler::Result QgsOgrExpressionCompiler::compileNode( const Qg
|
||||
case QgsExpressionNode::ntColumnRef:
|
||||
case QgsExpressionNode::ntInOperator:
|
||||
case QgsExpressionNode::ntLiteral:
|
||||
case QgsExpressionNode::ntIndexOperator:
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -3181,6 +3181,69 @@ class TestQgsExpression: public QObject
|
||||
|
||||
QCOMPARE( v.toDateTime().toMSecsSinceEpoch(), v2.toDateTime().toMSecsSinceEpoch() );
|
||||
}
|
||||
|
||||
void test_IndexOperator()
|
||||
{
|
||||
QgsExpressionContext context;
|
||||
QgsExpression e( QStringLiteral( "'['" ) );
|
||||
QVariant result = e.evaluate( &context );
|
||||
QCOMPARE( result.toString(), QStringLiteral( "[" ) );
|
||||
e = QgsExpression( QStringLiteral( "']'" ) );
|
||||
QCOMPARE( e.evaluate( &context ).toString(), QStringLiteral( "]" ) );
|
||||
e = QgsExpression( QStringLiteral( "'[3]'" ) );
|
||||
QCOMPARE( e.evaluate( &context ).toString(), QStringLiteral( "[3]" ) );
|
||||
e = QgsExpression( QStringLiteral( "'a[3]'" ) );
|
||||
QCOMPARE( e.evaluate( &context ).toString(), QStringLiteral( "a[3]" ) );
|
||||
e = QgsExpression( QStringLiteral( "\"a[3]\"" ) );
|
||||
QCOMPARE( e.evaluate( &context ).toString(), QStringLiteral( "[a[3]]" ) );
|
||||
e = QgsExpression( QStringLiteral( "(1+2)[0]" ) );
|
||||
QVERIFY( !e.evaluate( &context ).isValid() );
|
||||
QVERIFY( e.hasEvalError() );
|
||||
e = QgsExpression( QStringLiteral( "(1+2)['a']" ) );
|
||||
QVERIFY( !e.evaluate( &context ).isValid() );
|
||||
QVERIFY( e.hasEvalError() );
|
||||
// arrays
|
||||
e = QgsExpression( QStringLiteral( "array(1,2,3)[0]" ) );
|
||||
QCOMPARE( e.evaluate( &context ).toInt(), 1 );
|
||||
e = QgsExpression( QStringLiteral( "((array(1,2,3)))[0]" ) );
|
||||
QCOMPARE( e.evaluate( &context ).toInt(), 1 );
|
||||
e = QgsExpression( QStringLiteral( "array(1,2,3)[1]" ) );
|
||||
QCOMPARE( e.evaluate( &context ).toInt(), 2 );
|
||||
e = QgsExpression( QStringLiteral( "array(1,2,3)[2]" ) );
|
||||
QCOMPARE( e.evaluate( &context ).toInt(), 3 );
|
||||
e = QgsExpression( QStringLiteral( "array(1,2,3)[-1]" ) );
|
||||
QCOMPARE( e.evaluate( &context ).toInt(), 3 );
|
||||
e = QgsExpression( QStringLiteral( "array(1,2,3)[-2]" ) );
|
||||
QCOMPARE( e.evaluate( &context ).toInt(), 2 );
|
||||
e = QgsExpression( QStringLiteral( "array(1,2,3)[-3]" ) );
|
||||
QCOMPARE( e.evaluate( &context ).toInt(), 1 );
|
||||
e = QgsExpression( QStringLiteral( "array(1,2,3)[1+1]" ) );
|
||||
QCOMPARE( e.evaluate( &context ).toInt(), 3 );
|
||||
e = QgsExpression( QStringLiteral( "array(1,2,3)[(3-2)]" ) );
|
||||
QCOMPARE( e.evaluate( &context ).toInt(), 2 );
|
||||
e = QgsExpression( QStringLiteral( "array(1,2,3)[3]" ) );
|
||||
QVERIFY( !e.evaluate( &context ).isValid() );
|
||||
QVERIFY( !e.hasEvalError() ); // no eval error - we are tolerant to this
|
||||
e = QgsExpression( QStringLiteral( "array(1,2,3)[-4]" ) );
|
||||
QVERIFY( !e.evaluate( &context ).isValid() );
|
||||
QVERIFY( !e.hasEvalError() ); // no eval error - we are tolerant to this
|
||||
|
||||
// maps
|
||||
e = QgsExpression( QStringLiteral( "map('a',1,'b',2,'c',3)[0]" ) );
|
||||
QVERIFY( !e.evaluate( &context ).isValid() );
|
||||
QVERIFY( !e.hasEvalError() ); // no eval error - we are tolerant to this
|
||||
e = QgsExpression( QStringLiteral( "map('a',1,'b',2,'c',3)['d']" ) );
|
||||
QVERIFY( !e.evaluate( &context ).isValid() );
|
||||
QVERIFY( !e.hasEvalError() ); // no eval error - we are tolerant to this
|
||||
e = QgsExpression( QStringLiteral( "map('a',1,'b',2,'c',3)['a']" ) );
|
||||
QCOMPARE( e.evaluate( &context ).toInt(), 1 );
|
||||
e = QgsExpression( QStringLiteral( "map('a',1,'b',2,'c',3)['b']" ) );
|
||||
QCOMPARE( e.evaluate( &context ).toInt(), 2 );
|
||||
e = QgsExpression( QStringLiteral( "map('a',1,'b',2,'c',3)['c']" ) );
|
||||
QCOMPARE( e.evaluate( &context ).toInt(), 3 );
|
||||
e = QgsExpression( QStringLiteral( "map('a',1,'bbb',2,'c',3)['b'||'b'||'b']" ) );
|
||||
QCOMPARE( e.evaluate( &context ).toInt(), 2 );
|
||||
}
|
||||
};
|
||||
|
||||
QGSTEST_MAIN( TestQgsExpression )
|
||||
|
BIN
tests/testdata/qgis_server_accesscontrol/geo.gpkg
vendored
BIN
tests/testdata/qgis_server_accesscontrol/geo.gpkg
vendored
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user