Mike
Mike

Reputation: 423

optimization of MongoDB query

I have many collections in my MongoDB

My query is work's but it's take many many time. ~15 seconds when I get 20 records.

In my query I have ~11 lookups

is it possible that turning on indexing in collections (_id, appId) (<-- appId using in every collection) will help me to speed up this query?

or something is wrong in my query? thanks!

My query is:

const invoices = await Invoice.aggregate([
  { $match: query },
  { $unwind: "$items" },
  //products
  {
    $lookup: {
      from: "products",
      localField: "items.itemId",
      foreignField: "_id",
      as: "itemId",
    },
  },
  //colors
  {
    $lookup: {
      from: "colors",
      localField: "items.itemColor",
      foreignField: "_id",
      as: "itemColor",
    },
  },
  //sizes
  {
    $lookup: {
      from: "sizes",
      localField: "items.itemSize",
      foreignField: "_id",
      as: "itemSize",
    },
  },
  //taxes
  {
    $lookup: {
      from: "taxes",
      localField: "items.itemTax",
      foreignField: "_id",
      as: "itemTax",
    },
  },
  //shops
  {
    $lookup: {
      from: "shops",
      localField: "shop",
      foreignField: "_id",
      as: "shop",
    },
  },
  {
    $unwind: { path: "$shop", preserveNullAndEmptyArrays: true },
  },
  //shopTo
  {
    $lookup: {
      from: "shops",
      localField: "shopTo",
      foreignField: "_id",
      as: "shopTo",
    },
  },
  {
    $unwind: { path: "$shopTo", preserveNullAndEmptyArrays: true },
  },
  //moves_sup
  {
    $lookup: {
      from: "suppliers",
      localField: "supplier",
      foreignField: "_id",
      as: "moves_sup",
    },
  },
  {
    $unwind: { path: "$moves_sup", preserveNullAndEmptyArrays: true },
  },
  //moves_client
  {
    $lookup: {
      from: "clients",
      localField: "supplier",
      foreignField: "_id",
      as: "moves_client",
    },
  },
  {
    $unwind: { path: "$moves_client", preserveNullAndEmptyArrays: true },
  },
  //user
  {
    $lookup: {
      from: "users",
      localField: "user",
      foreignField: "_id",
      as: "user",
    },
  },
  {
    $unwind: { path: "$user", preserveNullAndEmptyArrays: true },
  },
  //editedBy
  {
    $lookup: {
      from: "users",
      localField: "editedBy",
      foreignField: "_id",
      as: "editedBy",
    },
  },
  {
    $unwind: { path: "$editedBy", preserveNullAndEmptyArrays: true },
  },
  //cat
  {
    $lookup: {
      from: "moneycategories",
      localField: "category",
      foreignField: "_id",
      as: "cat",
    },
  },
  {
    $unwind: { path: "$cat", preserveNullAndEmptyArrays: true },
  },
  {
    $addFields: {
      agentName: {
        $cond: [
          { $eq: [{ $type: "$moves_sup" }, "object"] },
          "$moves_sup.name",
          {
            $cond: [
              { $eq: [{ $type: "$moves_client" }, "object"] },
              "$moves_client.name",
              null,
            ],
          },
        ],
      },
      moves_client: "$$REMOVE",
      moves_sup: "$$REMOVE",
      "items.itemColorName": { $arrayElemAt: ["$itemColor.colorName", 0] },
      "items.itemSizeName": { $arrayElemAt: ["$itemSize.sizeName", 0] },
      "items.itemTaxName": { $arrayElemAt: ["$itemTax.name", 0] },
    },
  },
  {
    $group: {
      _id: "$_id",
      invoiceName: { $first: "$invoiceName" },
      invoiceNumber: { $first: "$invoiceNumber" },
      purchaseNumber: { $first: "$purchaseNumber" },
      date: { $first: "$date" },
      type: { $first: "$type" },
      // prMove: { $push: "$prMove" },
      agentName: { $first: "$agentName" },
      shopName: { $first: "$shop.shopName" },
      shopTo: {
        $first: "$shopTo.shopName",
      },
      supplier: { $first: "$supplier" },
      user: { $first: "$user.name" },
      category: { $first: "$cat" },
      categoryName: { $first: "$cat.categoryName" },
      editedBy: { $first: "$editedDate" },
      editedDate: { $first: "$editedDate" },
      comment: { $first: "comment" },
      addDate: { $first: "addDate" },
      items: { $push: "$items" },
    },
  },
])
  .sort({ date: -1 })
  .skip(+req.query.offset)
  .limit(+req.query.limit);

Upvotes: 1

Views: 114

Answers (1)

Wernfried Domscheit
Wernfried Domscheit

Reputation: 59456

If the lookup collections are not too big, you can try to replace them by using variables. For example stage

{
   $lookup: {
      from: "colors",
      localField: "items.itemColor",
      foreignField: "_id",
      as: "itemColor",
   }
}

could be converted to

const colors = await  db.colors.find({<maybe some filters if applicable>}).toArray();

{
   $set: {
      itemColor: {
         $filter: {
            input: colors,
            as: "color",
            cond: { $eq: ["$$color._id", "$items.itemColor"] }
         }
      }
   }
}

You could also try to skip some of the $unwind stages. Then above stage would be like this:

{
   $set: {
      items: {
         $map: {
            input: "$items",
            as: "item",
            in: {
               $mergeObjects: [
                  "$$item",
                  {
                     itemColor: {
                        $filter: {
                           input: colors,
                           as: "color",
                           cond: { $eq: ["$$color._id", "$$item.itemColor"] }
                        }
                     }
                  }
               ]
            }
         }
      }
   }
}

Upvotes: 1

Related Questions