mongodb查询以将子文档显示为主文档并使用它进行分页
Posted
技术标签:
【中文标题】mongodb查询以将子文档显示为主文档并使用它进行分页【英文标题】:mongodb query to show subdocument as maindocument and having pagination with it 【发布时间】:2021-02-06 03:38:31 【问题描述】:首先,我有这种类型的模型:
const produkSchema = new mongoose.Schema(
nama_produk: String,
etalase: type: mongoose.Schema.Types.ObjectID, ref: 'kategori',
kategori: type: mongoose.Schema.Types.ObjectID, ref: 'kategori',
jenis: type: mongoose.Schema.Types.ObjectID, ref: 'kategori.jenis',
bahan: String,
warna: String,
deskripsi: String,
foto_produk: [String],
harga: Number,
link_bukalapak: String,
link_shopee: String,
link_tokopedia: String,
link_lazada: String,
link_website: String,
display: type: Boolean, default: false,
,
weights:
nama_produk: 5,
,
timestamps: true
)
const tokoSchema = new mongoose.Schema(
username: type: String, trim: true,
password: type: String, required: true, select: false,
merek: String,
listMerek: [type: mongoose.Schema.Types.ObjectID, ref: 'produk'],
deskripsi: String,
follower: [type: mongoose.Schema.Types.ObjectID, ref: 'user'],
email: type: String, trim: true, unique: true,
instagram: String,
whatsapp: String,
website: String,
alamat: String,
foto_profil: String,
bukalapak: String,
shopee: String,
tokopedia: String,
fotoktp: String,
banner: [
gambar: type: String, required: true, trim: true,
// order: type: Number, required: true,
],
produk: [produkSchema],
etalase: [type: mongoose.Schema.Types.ObjectID, ref: 'kategori'],
// etalase: [
// kategori: type: mongoose.Schema.Types.ObjectID, ref: 'kategori',
// order: Number
// ],
approve: type: Number, default: 0, // 0: pending, 1: reject, 2: approve
populer: type: Boolean, default: false,
gambar_populer: [String],
pilihan: type: Boolean, default: false,
, timestamps: true);
我有一个端点可以使用以下代码过滤此produkSchema
:
exports.filterProduk = (req, res) =>
const merek, warna, kategori, jenis, hargaAwal, hargaAkhir, skip, limit = req.body
let query =
const $and = []
if (merek)
$and.push($or: merek.map(id => (_id: mongoose.Types.ObjectId(id))))
if (warna)
$and.push($or: warna.map(warna => ("produk.warna": warna)))
if (kategori)
query["produk.etalase"] = mongoose.Types.ObjectId(kategori)
if (jenis)
$and.push($or: jenis.map(id => ("produk.jenis": mongoose.Types.ObjectId(id))))
if (hargaAwal !== '')
query["produk.harga"] =
$gte: parseInt(hargaAwal),
if (hargaAkhir !== '')
query["produk.harga"] =
$lte: parseInt(hargaAkhir)
if ($and.length > 0)
query = $and, ...query
toko.aggregate([
$unwind: '$produk',
$match: query,
$lookup:
from: "kategoris",
as: "produk.etalase",
let: pjid: "$produk.jenis",
pipeline: [
$unwind: "$jenis",
$match: $expr: $eq: ["$$pjid", "$jenis._id"],
$project:
"jenis._id": 1,
"jenis.label": 1
]
,
$unwind: path: "$produk.etalase",
$group: _id: '$_id', produk: $push: '$produk', foto_profil: $first: '$foto_profil',
$limit: skip + limit,
$skip: skip
])
.then(async data =>
res.status(200).json(data, prefix: produk: "uploads/produk", toko: "uploads/toko")
)
实际结果是:
[
_id: blabla,
foto_profil: "blabla",
produk:[nama_produk: "blabla", bahan: "blabla", ...rest ProdukSchema as query]
,
_id: blabla,
foto_profil: "blabla",
produk:[nama_produk: "blabla", bahan: "blabla", ...rest ProdukSchema as query]
,
_id: blabla,
foto_profil: "blabla",
produk:[nama_produk: "blabla", bahan: "blabla", ...rest ProdukSchema as query]
]
预期:
[
nama_produk: "blabla", bahan: "blabla", ...rest ProdukSchema as query, foto_profil(from toko schema): "blabla",
nama_produk: "blabla", bahan: "blabla", ...rest ProdukSchema as query, foto_profil(from toko schema): "blabla"
]
并使用此produkSchema
进行分页(限制和偏移)
其实在此之前,我已经在here求过这个查询,但是这个查询会产生很多数据,需要分页,
我该怎么做?我应该将我的produkSchema
拆分为主子文档吗?或者是否存在针对这种情况的任何查询?
【问题讨论】:
@turivishal 非常好!两个 Playground 都与我的预期输出相同,但我需要此查询的跳过和限制,你能更新 Playground 吗? 【参考方案1】:我不确定确切的要求,但我可以猜到 2 个选项,
假设变量:let skip = 0;
let limit = 10;
第一个选项:
从末尾删除$group
阶段并在$unwind
阶段之后开始
$replaceRoot
合并对象 produk
和 foto_profil
使用 $mergeObjects
并替换根
$replaceRoot:
newRoot:
$mergeObjects: [ foto_profil: "$foto_profil" , "$produk"]
,
$skip: skip * limit ,
$limit: limit
Playground
第二个选项:
从末尾删除$group
阶段并在$unwind
阶段之后开始
$group
by toko id 和 produk id,这将分组 produk 并获得唯一的第一个 produk
$replaceRoot
合并对象 produk
和 foto_profil
使用 $mergeObjects
并替换根
$group:
_id:
_id: "$_id",
produk_id: "$produk._id"
,
root: $first: "$$ROOT"
,
$replaceRoot:
newRoot:
$mergeObjects: [ foto_profil: "$root.foto_profil" , "$root.produk"]
,
$skip: skip * limit ,
$limit: limit
Playground
【讨论】:
【参考方案2】:也许您可以在查找后执行此操作:
-
$unwind produk(其中 produk 仍为数组类型,然后展开它)。
$group: _id: nama_produk 等...
$project ...... 或任何你想做的事情。
这样您就可以通过 nama_produk 作为 _id 获取组列表。
【讨论】:
嗨@Fair,感谢您的回答,但我对您的回答有点困惑。您能否添加代码示例?无论如何,分页怎么样? 对不起,我没有注意到你已经在第一行 $unwind "produk"。重点是在您的 $group 上,您需要在“$group”上的“_id”中填写您要制作主文档的密钥。您写道,您的第一个字段预期输出是“nama_produk”,然后在 $group 上用“nama_produk”填写您的“_id”。 mongo 中的分页与 sql base 类似。在 sql 基础上,您使用偏移量和限制。在 mongo 中,您使用跳过和限制。或在使用 mongo 聚合时加 $ 符号。我以前从 req.query 客户端获取 limit & skip 的值,所以后端只发回客户端需要的值。以上是关于mongodb查询以将子文档显示为主文档并使用它进行分页的主要内容,如果未能解决你的问题,请参考以下文章