skip to Main Content

I am trying to sort the products array based on the createdAt date.

My orders model

const itemSchema = new Schema(
    {
        productId: { type: Schema.Types.ObjectId, ref: 'Product' },
        quantity: {
            type: Number,
            max: [10, 'Only 10 quantity of one item can be added.'],
            required: true,
        },
        price: { type: Number, required: true },
    },
    { timestamps: true }
);

const ordersSchema = new Schema({
    products: [itemSchema],
    userId: { type: Schema.Types.ObjectId, ref: 'User' },
});

I tried this according to a stack overflow post

 const orders = await Order.findOne({ userId: req.userId })
        .sort({ 'products.createdAt': -1 })
        .slice('products', 10)
        .populate({ path: 'products.productId', select: 'images title' });

But it does not return sorted array.

2

Answers


  1. db.collection.aggregate([
      {
        "$unwind": "$products"
      },
      {
        "$sort": {
          "products.createdAt": -1
        }
      },
      {
        "$group": {
          _id: "$_id",
          products: {
            "$push": "$products"
          }
        }
      }
    ])
    

    mongoplayground

    Input:

    [
      {
        userId: "1",
        products: [
          {
            productId: "1",
            createdAt: "2023-07-02"
          },
          {
            productId: "2",
            createdAt: "2023-07-01"
          },
          {
            productId: "3",
            createdAt: "2023-07-03"
          },
          
        ]
      }
    ]
    

    Output:

    [
      {
        "_id": ObjectId("5a934e000102030405000000"),
        "products": [
          {
            "createdAt": "2023-07-03",
            "productId": "3"
          },
          {
            "createdAt": "2023-07-02",
            "productId": "1"
          },
          {
            "createdAt": "2023-07-01",
            "productId": "2"
          }
        ]
      }
    ]
    

    Mongoose(v7.3.4) example :

    import mongoose from 'mongoose';
    import util from 'util';
    import { config } from '../../config';
    
    mongoose.set('debug', true);
    console.log(mongoose.version);
    
    const productSchema = new mongoose.Schema({
        title: String,
        images: [],
    });
    const Product = mongoose.model('Product', productSchema);
    
    const itemSchema = new mongoose.Schema(
        {
            productId: { type: mongoose.Schema.Types.ObjectId, ref: 'Product' },
        },
        { timestamps: true },
    );
    const Item = mongoose.model('Item', itemSchema);
    
    const ordersSchema = new mongoose.Schema({
        products: [itemSchema],
    });
    const Orders = mongoose.model('Orders', ordersSchema);
    
    (async function main() {
        try {
            await mongoose.connect(config.MONGODB_URI);
            await Promise.all([Product, Item, Orders].map((m) => m.collection.drop()));
            // seed
            const [p1, p2, p3] = await Product.create([
                { title: 'product a', images: ['a', 'b'] },
                { title: 'product b', images: ['c', 'd'] },
                { title: 'product c', images: ['e', 'f'] },
            ]);
            const items = await Item.create([
                { productId: p1, createdAt: new Date(2023, 6, 4) },
                { productId: p3, createdAt: new Date(2023, 6, 3) },
                { productId: p2, createdAt: new Date(2023, 6, 5) },
            ]);
            const [o1] = await Orders.create([{ products: items }]);
    
            // test
            const r = await Orders.aggregate()
                .match({ _id: o1?._id })
                .unwind('products')
                .lookup({
                    from: 'products',
                    localField: 'products.productId',
                    foreignField: '_id',
                    as: 'lookup_products',
                })
                .unwind('lookup_products')
                .addFields({ 'products.title': '$lookup_products.title', 'products.images': '$lookup_products.images' })
                .sort({ 'products.createdAt': -1 })
                .group({ _id: '$_id', products: { $push: '$products' } })
                .project({ lookup_products: 0 });
    
            console.log(util.inspect(r, false, null));
        } catch (error) {
            console.error(error);
        } finally {
            await mongoose.connection.close();
        }
    })();
    

    Logs:

    [
      {
        _id: new ObjectId("64b4e66fdafc40c3caae9ee5"),
        products: [
          {
            productId: new ObjectId("64b4e66edafc40c3caae9eda"),
            _id: new ObjectId("64b4e66fdafc40c3caae9ee1"),
            createdAt: 2023-07-04T16:00:00.000Z,
            updatedAt: 2023-07-04T16:00:00.000Z,
            __v: 0,
            title: 'product b',
            images: [ 'c', 'd' ]
          },
          {
            productId: new ObjectId("64b4e66edafc40c3caae9ed9"),
            _id: new ObjectId("64b4e66fdafc40c3caae9edf"),
            createdAt: 2023-07-03T16:00:00.000Z,
            updatedAt: 2023-07-03T16:00:00.000Z,
            __v: 0,
            title: 'product a',
            images: [ 'a', 'b' ]
          },
          {
            productId: new ObjectId("64b4e66edafc40c3caae9edb"),
            _id: new ObjectId("64b4e66fdafc40c3caae9ee0"),
            createdAt: 2023-07-02T16:00:00.000Z,
            updatedAt: 2023-07-02T16:00:00.000Z,
            __v: 0,
            title: 'product c',
            images: [ 'e', 'f' ]
          }
        ]
      }
    ]
    
    Login or Signup to reply.
  2. If you run current version (6.0) of MongoDB, then you can use $sortArray:

    db.collection.aggregate([
      {
        $set: {
          products: {
            $sortArray: {
              input: "$products",
              sortBy: { createdAt: 1 }
            }
          }
        }
      }
    ])
    

    Mongo Playground

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search