Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Mongodb aggregate – Sort then get surrounding documents

I’m trying to write a mongodb aggregate to apply a 2-level sort and then get the 2 documents before and after a particular document in the sorted set. To start, I’m trying to build a query for the "after" document

Documents are inserted out of order:

db.test.insertMany([
   { name: 'V', type: 'Trap' }, 
   { name: 'J', type: 'Magic' },
   { name: 'B', type: 'Effect' }
])

To sort alphabetically within each type I can do:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

db.test.aggregate([{ "$sort": { type: 1, name: 1 } }])

Result:
{ name: 'B', type: 'Effect' }
{ name: 'J', type: 'Magic' }
{ name: 'V', type: 'Trap' }

I’m trying to use the following query (with replacements) to get the "after" document:

[
  { $sort: { type: 1, name: 1 } },
  {
    $match: {
      name: {
        $gt: <name>,
      },
      type: {
        $gte: <type>,
      },
    },
  },
  { $limit: 1 }
]

For example:

Document { name: 'K', type: 'Trap' } should go between J & V.

  • The above query correctly gives "V".

Document { name: 'C', type: 'Effect' } should go between B & J.

  • The above query correctly gives "J".

However, document{ name: 'K', type: 'Effect' } also needs to go between B & J since it should remain in the "Effect" grouping.

  • The query incorrectly returns "V" again (should be J).

Removing either of the $match properties gives incorrect results in other cases. Can’t seem to use $and/or to get correct results in all cases either

For the reverse, my "before" query is as following (not working in all cases):

{ $sort: { type: -1, name: -1 } },
            {
               $match: {
                  name: { $lt: card.name },
                  type: { $lte: card.type }
               }
            },
{ $limit: 1 }

>Solution :

can write a match query which checks an or condition where check type is greater than the given type or if the type is equal to the given type then check if the name is greater than the given name. To get the before document replace gt with lt

db.collection.aggregate([
  { $sort: { type: 1, name: 1 } },
  {
    $match: {
      $or: [
        { type: { $gt: <type> } },
        { $and: [{ type: <type> }, { name: { $gt: <name> } }] }
      ]
    }
  },
  { $limit: 1 }
])

playground

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading