diff options
Diffstat (limited to 'src/ivicore/doc/src/query-language.qdoc')
-rw-r--r-- | src/ivicore/doc/src/query-language.qdoc | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/src/ivicore/doc/src/query-language.qdoc b/src/ivicore/doc/src/query-language.qdoc new file mode 100644 index 0000000..e704dde --- /dev/null +++ b/src/ivicore/doc/src/query-language.qdoc @@ -0,0 +1,266 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the QtIvi module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! +\page query-language.html +\title The Qt IVI Query Language +\keyword Qt IVI Query Language +\previouspage Models +\contentspage Concepts + +Automotive systems are getting bigger and bigger and with it also the feature-set in areas like +entertainment or connectivity. Modern system can handle phone calls, access the mobile phone's addressbook, and have a mediaplayer +which can manage a media database similar to desktop tools like iTunes. As addressbooks and media databases +are quite big these days, it's important to be able to filter, sort and search them in a convenient way. + +Because of this the Qt IVI Query Language was created and it provides you with an own minimal +language to express what exactly you want to be displayed and how it should be sorted. + +It was chosen to create a new language to be independent on the underlying architecture and +its (potentially) underlying database languages like SQL or PSQL. Other than the two named, the Qt IVI Query Language +is not specifying the actual data itself and is just used for filtering and sorting. + +\section1 Working with the Query Language + +Currently the QIviSearchAndBrowseModel is the only class which supports the usage of the query language. In this +case the backend of the model informs the query parser about the identifiers available for filtering and sorting. + +An identifier is the name of a column or the property which can be used in the query language. Let's say you use the QIviSearchAndBrowseModel +to access a list of QIviAudioTrackItems, then the available identfiers will be every property of the QIviAudioTrackItem. Writing the following +query would result in a search for tracks on the album "Nevermind": + +\code +album='Nevermind' +\endcode + +\section2 Filtering and Sorting + +The Qt IVI Query Language has two parts: the first part being the filter specification, and the second part defining the sort order. + +\code +album='Nevermind' [/trackNumber] +\endcode + +In the query shown above the first part is \c{album='Nevermind'}, and it indicates to only show tracks which are from the album "Nevermind". The second +part is specified by \c{[]} and defines the ordering: in this case the result should be sorted by trackNumber in ascending order. + +\section3 Filtering + +For filtering a result the following operators can be used to match a identifier to a value: + +\table +\header + \li Operator + \li Description +\row + \li = + \li Tests whether the identifier's value is equal to the passed value. In case of a string the comparison is case-sensitive. + The \c{==} operator is an alias and delivers the same result. + +\row + \li != + \li Tests whether the identifier's value is not equal to the passed value. In case of a string the comparison is case-sensitive. + +\row + \li ~= + \li Tests whether the identifier's value is equal to the passed value, but the comparison is done case-insensitive. + +\row + \li > + \li Tests whether the lefthand-side value is greater than the righthand-side value. (only works for numbers). + +\row + \li >= + \li Tests whether the lefthand-side value is greater than or equal to the righthand-side value. (only works for numbers). + +\row + \li < + \li Tests whether the lefthand-side value is less than the righthand-side value. (only works for numbers). + +\row + \li <= + \li Tests whether the lefthand-side value is less than or equal to the righthand-side value. (only works for numbers). + +\endtable + +When comparing an identifier against a string, the string always needs to be encapsulated either between two quotation marks - either single \c{'} or double \c{"} ones. +Numbers are supported in various formats, including signed and unsigned numbers and floats using an exponent e.g. -5.0E15. + +\section3 Sorting + +The second part of a query is used for sorting the result. It's optional and if not provided, the backend can decide in which order the result +should be listed. To sort by trackNumber in ascending order use the following code: + +\code +[/trackNumber] +\endcode + +to sort in descending order: + +\code +[\trackNumber] +\endcode + +Using this query on a big list of songs might not give the intendend result as there could be two different tracks having the same trackNumber. +For this case multiple sort orders can be provided. + +\code +[\trackNumber][/album] +\endcode + +The above query would sort the tracks by trackNumber in descending order and the track with the same trackNumber by album name in ascending order. + +\section2 Conjunctions + +Only adding a specific condition and defining the order is often not enough to achieve the wanted result. The Qt IVI Query Language also supports +basic boolean algebra. Multiple filters can be combined by using AND / OR conjunctions. The following query would only list the first 5 tracks +of the album 'Metallica': + +\code +album='Metallica' & trackNumber<=5 +\endcode + +Of course it's also supported to use brackets as well as to negate filters: + +\code +(album='Metallica') & !(trackNumber>5) +\endcode + +\section1 Backend integration + +For the backend, the query is translated from a string to a binary representation similar to an AST. Like the query itself, the representation is split into two +parts: the first part is a pointer to a QIviAbstractQueryTerm which can be cast to one of the following types: + +\annotatedlist qtivi_queryterms + +The second part is a QList<QIviOrderTerm> and provides the ordering the user wants the result to be in. + +Usually this C++ representation is translated into whatever form the backend supports. For getting the results from an SQL database this can look like this: + +First we need a function to translate the identifiers used in the query to the column names used in the database: +\code +QString SearchAndBrowseBackend::mapIdentifiers(const QString &type) +{ + if (type == QLatin1String("artist")) + return QLatin1String("artistName"); + else if (type == QLatin1String("album")) + return QLatin1String("albumName"); + else if (type == QLatin1String("track")) + return QLatin1String("trackName"); + else + return type; +} +\endcode + +Next we need functions to translate the QueryTerms and the OrderTerms into SQL clauses: + +\code +QString SearchAndBrowseBackend::createSortOrder(const QString &type, const QList<QIviOrderTerm> &orderTerms) +{ + QStringList order; + int i = 0; + for (const QIviOrderTerm & term : orderTerms) { + if (i) + order.append(","); + + order.append(mapIdentifiers(term.propertyName())); + if (term.isAscending()) + order.append("ASC"); + else + order.append("DESC"); + + i++; + } + + return order.join(' '); +} + +QString SearchAndBrowseBackend::createWhereClause(QIviAbstractQueryTerm *term) +{ + if (!term) + return QString(); + + switch (term->type()) { + case QIviAbstractQueryTerm::ScopeTerm: { + QIviScopeTerm *scope = static_cast<QIviScopeTerm*>(term); + return QString(QLatin1String("%1 (%2)")).arg(scope->isNegated() ? "NOT" : "",createWhereClause(scope->term())); + } + case QIviAbstractQueryTerm::ConjunctionTerm: { + QIviConjunctionTerm *conjunctionTerm = static_cast<QIviConjunctionTerm*>(term); + QString conjunction = QLatin1String("AND"); + if (conjunctionTerm->conjunction() == QIviConjunctionTerm::Or) + conjunction = QLatin1String("OR"); + + QString string; + QListIterator<QIviAbstractQueryTerm*> it(conjunctionTerm->terms()); + while (it.hasNext()) { + string += createWhereClause(it.next()); + if (it.hasNext()) + string += QLatin1Literal(" ") + conjunction + QLatin1Literal(" "); + } + return string; + } + case QIviAbstractQueryTerm::FilterTerm: { + QIviFilterTerm *filter = static_cast<QIviFilterTerm*>(term); + QString operatorString; + bool negated = filter->isNegated(); + QString value; + if (filter->value().type() == QVariant::String) + value = QString(QLatin1String("'%1'")).arg(filter->value().toString().replace('*', '%')); + else + value = filter->value().toString(); + + switch (filter->operatorType()){ + case QIviFilterTerm::Equals: operatorString = QLatin1String("="); break; + case QIviFilterTerm::EqualsCaseInsensitive: operatorString = QLatin1String("LIKE"); break; + case QIviFilterTerm::Unequals: operatorString = QLatin1String("="); negated = !negated; break; + case QIviFilterTerm::GreaterThan: operatorString = QLatin1String(">"); break; + case QIviFilterTerm::GreaterEquals: operatorString = QLatin1String(">="); break; + case QIviFilterTerm::LowerThan: operatorString = QLatin1String("<"); break; + case QIviFilterTerm::LowerEquals: operatorString = QLatin1String("<="); break; + } + + QStringList clause; + if (negated) + clause.append(QLatin1String("NOT")); + clause.append(mapIdentifiers(filter->propertyName())); + clause.append(operatorString); + clause.append(value); + + return clause.join(" "); + } + } + + return QString(); +} +\endcode + +The actual query can then be created like this: + +\code +QString query = QString(QLatin1String("SELECT * FROM tracks WHERE %1 ORDER BY %2")).arg(createWhereClause(queryTerm), createSortOrder(orderTerms)); +\endcode +*/ |