3

My Data-Structure looks like this:

[
  {
    "_id": "1",
    "title": "Yamaha",
    "data": "Sed ut perspiciatis",
    "type": "Bike"
  },
  {
    "_id": "2",
    "title": "Pulsar",
    "data": "Quis autem vel eum",
    "type": "Bike"
  },
  {
    "_id": "3",
    "title": "Tesla Model Y",
    "data": "because it is pleasure",
    "type": "Car"
  },
  {
    "_id": "4",
    "title": "Harley-Davidson",
    "data": "praising pain was born",
    "type": "Bike"
  },
  {
    "_id": "6",
    "title": "Mustang",
    "data": "non numquam eius",
    "type": "Car"
  },
  {
    "_id": "7",
    "title": "BMW",
    "data": "Man of Culture",
    "type": "Car"
  }
]

Now, From FrontEnd Users Can Search any of the item from database using their unique _id, Like this:

db.collection.find({_id: "3" })

Which returns the following:

[
  {
    "_id": "3",
    "data": "because it is pleasure",
    "title": "Tesla Model Y",
    "type": "Car"
  }
]

Question Part:

Now, Including the above-returned document, I also want to return those documents which have it's the matching type value.


My Questions means that; if the user is finding any document with their particular _id. let's suppose 3 then it should return the following:

Find the Item with their Unique _id and $group the type field Value

  [{
    "_id": "3",
    "title": "Tesla Model Y",
    "data": "because it is pleasure",
    "type": "Car"
  }
  {
    "_id": "6",
    "title": "Mustang",
    "data": "non numquam eius",
    "type": "Car"
  },
  {
    "_id": "7",
    "title": "BMW",
    "data": "Man of Culture",
    "type": "Car"
  }]

Is that possible to do? Is that possible to $group the document after finding By Id ?. I've tried Several Ways to make it but each of them is useless. Any Suggestions will be HelpFul for this complicated Requirement

:)

Vishal Sharma
  • 99
  • 1
  • 9

2 Answers2

3

Query

  • lookup with it self, join only with the type that the id=3 has.
  • empty join results => different type so they are filtered out

Test code here

db.collection.aggregate([
  {
    "$lookup": {
      "from": "collection",
      "let": {
        "type": "$type"
      },
      "pipeline": [
        {
          "$match": {
            "$expr": {
              "$and": [
                {
                  "$eq": [
                    "$_id",
                    "3"
                  ]
                },
                {
                  "$eq": [
                    "$$type",
                    "$type"
                  ]
                }
              ]
            }
          }
        }
      ],
      "as": "joined"
    }
  },
  {
    "$match": {
      "$expr": {
        "$ne": [
          "$joined",
          []
        ]
      }
    }
  },
  {
    "$unset": [
      "joined"
    ]
  }
])
Takis
  • 8,314
  • 2
  • 14
  • 25
  • 1
    Holly crap, I never thought anyone would spend the time to do that! :D Even though I'd say it's an awful query that would be extremely slow (as it creates an object for each entry and then filters it by comparison), kudos for the time spend writing it :) – Andrey Popov Sep 08 '21 at 19:06
  • 1
    its collection scan, but the lookup can use index. I dont think we can do it with 1 query and without collection scan because we dont know what we are looking for. – Takis Sep 08 '21 at 20:06
  • @Takis_ Thanks for the answer. But the second **$match** stage **(** after the `$lookup` **)** not showing any results in MongoDB Atlas `Aggegration Pipeline Builder`. But it's working in the playground. Why? – Vishal Sharma Sep 10 '21 at 11:55
  • @Takis_ I've also seen this solution: [Link](https://stackoverflow.com/a/25142571/16702515), But Second **$match** doesn't working. Have you any idea ? – Vishal Sharma Sep 10 '21 at 12:28
  • i dont know, i tested it on my driver and after on playground – Takis Sep 10 '21 at 13:15
  • @Takis_ Look, Here's 4 Image Source. It's works well for **$lookup** but not after that any stages. **`$lookup`:** [Img1](https://i.stack.imgur.com/3roic.png), [Img2](https://i.stack.imgur.com/7ey5R.png) . But for `$match` after **$lookup** not returning [Img1](https://i.stack.imgur.com/bZyvZ.png) and [Img2](https://i.stack.imgur.com/6Br5U.png) – Vishal Sharma Sep 10 '21 at 13:20
  • you have to check the version, i think its the $unset, replace it with `{$project : {"joined" : 0}}` – Takis Sep 10 '21 at 13:24
  • @Takis_ The problem is with `{"$match": { "$expr": { "$ne" : [ "$joined" ,[] ]}} }`. $unset and $project is fine. I've tested it. Any idea ;( – Vishal Sharma Sep 10 '21 at 13:33
  • @Takis_ I've exactly Copied and Pasted the aggregation and imported it into `Text to Pipeline`. And the version I'm using is **4.4.8** – Vishal Sharma Sep 10 '21 at 13:40
  • i cant think of a reason, if the join works and the joined is not always empty, you should get results, the match could be written also like `{"$match" : { "joined": { $ne: [] } }}` but i dont think this is the problem. – Takis Sep 10 '21 at 13:54
-1

You're basically mixing two separate queries:

  1. Get an item by ID - returns a single item
  2. Get a list of items, that have the same type as the type of the first item - returns a list of items

Because of the difference of the queries, there's no super straightforward way to do so. Surely you can use $aggregate to do the trick, but logic wise you'd still query quite a bit from the database, and you'd have to dig deeper to optimize it properly.

As long as you're not querying tens of millions of records, I'd suggest you do the two queries one after another, for the sake of simplicity.

Andrey Popov
  • 7,362
  • 4
  • 38
  • 58