查询数据
AirCode 的数据库支持传入不同的条件来查询数据,并且对查询结果进行处理。本文档将通过示例说明如何通过 aircode.db
在云函数中执行查询,包括:
为了便于演示,我们假定有一张名为 inventory
的数据表,包含以下数据:
[
{ item: 'MacBook Air', qty: 15, info: { location: 'Beijing', color: 'Black' } },
{ item: 'MacBook Pro', qty: 35, info: { location: 'Tokyo', color: 'Silver' } },
{ item: 'iPhone 14', qty: 80, info: { location: 'New York', color: 'Blue' } },
{ item: 'iPhone SE', qty: 120, info: { location: 'London', color: 'Red' } },
{ item: 'iPad mini', qty: 95, info: { location: 'Beijing', color: 'Pink' } }
]
[
{ item: 'MacBook Air', qty: 15, info: { location: 'Beijing', color: 'Black' } },
{ item: 'MacBook Pro', qty: 35, info: { location: 'Tokyo', color: 'Silver' } },
{ item: 'iPhone 14', qty: 80, info: { location: 'New York', color: 'Blue' } },
{ item: 'iPhone SE', qty: 120, info: { location: 'London', color: 'Red' } },
{ item: 'iPad mini', qty: 95, info: { location: 'Beijing', color: 'Pink' } }
]
获取所有记录
通过 where({ field: value }).find()
查询所有匹配的记录,结果为数组。若 where
中不传入值,则会返回该表的所有记录。
const aircode = require('aircode');
module.exports = async function(params, context) {
// Use `aircode.db.table` to get a table
const InventoryTable = aircode.db.table('inventory');
// Find matched records
const matchedRecords = await InventoryTable
.where({ item: 'MacBook Air' })
.find();
// Find all records in the table
const allRecords = await InventoryTable
.where()
.find();
return {
matchedRecords,
allRecords,
};
}
const aircode = require('aircode');
module.exports = async function(params, context) {
// Use `aircode.db.table` to get a table
const InventoryTable = aircode.db.table('inventory');
// Find matched records
const matchedRecords = await InventoryTable
.where({ item: 'MacBook Air' })
.find();
// Find all records in the table
const allRecords = await InventoryTable
.where()
.find();
return {
matchedRecords,
allRecords,
};
}
为了确保查询过程的稳定性,对于查询结果的最大条数和数据量大小均有限制,请参考:资源限制 - 数据库 - 查询限制。
获取单条记录
使用 findOne()
可以只返回满足条件的第一条记录,结果为 Object
类型,如果没有满足条件的则返回 null
。
const aircode = require('aircode');
module.exports = async function(params, context) {
// Use `aircode.db.table` to get a table
const InventoryTable = aircode.db.table('inventory');
// Use `findOne` to get the first matched record
const firstRecord = await InventoryTable
.where({ item: 'iPhone 14' })
.findOne();
return {
firstRecord,
};
}
const aircode = require('aircode');
module.exports = async function(params, context) {
// Use `aircode.db.table` to get a table
const InventoryTable = aircode.db.table('inventory');
// Use `findOne` to get the first matched record
const firstRecord = await InventoryTable
.where({ item: 'iPhone 14' })
.findOne();
return {
firstRecord,
};
}
按正则表达式查询
通过在查询语句中传入正则表达式,可以实现正则匹配的查询,这一般用来实现模糊匹配或者内容搜索。
const aircode = require('aircode');
module.exports = async function(params, context) {
// Use `aircode.db.table` to get a table
const InventoryTable = aircode.db.table('inventory');
// Use a regex to search all MacBooks
const result = await InventoryTable
.where({ item: /^MacBook/ })
.find();
return {
result,
};
}
const aircode = require('aircode');
module.exports = async function(params, context) {
// Use `aircode.db.table` to get a table
const InventoryTable = aircode.db.table('inventory');
// Use a regex to search all MacBooks
const result = await InventoryTable
.where({ item: /^MacBook/ })
.find();
return {
result,
};
}
特别的,当正则表达式以 ^
开头时,还可以利用已经建立的索引来优化查询效率,更多可参考使用索引优化查询。
按大于、小于等比较条件查询
除了全等匹配外,还可以通过 aircode.db
中提供的查询操作符来实现大于、小于等比较条件的查询。
const aircode = require('aircode');
module.exports = async function(params, context) {
// All operators are nested in `aircode.db`
const { db } = aircode;
// Use `db.table` to get a table
const InventoryTable = db.table('inventory');
// Use query operators to specific conditions
const gtResult = await InventoryTable
.where({ qty: db.gt(90) })
.find();
const inResult = await InventoryTable
.where({ item: db.in([ 'MacBook Pro', 'iPad Mini' ]) })
.find();
return {
gtResult,
inResult,
};
}
const aircode = require('aircode');
module.exports = async function(params, context) {
// All operators are nested in `aircode.db`
const { db } = aircode;
// Use `db.table` to get a table
const InventoryTable = db.table('inventory');
// Use query operators to specific conditions
const gtResult = await InventoryTable
.where({ qty: db.gt(90) })
.find();
const inResult = await InventoryTable
.where({ item: db.in([ 'MacBook Pro', 'iPad Mini' ]) })
.find();
return {
gtResult,
inResult,
};
}
完整的查询操作符定义请参考:数据库 API - 对比操作符。
按时间区间查询
使用时间区间来设置查询条件的方式和使用大于、小于是相同的,但需要注意传递的参数应该为 Date
类型。
例如,查询最近 24 小时之内插入的所有数据:
const aircode = require('aircode');
module.exports = async function(params, context) {
// All operators are nested in `aircode.db`
const { db } = aircode;
// Use `db.table` to get a table
const InventoryTable = db.table('inventory');
// Use `Date` object as the conditions
const to = new Date();
const from = new Date(to.getTime() - (24 * 60 * 60 * 1000));
const result = await InventoryTable
.where({ createdAt: db.gt(from).lte(to) })
.find();
return {
result,
};
}
const aircode = require('aircode');
module.exports = async function(params, context) {
// All operators are nested in `aircode.db`
const { db } = aircode;
// Use `db.table` to get a table
const InventoryTable = db.table('inventory');
// Use `Date` object as the conditions
const to = new Date();
const from = new Date(to.getTime() - (24 * 60 * 60 * 1000));
const result = await InventoryTable
.where({ createdAt: db.gt(from).lte(to) })
.find();
return {
result,
};
}
需要注意的是,AirCode 云函数中的时区都是 UTC±0。当利用时间区间查询时,可以使用 dayjs 来方便的进行时区转换。
例如,我们希望查询 America/New_York 时区中,2023 年 5 月 1 日 0 点到 2023 年 5 月 2 日 0 点之间创建的数据:
const aircode = require('aircode');
// Require `dayjs` and its plugins to support custom timezone
const dayjs = require('dayjs');
const utc = require('dayjs/plugin/utc');
const timezone = require('dayjs/plugin/timezone');
module.exports = async function (params, context) {
// All operators are nested in `aircode.db`
const { db } = aircode;
// Use `db.table` to get a table
const InventoryTable = db.table('inventory');
// Use `dayjs` to convert local time
const from = dayjs.tz('2023-05-01 00:00:00', 'America/New_York').toDate();
const to = dayjs.tz('2023-05-02 00:00:00', 'America/New_York').toDate();
// Use `Date` object as the conditions
const result = await InventoryTable
.where({ createdAt: db.gt(from).lte(to) })
.find();
return {
result,
};
}
const aircode = require('aircode');
// Require `dayjs` and its plugins to support custom timezone
const dayjs = require('dayjs');
const utc = require('dayjs/plugin/utc');
const timezone = require('dayjs/plugin/timezone');
module.exports = async function (params, context) {
// All operators are nested in `aircode.db`
const { db } = aircode;
// Use `db.table` to get a table
const InventoryTable = db.table('inventory');
// Use `dayjs` to convert local time
const from = dayjs.tz('2023-05-01 00:00:00', 'America/New_York').toDate();
const to = dayjs.tz('2023-05-02 00:00:00', 'America/New_York').toDate();
// Use `Date` object as the conditions
const result = await InventoryTable
.where({ createdAt: db.gt(from).lte(to) })
.find();
return {
result,
};
}
使用内嵌字段查询
当数据库中的某个字段是 Object
类型时,使用 .
连接符可以设置内嵌字段的查询条件。例如 where({ 'a.b': value })
会根据 a
的子字段 b
的值类查询。使用多个 .
来连接时,还可以支持多层嵌套。
const aircode = require('aircode');
module.exports = async function(params, context) {
// Use `aircode.db.table` to get a table
const InventoryTable = aircode.db.table('inventory');
// Use `.` to set condition for nesting field
const result = await InventoryTable
.where({ 'info.location': 'Beijing' })
.find();
return {
result,
};
}
const aircode = require('aircode');
module.exports = async function(params, context) {
// Use `aircode.db.table` to get a table
const InventoryTable = aircode.db.table('inventory');
// Use `.` to set condition for nesting field
const result = await InventoryTable
.where({ 'info.location': 'Beijing' })
.find();
return {
result,
};
}
注意
根据 JavaScript 的语法限制,使用 .
连接时键值必须添加引号。
使用逻辑操作符组合多个查询条件
AirCode 支持「与」、「或」、「或非」三种逻辑关系,也可以通过组合形成更复杂的逻辑条件。
「与」关系
向 where
方法中传入多个条件时,各个条件之间是「与」的关系。也可以使用 and
链式调用来表示。
// f1 && f2
Table.where(f1, f2);
Table.where(f1).and(f2);
// f1 && f2
Table.where(f1, f2);
Table.where(f1).and(f2);
「或」关系
在 where
方法后接上 or
方法的链式调用,则表示「或」的关系。
// f1 || f2
Table.where(f1).or(f2);
// f1 || f2
Table.where(f1).or(f2);
「或非」关系
在 where
方法后接上 nor
方法的链式调用,则表示「或非」的关系。
// !(f1 || f2)
Table.where(f1).nor(f2)
// !(f1 || f2)
Table.where(f1).nor(f2)
组合关系
当需要将多个逻辑关系组合起来时,可以通过链式调用的形式来实现。链式调用的顺序及括号逻辑都与表达式保持一致。
// (f1 && f2) || (f2 || f3) && (f4 && f5)
Table.where(f1, f2).or(f3, f4).and(f5, f6)
// (f1 && f2) || (f2 || f3) && (f4 && f5)
Table.where(f1, f2).or(f3, f4).and(f5, f6)
然而,有些时候逻辑表达式可能更复杂,无法单独用链式表达来解决。因此我们还在 aircode.db
对象上也提供了 and
、or
、nor
三个方法,用来表达这些条件。
// (f1 || f2) && !(f3 || f4) || (f5 && f6)
const { db } = aircode;
Table.where(db.or(f1, f2)).and(db.nor(f3, f4)).or(db.and(f5, f6));
// (f1 || f2) && !(f3 || f4) || (f5 && f6)
const { db } = aircode;
Table.where(db.or(f1, f2)).and(db.nor(f3, f4)).or(db.and(f5, f6));
返回查询结果总条数
使用 count()
时,可以只获取符合条件的记录的总条数,而不返回具体的数据内容。
const aircode = require('aircode');
module.exports = async function(params, context) {
// Use `aircode.db.table` to get a table
const InventoryTable = aircode.db.table('inventory');
// Use `count` to get the count of records
const count = await InventoryTable
.where({ 'info.location': 'Beijing' })
.count();
return {
count,
};
}
const aircode = require('aircode');
module.exports = async function(params, context) {
// Use `aircode.db.table` to get a table
const InventoryTable = aircode.db.table('inventory');
// Use `count` to get the count of records
const count = await InventoryTable
.where({ 'info.location': 'Beijing' })
.count();
return {
count,
};
}
排序和分页
有时候,要查询的记录条数太多,我们可能希望对结果进行排序并分页返回,这时候需要使用到 sort
、skip
和 limit
。
- 使用
sort({ field: order })
指定按某个字段排序order
为1
或'asc'
,代表正序,即从小到大排序order
为-1
或'desc'
,代表倒序,即从大到小排序
- 使用
skip(n)
跳过n
条记录 - 使用
limit(n)
指定只返回n
条记录
const aircode = require('aircode');
module.exports = async function(params, context) {
// Use `aircode.db.table` to get a table
const InventoryTable = aircode.db.table('inventory');
// use `page` and `pageSize` to filter to result
const pageSize = 3;
const page = 2;
const result = await InventoryTable
.where()
.sort({ qty: -1 }) // sort by `qty` in desc order
.skip((page - 1) * pageSize)
.limit(pageSize)
.find();
return {
result,
};
}
const aircode = require('aircode');
module.exports = async function(params, context) {
// Use `aircode.db.table` to get a table
const InventoryTable = aircode.db.table('inventory');
// use `page` and `pageSize` to filter to result
const pageSize = 3;
const page = 2;
const result = await InventoryTable
.where()
.sort({ qty: -1 }) // sort by `qty` in desc order
.skip((page - 1) * pageSize)
.limit(pageSize)
.find();
return {
result,
};
}
提示
sort
还支持按照多个字段排序,此时会按照字段传递的顺序会非常重要。
例如 sort({ foo: 1, bar: -1 })
会先按照 foo
字段正序排列,对于 foo
字段值相同的记录,再按照 bar
字段逆序排列。
指定结果只包含特定字段
默认情况下查询结果会包含这个记录的所有字段,如果希望省略某些无关的字段,可以使用 projection({ field: value })
来指定。
value
为1
时,代表仅包含该字段,其余字段均被忽略,_id
字段为特例,默认会返回value
为0
时,代表忽略该字段,返回其余字段
const aircode = require('aircode');
module.exports = async function(params, context) {
// Use `aircode.db.table` to get a table
const InventoryTable = aircode.db.table('inventory');
// Return fields include `_id` and `item`
const includeResult = await InventoryTable
.where()
.projection({ item: 1 })
.find();
// Return fields exclude `qty` and `info.location`
const excludeResult = await InventoryTable
.where()
.projection({ qty: 0, 'info.location': 0 })
.find();
return {
includeResult,
excludeResult,
};
}
const aircode = require('aircode');
module.exports = async function(params, context) {
// Use `aircode.db.table` to get a table
const InventoryTable = aircode.db.table('inventory');
// Return fields include `_id` and `item`
const includeResult = await InventoryTable
.where()
.projection({ item: 1 })
.find();
// Return fields exclude `qty` and `info.location`
const excludeResult = await InventoryTable
.where()
.projection({ qty: 0, 'info.location': 0 })
.find();
return {
includeResult,
excludeResult,
};
}
为了便于查询之后的操作,默认情况下返回结果都会包含 _id
字段。若希望过滤掉 _id
字段,需要显示地设置 _id: 0
。
注意
若返回结果无 _id
字段,则无法在后续代码中使用 save
或 delete
来执行更新或删除操作。
const aircode = require('aircode');
module.exports = async function(params, context) {
// Use `aircode.db.table` to get a table
const InventoryTable = aircode.db.table('inventory');
// Set `_id: 0` to exclude it
const resultWithoutId = await InventoryTable
.where()
.projection({ item: 1, _id: 0 })
.find();
// Record without id can not be passed to `delete`
// The following code will cause an error
// await InventoryTable.delete(resultWithoutId);
return {
resultWithoutId
};
}
const aircode = require('aircode');
module.exports = async function(params, context) {
// Use `aircode.db.table` to get a table
const InventoryTable = aircode.db.table('inventory');
// Set `_id: 0` to exclude it
const resultWithoutId = await InventoryTable
.where()
.projection({ item: 1, _id: 0 })
.find();
// Record without id can not be passed to `delete`
// The following code will cause an error
// await InventoryTable.delete(resultWithoutId);
return {
resultWithoutId
};
}
更多查询条件
完整的数据库查询条件及操作符请参考数据库 API。