mirror of
https://github.com/silverqx/TinyORM.git
synced 2025-12-17 00:24:46 -06:00
1346 lines
40 KiB
Plaintext
1346 lines
40 KiB
Plaintext
---
|
|
sidebar_position: 2
|
|
sidebar_label: Collections
|
|
description: The ModelsCollection is specialized container which provides a fluent, convenient wrapper for working with vector of models. Is much more powerful than vectors and expose a variety of map / reduce operations that may be chained using an intuitive interface. All TinyORM methods that return more than one model result will return instances of the ModelsCollection class.
|
|
keywords: [c++ orm, orm, collections, collection, model, tinyorm]
|
|
---
|
|
|
|
import Link from '@docusaurus/Link'
|
|
|
|
# TinyORM: Collections
|
|
|
|
- [Introduction](#introduction)
|
|
- [Creating Collections](#creating-collections)
|
|
- [Available Methods](#available-methods)
|
|
|
|
## Introduction
|
|
|
|
<div className="api-stability alert alert--success">
|
|
<Link to='/stability#stability-indexes'>__Stability: 2__</Link> - Stable
|
|
</div>
|
|
|
|
The `Orm::Tiny::Types::ModelsCollection` is specialized container which provides a fluent, convenient wrapper for working with vector of models. All TinyORM methods that return more than one model result, will return instances of the `ModelsCollection` class, including results retrieved via the `get` method or methods that return relationships like the `getRelation` and `getRelationValue`.
|
|
|
|
The `ModelsCollection` class extends `QList<Model>`, so it naturally inherits dozens of methods used to work with the underlying vector of TinyORM models. Be sure to review the [`QList`](https://doc.qt.io/qt/qlist.html) documentation to learn all about these helpful methods!
|
|
|
|
:::info
|
|
The `ModelsCollection` template parameter can be declared only with the model type or a model type pointer, it also can't be `const` and can't be a reference. It's constrained using the `DerivedCollectionModel` concept.
|
|
:::
|
|
|
|
You can iterate over the `ModelsCollection` the same way as over the `QList`:
|
|
|
|
```cpp
|
|
using Models::User;
|
|
|
|
ModelsCollection<User> users = User::whereEq("active", true)->get();
|
|
|
|
for (const auto &user : users)
|
|
qDebug() << user.getAttribute<QString>("name");
|
|
```
|
|
|
|
However, as previously mentioned, collections are much more powerful than vectors and expose a variety of map / reduce operations that may be chained using an intuitive interface. For example, we may remove all active users and then gather the first name of each remaining user:
|
|
|
|
```cpp
|
|
auto names = User::all().reject([](User *const user)
|
|
{
|
|
return user->getAttribute<bool>("active");
|
|
})
|
|
.pluck("name");
|
|
```
|
|
|
|
As you can see, the `ModelsCollection` class allows you to chain its methods to perform fluent mapping and reducing of the underlying vector. In general, collections are immutable, meaning every `ModelsCollection` method returns an entirely new `ModelsCollection` instance.
|
|
|
|
:::info
|
|
The `ModelsCollection<Model>` is returning from the Models' methods like `get`, `all`, `findMany`, `chunk`; the `ModelsCollection<Model *>` is returning from the relationship-related methods as `getRelation` and `getRelationValue`.
|
|
:::
|
|
|
|
#### Collection Conversion
|
|
|
|
While most TinyORM collection methods return a new instance of `ModelsCollection`, the `modelKeys`, `mapWithKeys`, and `pluck` methods return a base QList or std unordered/map instances. Likewise, one of the `map` methods overload returns the `QList<T>`.
|
|
|
|
### Creating Collections
|
|
|
|
Creating a `ModelsCollection` is as simple as:
|
|
|
|
```cpp
|
|
ModelsCollection<User> users {
|
|
{{"name", "Kate"}, {"votes", 150}},
|
|
{{"name", "John"}, {"votes", 200}},
|
|
};
|
|
```
|
|
|
|
You can also create a collection of pointers, eg. `ModelsCollection<User *>`:
|
|
|
|
```cpp
|
|
ModelsCollection<User *> userPointers {
|
|
&users[0], &users[1],
|
|
};
|
|
```
|
|
|
|
The `ModelsCollection<Model>` is implicitly convertible and assignable from the `QList<Model>`:
|
|
|
|
```cpp
|
|
QList<User> usersVector {
|
|
{{"name", "Kate"}, {"votes", 150}},
|
|
{{"name", "John"}, {"votes", 200}},
|
|
};
|
|
|
|
ModelsCollection<User> users(usersVector);
|
|
users = usersVector;
|
|
```
|
|
|
|
Alternatively, you can use the `Orm::collect<Model>` helper function to create a `ModelsCollection` from the given attributes:
|
|
|
|
```cpp
|
|
ModelsCollection<User> users = Orm::collect<User>({
|
|
{{"name", "Kate"}, {"added_on", QDateTime::currentDateTimeUtc()}},
|
|
{{"name", "John"}, {"added_on", QDateTime({2023, 6, 1}, {13, 46, 15}, QTimeZone::UTC)}},
|
|
});
|
|
```
|
|
|
|
:::caution
|
|
The `Orm::collect<Model>` function is __mandatory__ if your attributes contain the `QDateTime` instance, you can read more about this problem [here](tinyorm/getting-started.mdx#qdatetime-and-connection-name-problem).
|
|
:::
|
|
|
|
:::note
|
|
The results of [TinyORM](tinyorm/getting-started.mdx) queries are always returned as `ModelsCollection` instances.
|
|
:::
|
|
|
|
## Available Methods
|
|
|
|
For the majority of the remaining collection documentation, we'll discuss each method available on the `ModelsCollection` class. Remember, all of these methods may be chained to fluently manipulate the underlying vector.
|
|
|
|
Furthermore, almost every method returns a new `ModelsCollection` instance, allowing you to preserve the original copy of the collection when necessary:
|
|
|
|
<div className="collection-methods-list">
|
|
|
|
[all](#method-all)
|
|
[contains](#method-contains)
|
|
[doesntContain](#method-doesntcontain)
|
|
[each](#method-each)
|
|
[except](#method-except)
|
|
[filter](#method-filter)
|
|
[find](#method-find)
|
|
[first](#method-first)
|
|
[firstWhere](#method-first-where)
|
|
[fresh](#method-fresh)
|
|
[implode](#method-implode)
|
|
[isEmpty](#method-isempty)
|
|
[isNotEmpty](#method-isnotempty)
|
|
[last](#method-last)
|
|
[load](#method-load)
|
|
[map](#method-map)
|
|
[mapWithKeys](#method-mapwithkeys)
|
|
[mapWithModelKeys](#method-mapwithmodelkeys)
|
|
[modelKeys](#method-modelkeys)
|
|
[only](#method-only)
|
|
[pluck](#method-pluck)
|
|
[reject](#method-reject)
|
|
[sort](#method-sort)
|
|
[sortBy](#method-sortby)
|
|
[sortByDesc](#method-sortbydesc)
|
|
[sortDesc](#method-sortdesc)
|
|
[stableSort](#method-stablesort)
|
|
[stableSortBy](#method-stablesortby)
|
|
[stableSortByDesc](#method-stablesortbydesc)
|
|
[stableSortDesc](#method-stablesortdesc)
|
|
[tap](#method-tap)
|
|
[toBase](#method-tobase)
|
|
[toJson](#method-tojson)
|
|
[toJsonArray](#method-tojsonarray)
|
|
[toJsonDocument](#method-tojsondocument)
|
|
[toMap](#method-tomap)
|
|
[toMapVariantList](#method-tomapvariantlist)
|
|
[toQuery](#method-toquery)
|
|
[toList](#method-tolist)
|
|
[toListVariantList](#method-tolistvariantlist)
|
|
[unique](#method-unique)
|
|
[uniqueBy](#method-uniqueby)
|
|
[uniqueRelaxed](#method-uniquerelaxed)
|
|
[uniqueRelaxedBy](#method-uniquerelaxedby)
|
|
[value](#method-value)
|
|
[where](#method-where)
|
|
[whereBetween](#method-wherebetween)
|
|
[whereIn](#method-wherein)
|
|
[whereNotBetween](#method-wherenotbetween)
|
|
[whereNotIn](#method-wherenotin)
|
|
[whereNotNull](#method-wherenotnull)
|
|
[whereNull](#method-wherenull)
|
|
|
|
</div>
|
|
|
|
:::note
|
|
For a better understanding of the following examples, many of the variable declarations below use actual types instead of the `auto` keyword.
|
|
:::
|
|
|
|
<div className='collection-methods'>
|
|
|
|
#### `all()` {#method-all}
|
|
|
|
The `all` method returns a copy of the underlying vector represented by the collection:
|
|
|
|
```cpp
|
|
QList<User> = users.all();
|
|
```
|
|
|
|
:::note
|
|
The [`toBase`](#method-tobase) is an alias to the `all` method.
|
|
:::
|
|
|
|
#### `contains()` {#method-contains}
|
|
|
|
The `contains` method may be used to determine if a given model instance is contained by the collection. This method accepts a primary key or a model instance:
|
|
|
|
```cpp
|
|
users.contains(1);
|
|
|
|
users.contains(User::find(1));
|
|
```
|
|
|
|
Alternatively, you may pass a lambda expression to the `contains` method to determine if a model exists in the collection matching a given truth test:
|
|
|
|
```cpp
|
|
users.contains([](const User *const user)
|
|
{
|
|
return user->getKeyCasted() == 2;
|
|
});
|
|
```
|
|
|
|
For the inverse of `contains`, see the [doesntContain](#method-doesntcontain) method.
|
|
|
|
#### `doesntContain()` {#method-doesntcontain}
|
|
|
|
The `doesntContain` method determines whether the collection does not contain a given item. This method accepts a primary key or a model instance:
|
|
|
|
```cpp
|
|
users.doesntContain(1);
|
|
|
|
users.doesntContain(User::find(1));
|
|
```
|
|
|
|
Alternatively, you may pass a lambda expression to the `doesntContain` method to determine if a model does not exist in the collection matching a given truth test:
|
|
|
|
```cpp
|
|
users.doesntContain([](const User *const user)
|
|
{
|
|
return user->getKeyCasted() == 2;
|
|
});
|
|
```
|
|
|
|
For the inverse of `doesntContain`, see the [contains](#method-contains) method.
|
|
|
|
#### `each()` {#method-each}
|
|
|
|
The `each` method iterates over the models in the collection and passes each model to the lambda expression:
|
|
|
|
```cpp
|
|
ModelsCollection<User> users = Post::whereEq("user_id", 1)->get();
|
|
|
|
users.each([](User *const user)
|
|
{
|
|
// ...
|
|
});
|
|
```
|
|
|
|
If you would like to stop iterating through the models, you may return `false` from your lambda expression:
|
|
|
|
```cpp
|
|
users.each([](User *const user)
|
|
{
|
|
if (/* condition */)
|
|
return false;
|
|
|
|
// Some logic
|
|
|
|
return true;
|
|
});
|
|
```
|
|
|
|
You may also pass the lambda expression with two parameters, whereas the second one is an index:
|
|
|
|
```cpp
|
|
users.each([](User *const user, const std::size_t index)
|
|
{
|
|
// ...
|
|
});
|
|
```
|
|
|
|
The `each` method returns an lvalue __reference__ to the currently processed collection.
|
|
|
|
It can be also called on `ModelsCollection` rvalues, it returns an rvalue reference in this case.
|
|
|
|
#### `except()` {#method-except}
|
|
|
|
The `except` method returns all of the models that do not have the given primary keys:
|
|
|
|
```cpp
|
|
ModelsCollection<User *> usersResult = users.except({1, 2, 3});
|
|
```
|
|
|
|
All of the models are returned if the `ids` argument is empty `except({})`.
|
|
|
|
The order of models in the collection is preserved.
|
|
|
|
For the inverse of `except`, see the [only](#method-only) method.
|
|
|
|
#### `filter()` {#method-filter}
|
|
|
|
The `filter` method filters the collection using the lambda expression, keeping only those models that pass a given truth test:
|
|
|
|
```cpp
|
|
auto usersBanned = users.filter([](const User *const user)
|
|
{
|
|
return user->getAttribute<bool>("is_banned");
|
|
});
|
|
```
|
|
|
|
You may also pass the lambda expression with two parameters, whereas the second one is an index:
|
|
|
|
```cpp
|
|
auto usersBanned = users.filter([](const User *const user,
|
|
const std::size_t index)
|
|
{
|
|
return index < 10 && user->getAttribute<bool>("is_banned");
|
|
});
|
|
```
|
|
|
|
If no lambda expression is supplied, all models of the collection that are equivalent to the `nullptr` will be removed:
|
|
|
|
```cpp
|
|
ModelsCollection<User> usersRaw = User::findMany({1, 2});
|
|
ModelsCollection<User *> users {&usersRaw[0], nullptr, &usersRaw[1]};
|
|
|
|
ModelsCollection<User *> filtered = users.filter();
|
|
|
|
// {1, 2}
|
|
```
|
|
|
|
For the inverse of `filter`, see the [reject](#method-reject) method.
|
|
|
|
#### `find()` {#method-find}
|
|
|
|
The `find` method returns the model that has a primary key matching the given key:
|
|
|
|
```cpp
|
|
User *const user = users.find(1);
|
|
```
|
|
|
|
If you pass a model instance, `find` will attempt to return a model matching the primary key:
|
|
|
|
```cpp
|
|
User *user = users.find(anotherUser);
|
|
```
|
|
|
|
The two overloads above also accept the second `defaultModel` model argument, which will be returned if a model was not found in the collection, its default value is the `nullptr`.
|
|
|
|
Alternatively, may pass more IDs and `find` will return all models which have a primary key within the given unordered set:
|
|
|
|
```cpp
|
|
ModelsCollection<User *> usersMany = users.find({1, 2});
|
|
```
|
|
|
|
This overload internally calls the [`only`](#method-only) method.
|
|
|
|
#### `first()` {#method-first}
|
|
|
|
The `first` method returns the first model in the collection that passes a given truth test:
|
|
|
|
```cpp
|
|
ModelsCollection<User> users {
|
|
{{"name", "Kate"}, {"votes", 150}},
|
|
{{"name", "John"}, {"votes", 200}},
|
|
{{"name", "Jack"}, {"votes", 400}},
|
|
};
|
|
|
|
User *user = users.first([](User *const user)
|
|
{
|
|
return user->getAttribute<quint64>("votes") > 150;
|
|
});
|
|
|
|
// {{"name", "John"}, {"votes", 200}}
|
|
```
|
|
|
|
If no model passes a given truth test then the value of the second `defaultModel` argument will be returned, its default value is the `nullptr`.
|
|
|
|
```cpp
|
|
using NullVariant = Orm::Utils::NullVariant;
|
|
|
|
User defaultUser {{"name", NullVariant::QString()},
|
|
{"votes", NullVariant::ULongLong()}};
|
|
|
|
User *user = users.first([](User *const user)
|
|
{
|
|
return user->getAttribute<quint64>("votes") > 500;
|
|
},
|
|
&defaultUser);
|
|
|
|
/*
|
|
{{"name", NullVariant::QString()},
|
|
{"votes", NullVariant::ULongLong()}}
|
|
*/
|
|
```
|
|
|
|
You can also call all `first` overloads provided by the [`QList::first`](https://doc.qt.io/qt/qlist.html#first).
|
|
|
|
#### `firstWhere()` {#method-first-where}
|
|
|
|
The `firstWhere` method returns the first model in the collection with the given column / value pair:
|
|
|
|
```cpp
|
|
using NullVariant = Orm::Utils::NullVariant;
|
|
|
|
ModelsCollection<User> users {
|
|
{{"name", "Leon"}, {"age", NullVariant::UShort()}},
|
|
{{"name", "Jill"}, {"age", 14}},
|
|
{{"name", "Jack"}, {"age", 23}},
|
|
{{"name", "Jill"}, {"age", 84}},
|
|
};
|
|
|
|
auto user = users.firstWhereEq("name", "Linda");
|
|
|
|
// {{"name", "Jill"}, {"age", 14}}
|
|
```
|
|
|
|
You may also call the `firstWhere` method with a comparison operator:
|
|
|
|
```cpp
|
|
users.firstWhere("age", ">=", 18);
|
|
|
|
// {{"name", "Jack"}, {"age", 23}}
|
|
```
|
|
|
|
#### `fresh()` {#method-fresh}
|
|
|
|
The `fresh` method retrieves a fresh instance of each model in the collection from the database. In addition, any specified relationships will be eager loaded:
|
|
|
|
```cpp
|
|
auto usersFresh = users.fresh();
|
|
|
|
auto usersFresh = users.fresh("comments");
|
|
|
|
auto usersFresh = users.fresh("posts:id,name");
|
|
|
|
auto usersFresh = users.fresh({"comments", "posts:id,name"});
|
|
```
|
|
|
|
The `relations` argument format is the same as for TinyBuilder's [`load`](relationships.mdx#lazy-eager-loading) method.
|
|
|
|
#### `implode()` {#method-implode}
|
|
|
|
The `implode` method joins attributes by the given column and the "glue" string you wish to place between the values:
|
|
|
|
```cpp
|
|
ModelsCollection<Product> products {
|
|
{{"product", "Desk"}, {"price", 200}},
|
|
{{"product", "Chair"}, {"price", 100}},
|
|
};
|
|
|
|
products.implode("product", ", ");
|
|
|
|
// {Desk, Chair}
|
|
```
|
|
|
|
The default "glue" value is an empty string "".
|
|
|
|
#### `isEmpty()` {#method-isempty}
|
|
|
|
The `isEmpty` method returns `true` if the collection is empty; otherwise, `false` is returned:
|
|
|
|
```cpp
|
|
ModelsCollection<User>().isEmpty();
|
|
|
|
// true
|
|
```
|
|
|
|
#### `isNotEmpty()` {#method-isnotempty}
|
|
|
|
The `isNotEmpty` method returns `true` if the collection is not empty; otherwise, `false` is returned:
|
|
|
|
```cpp
|
|
ModelsCollection<User>().isNotEmpty();
|
|
|
|
// false
|
|
```
|
|
|
|
#### `last()` {#method-last}
|
|
|
|
The `last` method returns the last model in the collection that passes a given truth test:
|
|
|
|
```cpp
|
|
ModelsCollection<User> users {
|
|
{{"name", "Kate"}, {"votes", 150}},
|
|
{{"name", "John"}, {"votes", 200}},
|
|
{{"name", "Jack"}, {"votes", 400}},
|
|
{{"name", "Rose"}, {"votes", 350}},
|
|
};
|
|
|
|
User *user = users.last([](User *const user)
|
|
{
|
|
return user->getAttribute<quint64>("votes") < 300;
|
|
});
|
|
|
|
// {{"name", "John"}, {"votes", 200}}
|
|
```
|
|
|
|
If no model passes a given truth test then the value of the second `defaultModel` argument will be returned, its default value is the `nullptr`.
|
|
|
|
```cpp
|
|
using NullVariant = Orm::Utils::NullVariant;
|
|
|
|
User defaultUser {{"name", NullVariant::QString()},
|
|
{"votes", NullVariant::ULongLong()}};
|
|
|
|
User *user = users.last([](User *const user)
|
|
{
|
|
return user->getAttribute<quint64>("votes") < 100;
|
|
},
|
|
&defaultUser);
|
|
|
|
/*
|
|
{{"name", NullVariant::QString()},
|
|
{"votes", NullVariant::ULongLong()}}
|
|
*/
|
|
```
|
|
|
|
You can also call all `last` overloads provided by the [`QList::last`](https://doc.qt.io/qt/qlist.html#last).
|
|
|
|
#### `load()` {#method-load}
|
|
|
|
The `load` method eager loads the given relationships for all models in the collection:
|
|
|
|
```cpp
|
|
users.load({"comments", "posts"});
|
|
|
|
users.load("comments.author");
|
|
|
|
users.load({{"comments"}, {"posts", [](auto &query)
|
|
{
|
|
query.whereEq("active", true);
|
|
}}});
|
|
```
|
|
|
|
The `relations` argument format is the same as for TinyBuilder's [`load`](relationships.mdx#lazy-eager-loading) method.
|
|
|
|
#### `map()` {#method-map}
|
|
|
|
The `map` method iterates through the collection and passes a __copy__ of each model to the given lambda expression. The lambda expression is free to modify the model and return it, thus forming a new collection of modified models:
|
|
|
|
```cpp
|
|
ModelsCollection<User> users {
|
|
{{"name", "John"}, {"votes", 200}},
|
|
{{"name", "Jack"}, {"votes", 400}},
|
|
};
|
|
|
|
auto usersAdded = users.map([](User &&userCopy)
|
|
{
|
|
if (userCopy.getAttribute<QString>("name") == "John")
|
|
userCopy["votes"] = userCopy.getAttribute<quint64>("votes") + 1;
|
|
|
|
return std::move(userCopy);
|
|
});
|
|
|
|
/*
|
|
{
|
|
{{"name", "John"}, {"price", 201}},
|
|
{{"name", "Jack"}, {"price", 400}},
|
|
}
|
|
*/
|
|
```
|
|
|
|
The second `map` overload allows to return the `QList<T>`:
|
|
|
|
```cpp
|
|
QList<quint64> usersAdded = users.map<quint64>([](User &&userCopy)
|
|
{
|
|
const auto votesRef = userCopy["votes"];
|
|
|
|
if (userCopy.getAttribute<QString>("name") == "John")
|
|
votesRef = userCopy.getAttribute<quint64>("votes") + 1;
|
|
|
|
return votesRef->value<quint64>();
|
|
});
|
|
|
|
// {201, 400}
|
|
```
|
|
|
|
Both overloads allow to pass the lambda expression with two arguments, whereas the second argument can be an index of the `std::size_t` type.
|
|
|
|
:::caution
|
|
Like most other collection methods, `map` returns a new collection instance; it does not modify the collection it is called on. If you want to modify the original collection in place, use the [`each`](#method-each) method.
|
|
:::
|
|
|
|
:::info
|
|
The model copy is passed to the lambda expression even if the `map` iterates over a collection of model pointers `ModelsCollection<Model *>`. The models are dereferenced behind the scene.
|
|
:::
|
|
|
|
#### `mapWithKeys()` {#method-mapwithkeys}
|
|
|
|
The `mapWithKeys` method iterates through the collection and passes each model to the given lambda expression. It returns the `std::unordered_map<K, V>` and the lambda expression should return the `std::pair<K, V>` containing a single column / value pair:
|
|
|
|
```cpp
|
|
ModelsCollection<User> users {
|
|
{{"id", 1}, {"name", "John"}, {"email", "john@example.com"}},
|
|
{{"id", 2}, {"name", "Jill"}, {"email", "jill@example.com"}},
|
|
};
|
|
|
|
auto usersMap = users.mapWithKeys<quint64, QString>(
|
|
[](User *const user) -> std::pair<quint64, QString>
|
|
{
|
|
return {user->getKeyCasted(), user->getAttribute<QString>("name")};
|
|
});
|
|
|
|
// {{1, 'John'}, {2, 'Jill'}}
|
|
```
|
|
|
|
You can also map IDs to the model pointers:
|
|
|
|
```cpp
|
|
auto usersMap = users.mapWithKeys<quint64, User *>(
|
|
[](User *const user) -> std::pair<quint64, User *>
|
|
{
|
|
return {user->getKeyCasted(), user};
|
|
});
|
|
```
|
|
|
|
#### `mapWithModelKeys()` {#method-mapwithmodelkeys}
|
|
|
|
The `mapWithModelKeys` maps the primary keys to the `Model *`, it returns the `std::unordered_map<Model::KeyType, Model *>`:
|
|
|
|
```cpp
|
|
auto usersMap = users.mapWithModelKeys();
|
|
```
|
|
|
|
#### `modelKeys()` {#method-modelkeys}
|
|
|
|
The `modelKeys` method returns the primary keys for all models in the collection:
|
|
|
|
```cpp
|
|
ModelsCollection<User> users {
|
|
{{"id", 1}, {"name", "John"}},
|
|
{{"id", 2}, {"name", "Jill"}},
|
|
{{"id", 3}, {"name", "Kate"}},
|
|
{{"id", 5}, {"name", "Rose"}},
|
|
};
|
|
|
|
users.modelKeys(); // Returns QList<QVariant>
|
|
users.modelKeys<quint64>();
|
|
|
|
// {1, 2, 3, 5}
|
|
```
|
|
|
|
#### `only()` {#method-only}
|
|
|
|
The `only` method returns all of the models that have the given primary keys:
|
|
|
|
```cpp
|
|
ModelsCollection<User *> usersResult = users.only({1, 2, 3});
|
|
```
|
|
|
|
An empty collection is returned if the `ids` argument is empty `only({})`.
|
|
|
|
The order of models in the collection is preserved.
|
|
|
|
For the inverse of `only`, see the [except](#method-except) method.
|
|
|
|
#### `pluck()` {#method-pluck}
|
|
|
|
The `pluck` method retrieves all of the values for a given column, the following overload returns the `QList<QVariant>`:
|
|
|
|
```cpp
|
|
ModelsCollection<Product> products {
|
|
{{"id", 1}, {"name", "Desk"}},
|
|
{{"id", 2}, {"name", "Chair"}},
|
|
};
|
|
|
|
auto plucked = products.pluck("name");
|
|
|
|
// {Desk, Chair}
|
|
```
|
|
|
|
The second overload allows returning the custom type `QList<T>`:
|
|
|
|
```cpp
|
|
auto plucked = products.pluck<QString>("name");
|
|
```
|
|
|
|
You may also specify how you wish the resulting collection to be keyed, this overload returns the `std::map<T, QVariant>`:
|
|
|
|
```cpp
|
|
auto plucked = products.pluck<quint64>("name", "id");
|
|
|
|
// {{1, "Desk"}, {2, "Chair"}}
|
|
```
|
|
|
|
If duplicate keys exist, the last matching attribute will be inserted into the plucked collection:
|
|
|
|
```cpp
|
|
ModelsCollection<Product> collection {
|
|
{{"brand", "Tesla"}, {"color", "red"}},
|
|
{{"brand", "Pagani"}, {"color", "white"}},
|
|
{{"brand", "Tesla"}, {"color", "black"}},
|
|
{{"brand", "Pagani"}, {"color", "orange"}},
|
|
};
|
|
|
|
auto plucked = collection.pluck<QString>("color", "brand");
|
|
|
|
// {{'Tesla', 'black'}, {'Pagani', 'orange"}}
|
|
```
|
|
|
|
#### `reject()` {#method-reject}
|
|
|
|
The `reject` method filters the collection using the given lambda expression. The lambda should return `true` if the model should be removed from the resulting collection:
|
|
|
|
```cpp
|
|
auto usersWithNote = users.reject([](const User *const user)
|
|
{
|
|
return user->getAttribute("note").isNull();
|
|
});
|
|
```
|
|
|
|
You may also pass the lambda expression with two arguments, whereas the second argument can be an index of the `std::size_t` type.
|
|
|
|
For the inverse of the `reject` method, see the [`filter`](#method-filter) method.
|
|
|
|
#### `sort()` {#method-sort}
|
|
|
|
The `sort` method sorts the models collection by primary keys:
|
|
|
|
```cpp
|
|
ModelsCollection<User> users {
|
|
{{"id", 2}, {"name", "Kate"}},
|
|
{{"id", 3}, {"name", "John"}},
|
|
{{"id", 1}, {"name", "Jack"}},
|
|
};
|
|
|
|
auto sorted = users.sort();
|
|
|
|
/*
|
|
{
|
|
{{"id", 1}, {"name", "Jack"}},
|
|
{{"id", 2}, {"name", "Kate"}},
|
|
{{"id", 3}, {"name", "John"}},
|
|
}
|
|
*/
|
|
```
|
|
|
|
You may pass a predicate and projection callbacks to the `sort` method with your own algorithms. Refer to the CPP reference documentation on [`ranges::sort`](https://en.cppreference.com/w/cpp/algorithm/ranges/sort), which is what the `sort` method calls internally.
|
|
|
|
You can eg. sort by multiple columns, for an alternative method of multi-column sorting look at [sortBy](#method-sortby):
|
|
|
|
```cpp
|
|
ModelsCollection<User> users {
|
|
{{"name", "Kate"}, {"votes", 350}},
|
|
{{"name", "John"}, {"votes", 200}},
|
|
{{"name", "John"}, {"votes", 150}},
|
|
{{"name", "Kate"}, {"votes", 200}},
|
|
};
|
|
|
|
auto sorted = users.sort([](const User *const left,
|
|
const User *const right)
|
|
{
|
|
const auto leftValue = left->getAttribute<QString>("name");
|
|
const auto rightValue = right->getAttribute<QString>("name");
|
|
|
|
if (leftValue == rightValue)
|
|
return left->getAttribute<quint64>("votes") <
|
|
right->getAttribute<quint64>("votes");
|
|
|
|
return leftValue < rightValue;
|
|
});
|
|
|
|
/*
|
|
{
|
|
{{"name", "John"}, {"votes", 150}},
|
|
{{"name", "John"}, {"votes", 200}},
|
|
{{"name", "Kate"}, {"votes", 200}},
|
|
{{"name", "Kate"}, {"votes", 350}},
|
|
}
|
|
*/
|
|
```
|
|
|
|
The order of equal elements is not guaranteed to be preserved.
|
|
|
|
:::info
|
|
You can use the [stable](#method-stablesort) sort method variants to preserve the order of equal models.
|
|
:::
|
|
|
|
#### `sortBy()` {#method-sortby}
|
|
|
|
The `sortBy` method sorts the collection by the given column, this overload needs the template argument so it can cast the attribute value before comparing:
|
|
|
|
```cpp
|
|
ModelsCollection<User> users {
|
|
{{"name", "Kate"}, {"votes", 150}},
|
|
{{"name", "John"}, {"votes", 200}},
|
|
{{"name", "Jack"}, {"votes", 400}},
|
|
};
|
|
|
|
auto sorted = users.sortBy<QString>("name");
|
|
|
|
/*
|
|
{
|
|
{{"name", "Jack"}, {"votes", 400}},
|
|
{{"name", "John"}, {"votes", 200}},
|
|
{{"name", "Kate"}, {"votes", 150}},
|
|
}
|
|
*/
|
|
```
|
|
|
|
You may pass the projection callback to determine how to sort the collection's models:
|
|
|
|
```cpp
|
|
auto sorted = users.sortBy([](User *const user)
|
|
{
|
|
return user->getAttribute<quint64>("votes");
|
|
});
|
|
|
|
/*
|
|
{
|
|
{{"name", "Kate"}, {"votes", 150}},
|
|
{{"name", "John"}, {"votes", 200}},
|
|
{{"name", "Jack"}, {"votes", 400}},
|
|
}
|
|
*/
|
|
```
|
|
|
|
If you would like to sort your collection by multiple columns, you may pass a vector of comparison lambda expressions that define each sort operation to the `sortBy` method, in the following example is the `name` column sorted in ascending order and the second `votes` column is sorted in descending order:
|
|
|
|
```cpp
|
|
using AttributeUtils = Orm::Tiny::Utils::Attribute;
|
|
|
|
ModelsCollection<User> users {
|
|
{{"name", "Kate"}, {"votes", 350}},
|
|
{{"name", "John"}, {"votes", 200}},
|
|
{{"name", "John"}, {"votes", 150}},
|
|
{{"name", "Kate"}, {"votes", 200}},
|
|
};
|
|
|
|
auto sorted = users.sortBy({
|
|
[](const User *const left, const User *const right)
|
|
{
|
|
return AttributeUtils::compareForSortBy(
|
|
left->getAttribute<QString>("name"),
|
|
right->getAttribute<QString>("name"));
|
|
},
|
|
[](const User *const left, const User *const right)
|
|
{
|
|
return AttributeUtils::compareForSortByDesc(
|
|
left->getAttribute<quint64>("votes"),
|
|
right->getAttribute<quint64>("votes"));
|
|
},
|
|
});
|
|
|
|
/*
|
|
{
|
|
{{"name", "John"}, {"votes", 200}},
|
|
{{"name", "John"}, {"votes", 150}},
|
|
{{"name", "Kate"}, {"votes", 350}},
|
|
{{"name", "Kate"}, {"votes", 200}},
|
|
}
|
|
*/
|
|
```
|
|
|
|
The `AttributeUtils::compareForSortBy` and `compareForSortByDesc` methods are helper methods, they are needed because the Qt framework doesn't define `<=>` spaceship operator on its types, it doesn't support the three-way comparison.
|
|
|
|
The order of equal elements is not guaranteed to be preserved.
|
|
|
|
#### `sortByDesc()` {#method-sortbydesc}
|
|
|
|
This method has the same signature as the [`sortBy`](#method-sortby) method but will sort the collection in the opposite order.
|
|
|
|
The order of equal elements is not guaranteed to be preserved.
|
|
|
|
#### `sortDesc()` {#method-sortdesc}
|
|
|
|
This method will sort the collection in the opposite order as the [`sort`](#method-sort) method:
|
|
|
|
```cpp
|
|
ModelsCollection<User> users {
|
|
{{"id", 2}, {"name", "Kate"}},
|
|
{{"id", 3}, {"name", "John"}},
|
|
{{"id", 1}, {"name", "Jack"}},
|
|
};
|
|
|
|
auto sorted = users.sortDesc();
|
|
|
|
/*
|
|
{
|
|
{{"id", 3}, {"name", "John"}},
|
|
{{"id", 2}, {"name", "Kate"}},
|
|
{{"id", 1}, {"name", "Jack"}},
|
|
}
|
|
*/
|
|
```
|
|
|
|
The order of equal elements is not guaranteed to be preserved.
|
|
|
|
#### `stableSort()` {#method-stablesort}
|
|
|
|
This method has the same signature as the [`sort`](#method-sort) method but will preserve the order of equal elements (guaranteed to be preserved).
|
|
|
|
#### `stableSortBy()` {#method-stablesortby}
|
|
|
|
This method has the same signature as the [`sortBy`](#method-sortby) method but will preserve the order of equal elements (guaranteed to be preserved).
|
|
|
|
#### `stableSortByDesc()` {#method-stablesortbydesc}
|
|
|
|
This method has the same signature as the [`sortByDesc`](#method-sortbydesc) method but will sort the collection in the opposite order and preserve the order of equal elements (guaranteed to be preserved).
|
|
|
|
#### `stableSortDesc()` {#method-stablesortdesc}
|
|
|
|
This method has the same signature as the [`sortDesc`](#method-sortdesc) method but will sort the collection in the opposite order and preserve the order of equal elements (guaranteed to be preserved).
|
|
|
|
#### `tap()` {#method-tap}
|
|
|
|
The `tap` method passes a collection to the given lambda expression, allowing you to "tap" into the collection at a specific point and do something with the models while not affecting the collection itself:
|
|
|
|
```cpp
|
|
ModelsCollection<User> users {
|
|
{{"id", 2}, {"name", "Kate"}},
|
|
{{"id", 3}, {"name", "John"}},
|
|
{{"id", 1}, {"name", "Jack"}},
|
|
};
|
|
|
|
users.sort()
|
|
.tap([](/*const */ModelsCollection<User *> &usersRef)
|
|
{
|
|
qDebug() << "IDs after sorting:"
|
|
<< usersRef.template modelKeys<quint64>();
|
|
})
|
|
.value<quint64>("id");
|
|
|
|
// 1
|
|
```
|
|
|
|
The `tap` method returns an lvalue __reference__ to the currently processed collection.
|
|
|
|
It can be also called on `ModelsCollection` rvalues, it returns an rvalue reference in this case.
|
|
|
|
#### `toBase()` {#method-tobase}
|
|
|
|
The `toBase` method returns a copy of the underlying vector represented by the collection:
|
|
|
|
```cpp
|
|
QList<User> = users.toBase();
|
|
```
|
|
|
|
:::note
|
|
The [`toBase`](#method-tobase) is an alias to the `all` method.
|
|
:::
|
|
|
|
#### `toJson()` {#method-tojson}
|
|
|
|
The `toJson` method converts the collection of models with all nested relations into a JSON serialized [`QByteArray`](https://doc.qt.io/qt/qbytearray.html).
|
|
|
|
It returns an empty array for empty `many` type relations and `null` for empty `one` type relations.
|
|
|
|
:::info
|
|
The `toJson` method accepts the [`QJsonDocument::JsonFormat`](https://doc.qt.io/qt/qjsondocument.html#JsonFormat-enum), possible values are `QJsonDocument::Indented` or `QJsonDocument::Compact`.
|
|
:::
|
|
|
|
#### `toJsonArray()` {#method-tojsonarray}
|
|
|
|
The `toJsonArray` method converts the collection of models with all nested relations into a [`QJsonArray`](https://doc.qt.io/qt/qjsonarray.html).
|
|
|
|
#### `toJsonDocument()` {#method-tojsondocument}
|
|
|
|
The `toJsonDocument` method converts the collection of models with all nested relations into a [`QJsonDocument`](https://doc.qt.io/qt/qjsondocument.html).
|
|
|
|
#### `toMap()` {#method-tomap}
|
|
|
|
The `toMap` method converts the collection of models with all nested relations into an attributes map `QList<QVariantMap>`.
|
|
|
|
It returns an empty `QVariantList` for empty `many` type relations and a null <abbr title='QVariant::fromValue(nullptr)'>`QVariant`</abbr> for empty `one` type relations.
|
|
|
|
#### `toMapVariantList()` {#method-tomapvariantlist}
|
|
|
|
The `toMapVariantList` method converts the collection of models with all nested relations into an attributes map, but it returns the <abbr title='QList<QVariant>'>`QVariantList`</abbr> instead of the `QList<QVariantMap>`.
|
|
|
|
It returns an empty `QVariantList` for empty `many` type relations and a null <abbr title='QVariant::fromValue(nullptr)'>`QVariant`</abbr> for empty `one` type relations.
|
|
|
|
:::note
|
|
The `toMapVariantList` method is internally needed by the `toJson` related methods.
|
|
:::
|
|
|
|
#### `toQuery()` {#method-toquery}
|
|
|
|
The `toQuery` method returns the `TinyBuilder` instance containing a `whereIn` constraint with the collection of models' primary keys:
|
|
|
|
```cpp
|
|
using Models::User;
|
|
|
|
ModelsCollection<User> users = User::whereEq("status", "VIP")->get();
|
|
|
|
users.toQuery()->update({
|
|
{"status", "Administrator"},
|
|
});
|
|
```
|
|
|
|
#### `toList()` {#method-tolist}
|
|
|
|
The `toList` method converts the collection of models with all nested relations into an attributes vector `QList<QList<AttributeItem>>`.
|
|
|
|
It returns an empty `QVariantList` for empty `many` type relations and a null <abbr title='QVariant::fromValue(nullptr)'>`QVariant`</abbr> for empty `one` type relations.
|
|
|
|
#### `toListVariantList()` {#method-tolistvariantlist}
|
|
|
|
The `toListVariantList` method converts the collection of models with all nested relations into an attributes vector, but it returns the <abbr title='QList<QVariant>'>`QVariantList`</abbr> instead of the `QList<QList<AttributeItem>>`.
|
|
|
|
It returns an empty `QVariantList` for empty `many` type relations and a null <abbr title='QVariant::fromValue(nullptr)'>`QVariant`</abbr> for empty `one` type relations.
|
|
|
|
:::note
|
|
The `toListVariantList` method is internally needed by the `toJson` related methods.
|
|
:::
|
|
|
|
#### `unique()` {#method-unique}
|
|
|
|
The `unique` method returns all of the unique models in the __sorted__ collection. Any models with the same primary key as another model in the collection are removed:
|
|
|
|
```cpp
|
|
ModelsCollection<User> users {
|
|
{{"id", 2}, {"name", "Kate"}},
|
|
{{"id", 1}, {"name", "Jack"}},
|
|
{{"id", 3}, {"name", "John"}},
|
|
{{"id", 1}, {"name", "Jack"}},
|
|
};
|
|
|
|
auto unique = users.unique();
|
|
|
|
/*
|
|
{
|
|
{{"id", 1}, {"name", "Jack"}},
|
|
{{"id", 2}, {"name", "Kate"}},
|
|
{{"id", 3}, {"name", "John"}},
|
|
}
|
|
*/
|
|
```
|
|
|
|
It sorts the collection internally because the [`ranges::unique`](https://en.cppreference.com/w/cpp/algorithm/ranges/unique) can correctly operate only on the sorted container. You can disable it by passing `false` using the first `sort` parameter:
|
|
|
|
```cpp
|
|
auto unique = users.sort().unique(false);
|
|
|
|
/*
|
|
{
|
|
{{"id", 1}, {"name", "Jack"}},
|
|
{{"id", 2}, {"name", "Kate"}},
|
|
{{"id", 3}, {"name", "John"}},
|
|
}
|
|
*/
|
|
```
|
|
|
|
#### `uniqueBy()` {#method-uniqueby}
|
|
|
|
The `uniqueBy` method returns all of the unique models in the __sorted__ collection by the given column. Any models with the same column value as another model in the collection are removed. It needs the template argument, so it can cast the attribute value before comparing:
|
|
|
|
```cpp
|
|
ModelsCollection<User> users {
|
|
{{"name", "Kate"}},
|
|
{{"name", "Jack"}},
|
|
{{"name", "John"}},
|
|
{{"name", "Jack"}},
|
|
};
|
|
|
|
auto unique = users.uniqueBy<QString>("name");
|
|
|
|
/*
|
|
{
|
|
{{"name", "Jack"}},
|
|
{{"name", "John"}},
|
|
{{"name", "Kate"}},
|
|
}
|
|
*/
|
|
```
|
|
|
|
It sorts the collection internally because the [`ranges::unique`](https://en.cppreference.com/w/cpp/algorithm/ranges/unique) can correctly operate only on the sorted container. You can disable it by passing `false` using the second `sort` parameter:
|
|
|
|
```cpp
|
|
auto unique = users.sortBy<QString>("name")
|
|
.uniqueBy<QString>("name", false);
|
|
|
|
/*
|
|
{
|
|
{{"name", "Jack"}},
|
|
{{"name", "John"}},
|
|
{{"name", "Kate"}},
|
|
}
|
|
*/
|
|
```
|
|
|
|
#### `uniqueRelaxed()` {#method-uniquerelaxed}
|
|
|
|
The `uniqueRelaxed` method returns all of the unique models in the collection, it doesn't need a sorted collection. Any models with the same primary key as another model in the collection are removed:
|
|
|
|
```cpp
|
|
ModelsCollection<User> users {
|
|
{{"id", 2}, {"name", "Kate"}},
|
|
{{"id", 1}, {"name", "Jack"}},
|
|
{{"id", 3}, {"name", "John"}},
|
|
{{"id", 1}, {"name", "Jack"}},
|
|
};
|
|
|
|
auto unique = users.uniqueRelaxed();
|
|
|
|
/*
|
|
{
|
|
{{"id", 2}, {"name", "Kate"}},
|
|
{{"id", 1}, {"name", "Jack"}},
|
|
{{"id", 3}, {"name", "John"}},
|
|
}
|
|
*/
|
|
```
|
|
|
|
#### `uniqueRelaxedBy()` {#method-uniquerelaxedby}
|
|
|
|
The `uniqueRelaxedBy` method returns all of the unique models in the collection by the given column, it doesn't need a sorted collection, but it needs the template argument, so it can cast the attribute value before comparing:
|
|
|
|
```cpp
|
|
ModelsCollection<User> users {
|
|
{{"name", "Kate"}},
|
|
{{"name", "Jack"}},
|
|
{{"name", "John"}},
|
|
{{"name", "Jack"}},
|
|
};
|
|
|
|
auto unique = users.uniqueRelaxedBy<QString>("name");
|
|
|
|
/*
|
|
{
|
|
{{"name", "Kate"}},
|
|
{{"name", "Jack"}},
|
|
{{"name", "John"}},
|
|
}
|
|
*/
|
|
```
|
|
|
|
#### `value()` {#method-value}
|
|
|
|
The `value` method retrieves a given value from the first model of the collection:
|
|
|
|
```cpp
|
|
ModelsCollection<User> users {
|
|
{{"name", "John"}, {"votes", 200}},
|
|
{{"name", "Jack"}, {"votes", 400}},
|
|
};
|
|
|
|
QVariant votes = users.value("votes");
|
|
|
|
// 200
|
|
```
|
|
|
|
Alternatively, you can cast an obtained `QVariant` value to the given type by the second `value` overload:
|
|
|
|
```cpp
|
|
quint64 votes = users.value<quint64>("votes");
|
|
```
|
|
|
|
The `value` method also accepts the second `defaultValue` argument, which will be returned if a collection is empty, the first model is `nullptr`, or a model doesn't contain the given column:
|
|
|
|
```cpp
|
|
auto votes = ModelsCollection<User>().value("votes", 0);
|
|
|
|
// 0
|
|
```
|
|
|
|
You can also call all `value` overloads provided by the [`QList::value`](https://doc.qt.io/qt/qlist.html#value).
|
|
|
|
#### `where()` {#method-where}
|
|
|
|
The `where` method filters the collection by a given column / value pair:
|
|
|
|
```cpp
|
|
ModelsCollection<Product> products {
|
|
{{"product", "Desk"}, {"price", 200}},
|
|
{{"product", "Chair"}, {"price", 100}},
|
|
{{"product", "Bookcase"}, {"price", 150}},
|
|
{{"product", "Door"}, {"price", 100}},
|
|
};
|
|
|
|
auto filtered = products.where("price", "=", 100);
|
|
|
|
/*
|
|
{
|
|
{{"product", "Chair"}, {"price", 100}},
|
|
{{"product", "Door"}, {"price", 100}},
|
|
}
|
|
*/
|
|
```
|
|
|
|
For convenience, if you want to verify that a column is `=` to a given value, you may call `whereEq` method. Similar `XxxEq` methods are also defined for other commands:
|
|
|
|
auto filtered = products.whereEq("price", 100);
|
|
|
|
Optionally, you may pass a comparison operator as the second argument.<br/>Supported operators are `=`, `!=`, `<`, `>`, `<=`, and `>=`:
|
|
|
|
```cpp
|
|
ModelsCollection<Product> products {
|
|
{{"product", "Desk"}, {"price", 200}},
|
|
{{"product", "Chair"}, {"price", 100}},
|
|
{{"product", "Bookcase"}, {"price", 150}},
|
|
{{"product", "Door"}, {"price", 250}},
|
|
};
|
|
|
|
auto filtered = products.where("price", ">", 150);
|
|
|
|
/*
|
|
{
|
|
{{"product", "Desk"}, {"price", 200}},
|
|
{{"product", "Door"}, {"price", 250}},
|
|
}
|
|
*/
|
|
```
|
|
|
|
#### `whereBetween()` {#method-wherebetween}
|
|
|
|
The `whereBetween` method filters the collection by determining if a specified models' attribute value is within a given range:
|
|
|
|
```cpp
|
|
ModelsCollection<Product> products {
|
|
{{"product", "Desk"}, {"price", 200}},
|
|
{{"product", "Chair"}, {"price", 80}},
|
|
{{"product", "Bookcase"}, {"price", 150}},
|
|
{{"product", "Pencil"}, {"price", 30}},
|
|
{{"product", "Door"}, {"price", 100}},
|
|
};
|
|
|
|
auto filtered = products.whereBetween<quint64>("price", {100, 200});
|
|
|
|
/*
|
|
{
|
|
{{"product", "Desk"}, {"price", 200}},
|
|
{{"product", "Bookcase"}, {"price", 150}},
|
|
{{"product", "Door"}, {"price", 100}},
|
|
}
|
|
*/
|
|
```
|
|
|
|
#### `whereIn()` {#method-wherein}
|
|
|
|
The `whereIn` method filters models from the collection that have a specified attribute value that is contained within the given unordered set:
|
|
|
|
```cpp
|
|
ModelsCollection<Product> products {
|
|
{{"product", "Desk"}, {"price", 200}},
|
|
{{"product", "Chair"}, {"price", 100}},
|
|
{{"product", "Bookcase"}, {"price", 150}},
|
|
{{"product", "Door"}, {"price", 250}},
|
|
};
|
|
|
|
auto filtered = products.whereIn<quint64>("price", {100, 200});
|
|
|
|
/*
|
|
{
|
|
{{"product", "Desk"}, {"price", 200}},
|
|
{{"product", "Chair"}, {"price", 100}},
|
|
}
|
|
*/
|
|
```
|
|
|
|
An empty collection is returned if the `values` argument is empty `whereIn("price", {})`.
|
|
|
|
The order of models in the collection is preserved.
|
|
|
|
#### `whereNotBetween()` {#method-wherenotbetween}
|
|
|
|
The `whereNotBetween` method filters the collection by determining if a specified models' attribute value is outside of a given range:
|
|
|
|
```cpp
|
|
ModelsCollection<Product> products {
|
|
{{"product", "Desk"}, {"price", 200}},
|
|
{{"product", "Chair"}, {"price", 80}},
|
|
{{"product", "Bookcase"}, {"price", 150}},
|
|
{{"product", "Pencil"}, {"price", 30}},
|
|
{{"product", "Door"}, {"price", 100}},
|
|
};
|
|
|
|
auto filtered = products.whereNotBetween<quint64>("price", {100, 200});
|
|
|
|
/*
|
|
{
|
|
{{"product", "Chair"}, {"price", 80}},
|
|
{{"product", "Pencil"}, {"price", 30}},
|
|
}
|
|
*/
|
|
```
|
|
|
|
#### `whereNotIn()` {#method-wherenotin}
|
|
|
|
The `whereNotIn` method removes models from the collection that have a specified attribute value that is contained within the given unordered set:
|
|
|
|
```cpp
|
|
ModelsCollection<Product> products {
|
|
{{"product", "Desk"}, {"price", 200}},
|
|
{{"product", "Chair"}, {"price", 100}},
|
|
{{"product", "Bookcase"}, {"price", 150}},
|
|
{{"product", "Door"}, {"price", 250}},
|
|
};
|
|
|
|
auto filtered = products.whereNotIn<quint64>("price", {100, 200});
|
|
|
|
/*
|
|
{
|
|
{{"product", "Bookcase"}, {"price", 150}},
|
|
{{"product", "Door"}, {"price", 250}},
|
|
}
|
|
*/
|
|
```
|
|
|
|
All of the models are returned if the `values` argument is empty `whereNotIn("price", {})`.
|
|
|
|
The order of models in the collection is preserved.
|
|
|
|
#### `whereNotNull()` {#method-wherenotnull}
|
|
|
|
The `whereNotNull` method returns models from the collection where the given column is not `null` QVariant:
|
|
|
|
```cpp
|
|
#include <orm/utils/nullvariant.hpp>
|
|
|
|
using NullVariant = Orm::Utils::NullVariant;
|
|
|
|
ModelsCollection<User> users {
|
|
{{"name", "John"}},
|
|
{{"name", NullVariant::QString()}},
|
|
{{"name", "Jack"}},
|
|
};
|
|
|
|
auto filtered = users.whereNotNull("name");
|
|
|
|
/*
|
|
{
|
|
{{"name", "John"}},
|
|
{{"name", "Jack"}},
|
|
}
|
|
*/
|
|
```
|
|
|
|
:::note
|
|
The `NullVariant` class returns the correct `null` QVariant for both Qt 5 `QVariant(QVariant::String)` and also Qt 6 `QVariant(QMetaType(QMetaType::QString))`. The reason why this class still exists even after Qt v5.15 support was removed is the performance boost.
|
|
:::
|
|
|
|
#### `whereNull()` {#method-wherenull}
|
|
|
|
The `whereNull` method returns models from the collection where the given column is `null` QVariant:
|
|
|
|
```cpp
|
|
#include <orm/utils/nullvariant.hpp>
|
|
|
|
using NullVariant = Orm::Utils::NullVariant;
|
|
|
|
ModelsCollection<User> users {
|
|
{{"name", "John"}},
|
|
{{"name", NullVariant::QString()}},
|
|
{{"name", "Jack"}},
|
|
};
|
|
|
|
auto filtered = users.whereNotNull("name");
|
|
|
|
// {{"name", NullVariant::QString()}}
|
|
```
|
|
|
|
:::note
|
|
The `NullVariant` class returns the correct `null` QVariant for both Qt 5 `QVariant(QVariant::String)` and also Qt 6 `QVariant(QMetaType(QMetaType::QString))`. The reason why this class still exists even after Qt v5.15 support was removed is the performance boost.
|
|
:::
|
|
|
|
</div>
|