基于 firestore 中的多个字段进行查询
Querying based on multiple fields from firestore
我有一个如下所示的“黑客马拉松”模型:
我需要对数据进行分页,但是现在我首先根据amountInPrizes范围和用户在前端设置的标签列表查询数据库,然后根据其余部分手动过滤数组用户指定的字段(因为 firestore 不允许在不同字段上进行多个不等式查询,我找不到其他方法)
路线如下:
const {minPrize, maxPrize, inPerson, online, startDateString, endDateString, tags} = req.query;
try {
let hackathonQuery = db.collection("hackathons");
if (minPrize && minPrize !== "") {
hackathonQuery = hackathonQuery.where("amountInPrizes", ">=", Number.parseInt(minPrize));
}
if (maxPrize && maxPrize !== "") {
hackathonQuery = hackathonQuery.where("amountInPrizes", "<=", Number.parseInt(maxPrize));
}
if (tags && tags !== "") {
const tagsList = tags.split(",");
if (tagsList.length >= 1) {
hackathonQuery = hackathonQuery.where("tags", "array-contains-any", tagsList);
}
}
// this is where the pagination should be, but there's still a bunch of manual filters after, so the pagination wouldn't be accurate
const hackathonsSnapshot = await hackathonQuery.orderBy("amountInPrizes", "desc").get();
const hackathons = [];
hackathonsSnapshot.forEach(doc => {
const hackathon = doc.data();
if (startDateString && startDateString !== "") {
const startDate = new Date(startDateString);
if (new Date(hackathon.startDate).getTime() < startDate.getTime()) {
return;
}
}
if (endDateString && endDateString !== "") {
const endDate = new Date(endDateString);
if (new Date(hackathon.endDate).getTime() > endDate.getTime()) {
return;
}
}
if (inPerson && inPerson === "true") {
// to make it so that if both are selected it queries correctly
if (online !== "true") {
if (hackathon.location == "Online") {
return;
}
}
}
if (online && online === "true") {
// to make it so that if both are selected it queries correctly
if (inPerson !== "true") {
if (hackathon.location !== "Online") {
return;
}
}
}
hackathons.push({
id: doc.id,
...hackathon,
})
})
return res.status(200).json({hackathons});
} catch (err) {
console.log(err.message);
res.status(400).send("Server error");
}
如果我要在初始查询中添加 .limit(),则黑客马拉松每一页上的其余手动过滤器都会过滤掉数量不一致的文档。有没有解决这个问题的方法或更好的方法来完成这整个事情?
(抱歉,如果解释令人困惑)
如果您真的需要完全灵活的用户输入,那就别无选择。
如果您可以更改模型并稍微更改用户界面,那么您可以做一些事情:
在模型中添加 onlineLocation: boolean 并在保存文档时根据数据将其设置为 true/false。这将消除您的在线过滤,并允许您在查询中添加一个 where...(您将不需要使用不等式)。
日期范围 - 据我了解,用户可以 select 开始日期。如果更改界面以允许用户从预定义值(例如:上周、上个月、去年...等)select 是可以接受的,您可以做的是除了开始日期之外还可以保存:
- startDayIndex = Math.floor(startDate.getTime() / millisecondsInDay)
这导致自 1970 年 1 月 1 日以来的日指数
- startWeekIndex = Math.floor(startDate.getTime() / millisecondsInWeek)
这导致自 1970 年 1 月 1 日以来的周指数
ETC...
这将允许您使用相等查询来查询 firestore(获取开始日期为 3 周前的所有文档)
我不确定这是否适用于您的用例。
如果日期范围保持原样很重要,您可以将 amountInPrizes 更改为范围而不是数字。这样用户就可以获得例如 amountInPrizes 在 1000-2000 之间的所有文档。
您将需要再次向您的模型添加一个字段,例如:prizeRange,并使用相等查询查询该字段,将日期保留在范围查询中
我有一个如下所示的“黑客马拉松”模型:
我需要对数据进行分页,但是现在我首先根据amountInPrizes范围和用户在前端设置的标签列表查询数据库,然后根据其余部分手动过滤数组用户指定的字段(因为 firestore 不允许在不同字段上进行多个不等式查询,我找不到其他方法)
路线如下:
const {minPrize, maxPrize, inPerson, online, startDateString, endDateString, tags} = req.query;
try {
let hackathonQuery = db.collection("hackathons");
if (minPrize && minPrize !== "") {
hackathonQuery = hackathonQuery.where("amountInPrizes", ">=", Number.parseInt(minPrize));
}
if (maxPrize && maxPrize !== "") {
hackathonQuery = hackathonQuery.where("amountInPrizes", "<=", Number.parseInt(maxPrize));
}
if (tags && tags !== "") {
const tagsList = tags.split(",");
if (tagsList.length >= 1) {
hackathonQuery = hackathonQuery.where("tags", "array-contains-any", tagsList);
}
}
// this is where the pagination should be, but there's still a bunch of manual filters after, so the pagination wouldn't be accurate
const hackathonsSnapshot = await hackathonQuery.orderBy("amountInPrizes", "desc").get();
const hackathons = [];
hackathonsSnapshot.forEach(doc => {
const hackathon = doc.data();
if (startDateString && startDateString !== "") {
const startDate = new Date(startDateString);
if (new Date(hackathon.startDate).getTime() < startDate.getTime()) {
return;
}
}
if (endDateString && endDateString !== "") {
const endDate = new Date(endDateString);
if (new Date(hackathon.endDate).getTime() > endDate.getTime()) {
return;
}
}
if (inPerson && inPerson === "true") {
// to make it so that if both are selected it queries correctly
if (online !== "true") {
if (hackathon.location == "Online") {
return;
}
}
}
if (online && online === "true") {
// to make it so that if both are selected it queries correctly
if (inPerson !== "true") {
if (hackathon.location !== "Online") {
return;
}
}
}
hackathons.push({
id: doc.id,
...hackathon,
})
})
return res.status(200).json({hackathons});
} catch (err) {
console.log(err.message);
res.status(400).send("Server error");
}
如果我要在初始查询中添加 .limit(),则黑客马拉松每一页上的其余手动过滤器都会过滤掉数量不一致的文档。有没有解决这个问题的方法或更好的方法来完成这整个事情?
(抱歉,如果解释令人困惑)
如果您真的需要完全灵活的用户输入,那就别无选择。 如果您可以更改模型并稍微更改用户界面,那么您可以做一些事情:
在模型中添加 onlineLocation: boolean 并在保存文档时根据数据将其设置为 true/false。这将消除您的在线过滤,并允许您在查询中添加一个 where...(您将不需要使用不等式)。
日期范围 - 据我了解,用户可以 select 开始日期。如果更改界面以允许用户从预定义值(例如:上周、上个月、去年...等)select 是可以接受的,您可以做的是除了开始日期之外还可以保存:
- startDayIndex = Math.floor(startDate.getTime() / millisecondsInDay) 这导致自 1970 年 1 月 1 日以来的日指数
- startWeekIndex = Math.floor(startDate.getTime() / millisecondsInWeek) 这导致自 1970 年 1 月 1 日以来的周指数 ETC... 这将允许您使用相等查询来查询 firestore(获取开始日期为 3 周前的所有文档)
我不确定这是否适用于您的用例。 如果日期范围保持原样很重要,您可以将 amountInPrizes 更改为范围而不是数字。这样用户就可以获得例如 amountInPrizes 在 1000-2000 之间的所有文档。 您将需要再次向您的模型添加一个字段,例如:prizeRange,并使用相等查询查询该字段,将日期保留在范围查询中