Qt 接口框架查询语言

随着汽车的系统变得越来越庞大,并且集成了更多的功能,尤其是在娱乐和连接性方面。现代系统可以处理电话通话,访问手机通讯录,并拥有一个管理媒体数据库的媒体播放器。由于地址簿和媒体数据库现在可能很大,因此用户能够以方便的方式筛选、排序和搜索它们非常重要。

Qt 接口框架查询语言提供了一个最小的语言来表达您想要显示的内容以及应该如何排序。此语言既独立于底层架构,也独立于潜在底层数据库语言:如 SQL 或 PSQL。Qt 接口框架查询语言不指定数据本身,仅用于筛选和排序。

使用查询语言

目前只有 QIfFilterAndBrowseModel 类支持查询语言的使用。在这种情况下,模型的后端会告知查询解析器可以用于筛选和排序的标识符。

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

假设您使用 QIfFilterAndBrowseModel 访问一系列 QIfAudioTrackItems:那么 QIfAudioTrackItem 的每个属性都是您的标识符。

以下查询会导致在专辑 "Nevermind" 上进行音轨搜索

album='Nevermind'

筛选和排序

Qt 接口框架查询语言有两个部分

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

在上述查询中,第一部分是 album='Nevermind',它表示只显示来自专辑 "Nevermind" 的音轨。第二部分由 [] 指定,并定义了顺序:结果应按 trackNumber 以升序排序。

筛选

要筛选结果,可以使用以下操作符将标识符与值匹配

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

注意: == 操作符是一个别名,并产生相同的结果。

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

注意:当将标识符与字符串进行比较时,字符串必须用单引号'或双引号""括起来。支持的数字格式包括符号数、无符号数和使用指数的浮点数,如 -5.0E15。

排序

查询的第二部分用于对结果进行排序。它是可选的,如果没有提供,后端可以决定结果的列表顺序。

要按升序对 trackNumber 进行排序,请写入以下内容

[/trackNumber]

要按降序排序

[\trackNumber]

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

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

[\trackNumber][/album]

上述查询首先按 trackNumber 降序排序,然后按专辑名升序排序具有相同 trackNumber 的曲目。

连词

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

以下查询只列出专辑 'Metallica' 的前 5 首曲目

album='Metallica' & trackNumber<=5

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

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

与后端集成

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

  1. 指向 QIfAbstractQueryTerm 的指针,可以转换为以下类型之一

    QIfConjunctionTerm

    两个查询术语之间连词的表示

    QIfFilterTerm

    过滤器的表示

    QIfScopeTerm

    可以包含另一个术语的范围的表示

  2. 一个 QList<QIfOrderTerm>,提供了用户想要结果所在的顺序。

通常,这种 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<QIfOrderTerm> &orderTerms)
{
    QStringList order;
    int i = 0;
    for (const QIfOrderTerm & 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(QIfAbstractQueryTerm *term)
{
    if (!term)
        return QString();

    switch (term->type()) {
    case QIfAbstractQueryTerm::ScopeTerm: {
        QIfScopeTerm *scope = static_cast<QIfScopeTerm*>(term);
        return QString(QLatin1String("%1 (%2)")).arg(scope->isNegated() ? "NOT" : "",createWhereClause(scope->term()));
    }
    case QIfAbstractQueryTerm::ConjunctionTerm: {
        QIfConjunctionTerm *conjunctionTerm = static_cast<QIfConjunctionTerm*>(term);
        QString conjunction = QLatin1String("AND");
        if (conjunctionTerm->conjunction() == QIfConjunctionTerm::Or)
            conjunction = QLatin1String("OR");

        QString string;
        QListIterator<QIfAbstractQueryTerm*> it(conjunctionTerm->terms());
        while (it.hasNext()) {
            string += createWhereClause(it.next());
            if (it.hasNext())
                string += QLatin1Literal(" ") + conjunction + QLatin1Literal(" ");
        }
        return string;
    }
    case QIfAbstractQueryTerm::FilterTerm: {
        QIfFilterTerm *filter = static_cast<QIfFilterTerm*>(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 QIfFilterTerm::Equals: operatorString = QLatin1String("="); break;
            case QIfFilterTerm::EqualsCaseInsensitive: operatorString = QLatin1String("LIKE"); break;
            case QIfFilterTerm::Unequals: operatorString = QLatin1String("="); negated = !negated; break;
            case QIfFilterTerm::GreaterThan: operatorString = QLatin1String(">"); break;
            case QIfFilterTerm::GreaterEquals: operatorString = QLatin1String(">="); break;
            case QIfFilterTerm::LowerThan: operatorString = QLatin1String("<"); break;
            case QIfFilterTerm::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));

© 2024 Qt公司有限。此处包含的文档贡献是各自所有者的版权。此处提供的文档是根据 free software foundation 发布的 GNU 自由文档许可证版本 1.3 提供的。Qt及其相关商标是芬兰的 Qt公司及其在全世界其他国家的商标。所有其他商标均为各自所有者的财产。