Qt IVI 查询语言

汽车系统正在变得越来越大,并包含更多功能,尤其是在娱乐和连接性方面。现代系统可以处理电话呼叫,访问手机通讯录,并拥有一个管理媒体数据库的媒体播放器。由于通讯录和媒体数据库现在可能相当大,因此用户能够以方便的方式过滤、排序和搜索它们变得非常重要。

Qt IVI 查询语言为您提供了一个最小的语言来表述您想要显示的内容以及如何排序。该语言独立于底层架构和潜在底层数据库语言(如 SQL 或 PSQL)。Qt IVI 查询语言不指定数据本身,但仅用于过滤和排序。

使用查询语言

目前,仅 QIviSearchAndBrowseModel 类支持查询语言的使用。在这种情况下,模型的后端向查询解析器通知可用于过滤和排序的标识符。

标识符是列或属性名称,可以在查询语言中使用。

假设您使用 QIviSearchAndBrowseModel 访问 QIviAudioTrackItems 的列表,那么 QIviAudioTrackItem 的每个属性都是您的标识符。

以下查询将在专辑 "Nevermind" 上搜索曲目

album='Nevermind'

过滤和排序

Qt IVI 查询语言有两个部分

  1. 过滤规范
  2. 排序顺序定义
album='Nevermind' [/trackNumber]

在上面的查询中,第一部分是 album='Nevermind',它指示只显示来自专辑 "Nevermind" 的曲目。第二部分使用 [] 指定,定义了顺序:结果应根据 trackNumber 降序排序。

过滤

要过滤结果,可以使用以下操作符匹配标识符和值

操作符描述
=测试标识符的值是否等于给定值。对于字符串,比较是大小写敏感的。

注意: == 操作符是别名,并提供相同的结果。

!=测试标识符的值是否不等于给定值。对于字符串,比较是大小写敏感的。
~=测试标识符的值是否等于给定值。对于字符串,比较是不区分大小写的。
>测试左侧值是否大于右侧值。这种比较仅适用于数字。
>=测试左侧值是否大于或等于右侧值。这种比较仅适用于数字。
<测试左侧值是否小于右侧值。这种比较仅适用于数字。
<=检查左侧值是否小于或等于右侧值。此比较仅适用于数字。

当您将标识符与字符串进行比较时,字符串必须始终用单引号《`'`》或双引号《`"`》括起来。支持各种格式,包括有符号数、无符号数和带有指数的浮点数,例如 -5.0E15。

排序

查询的第二部分用于对结果进行排序。它是可选的,如果未提供,后端可以决定结果的列表顺序。要按照升序对 trackNumber 进行排序,请编写以下内容:

[/trackNumber]

要按降序排序

[\trackNumber]

在大型歌曲列表中使用此查询可能不会得到预期的结果,因为有两条不同的曲目可能有相同的 trackNumber。

在这种情况下,您可以提供多个排序顺序。

[\trackNumber][/album]

上述查询按照 trackNumber 的降序对曲目进行排序,对于具有相同的 trackNumber 的曲目则按专辑名称的升序排序。

连接词

通常,仅添加特定条件并定义顺序不足以达到预期的结果。可能需要使用基本布尔代数,Qt IVI 查询语言支持此代数。您可以通过使用 AND / OR 连接词组合多个过滤器。

以下查询仅列出专辑《Metallica》中的前 5 首曲目。

album='Metallica' & trackNumber<=5

您还可以使用括号来否定过滤器。

(album='Metallica') & !(trackNumber>5)

与后端集成

对于后端,查询被转换为类似于抽象语法树(AST)的二进制表示。如查询本身一样,表示被分为两部分:

  1. 指向一个QIviAbstractQueryTerm的指针,它可以被转换为一个以下类型之一:

    QIviConjunctionTerm

    两个查询项之间的连接词表示

    QIviFilterTerm

    过滤器的表示

    QIviScopeTerm

    可以包含另一个项的作用域的表示

  2. 一个QList<QIviOrderTerm>,它提供了用户希望结果出现的顺序。

通常,这种 C++ 表示被转换为后端支持的形式。

例如,要从一个 SQL 数据库中获取结果

首先,我们需要一个函数来将查询中使用的标识符转换为数据库中使用的列名

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;
}

然后,我们需要函数将查询项和排序项转换为 SQL 子句

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();
}

最后,您可以创建以下查询:

QString query = QString(QLatin1String("SELECT * FROM tracks WHERE %1 ORDER BY %2")).arg(createWhereClause(queryTerm), createSortOrder(orderTerms));

©2020 The Qt Company Ltd. 本处包含的文档贡献是其各自所有者的版权。本处提供的文档是根据 Free Software Foundation 发布的《GNU 自由文档许可证 version 1.3》的条款许可的。Qt 及其相关标志是芬兰以及/或其他国家的 The Qt Company Ltd. 的商标。所有其他商标均为其各自所有者的财产。