eolas/zk/Querying_a_collection.md
2024-02-16 15:19:05 +00:00

239 lines
5.6 KiB
Markdown

---
categories:
- Databases
tags: [mongo-db, node-js, mongoose]
---
# Query a Mongo collection with Mongoose
We now have the following entries in our `courses` collection:
```js
{
name: 'Python Course',
author: 'Terry Ogleton',
tags: [ 'python', 'backend' ],
isPublished: true,
_id: new ObjectId("62f4e2527ac4aa2c30d41d23"),
date: 2022-08-11T11:04:50.750Z,
__v: 0
}
{
name: 'Javascript Course',
author: 'Tosh Gnomay',
tags: [ 'js', 'frontend' ],
isPublished: true,
_id: new ObjectId("62f4e2527ac4aa2c30d41d24"),
date: 2022-08-11T11:04:50.750Z,
__v: 0
}
```
Now we will query the collection. This capability is provided via the Mongoose
schema class we used to create the `Course`
[model](/Databases/MongoDB/Create_collections_and_documents_with_Mongoose.md#models).
We have the following methods available to use from the schema:
- `find`
- `findById`
- `findByIdAndRemove`
- `findByIdAndUpdate`
- `findOne`
- `findOneAndUpdate`
- `findOneAndRemove`
- ...
The various `find` methods return a value that is promisified.
## Return values with `find`
```js
async function getCourses() {
const courses = await Course.find();
console.log(courses);
}
```
## Filter values returned
This will return all the published courses where Tosh Gnomay is the author:
```js
async function getCourses() {
const courses = await Course.find({
author: "Tosh Gnomay",
isPublished: true,
});
console.log(courses);
}
```
This time we will filter by the same author but we will add additional
parameters to distinguish:
- only the first ten entries (using `.limit(10)`)
- sort ascending by name (using `.sort({name: 1}))` , to descend we would use
`-1`)
- only return the properties `name` and `tags` for the item in the collection
(using `.select({name: 1, tags: 1})`)
```js
async function getCourses() {
const courses = await Course.find({
author: "Tosh Gnomay",
isPublished: true,
})
.limit(10)
.sort({ name: 1 })
.select({ name: 1, tags: 1 });
console.log(courses);
}
```
This returns:
```js
[
{
_id: new ObjectId("62f4f07a875cff48827b8731"),
name: "Java Course",
tags: ["java", "backend"],
},
{
_id: new ObjectId("62f4e2527ac4aa2c30d41d24"),
name: "Javascript Course",
tags: ["js", "frontend"],
},
];
```
> Note that the UUID is always returned, whether we specify it or not.
## Querying with operators
So far when filtering we have been doing so with reference to properties that
exist on the document's model (`author`, `isPublished` etc) and we have applied
tranformations on the data returned (sorting, limiting the number or matches
etc.). However we can also apply **operators** within our queries. Operators
allow us to perform computations on the data, for example: for a given numerical
property on an object, return the objects for which this value is within a
certain range.
When we apply operators we use the `$` symbol before the operator and pass the
operator function as an object.
The schema:
```
Model.find( { property: { $operator: conditions } } )
```
### Comparison operators
The following comparison operators are available in MongoDB:
| Operator | Function |
| -------- | ------------------------- |
| `eq` | Equal to |
| `ne` | Not equal to |
| `gt` | Greater than |
| `gte` | Greater than or less than |
| `lt` | Less than |
| `lte` | Less than or equal to |
| `in` | In |
| `nin` | Not in |
We can employ these comparators within a `.find` filter. For example let's
imagine that our `courses` instances have a property of `price`.
To filter course prices that are greater than or equal to 10 and less than or
equal to 29:
```js
Course.find({ price: { $gte: 10, $lte: 20 } });
```
To filter course prices that are either 10, 15 or 20:
```js
Course.find({ price: { $in: [10, 15, 20] } });
```
### Logical operators
When we apply logical operators, we do not apply the query within the main
`find` method. We use a dedicated method that corresponds to the logical
predicate.
For example to query by logical
[OR](/Logic/Truth-functional_connectives.md#disjunction):
```js
async function getCourses() {
const courses = await Course.find().or([
{ author: "Tosh Gnomay" },
{ isPublished: true },
]);
console.log(courses);
}
```
We write each disjunct as an object representing the conditions we are filtering
for within an array that is passed to the `.or()` method.
The same syntax applies for conjunction.
### Regular expressions
When filtering by strings we can use Regex for greater power.
Previously we filtered by the author name:
```js
.find({author: "Tosh Gnomay"})
```
To demonstrate regex we could filter by names beginning with `T`:
```js
.find({author: /^T*/})
```
```js
async function getCourses() {
const courses = await Course.find()
.or([{ author: "Tosh Gnomay" }, { isPublished: true }])
.count();
console.log(courses);
}
```
This will return a number.
### Pagination
We previously used the `limit()` method to control how many matches we return
from a query. We can extend this functionality by creating pagination. This
allows us to meter the return values into set chunks. This is used frequently
when interacting with a database through a mediating RESTful API. For example to
return values from endpoints such as:
```
/api/courses?pageNumber=2&pageSize=10
```
To do this you pass two values as query parameters
```js
const pageNumber = 2;
const pageSize = 10;
async function getCourses() {
const courses = await Course.find()
.skip(pageNumber - 1 * pageSize)
.limit(pageSize);
console.log(courses);
}
```