简体   繁体   中英

Mongoose: find() - match two values and return only the documents matched

I've looked around and I can't find an exact solution to this problem. I've looked through the Mongoose docs and tried to find a way. I'm new to MongoDB and wanted to aim to make my own API after following a lot of tutorials.

The Aim:

GET request to http://localhost:3000/seasons/19/1 and return the series details 19 and the episode selected 1

Something like:

{
        "_id": "5b893aff76c12c166c4e75ae",
        "season_number": 19,
        "name": "Season 19",
        "number_of_episodes": 24,
        "episode": [
            {
                "_id": "5b895e56089a9a152c16af1f",
                "episode_number": 1,
                "name": "19.01",
                "length": "1:23:33",
                "author": "Aaron Wright",
                "release": "2018-01-13T00:00:00.000Z",
                "description": "We kick off our 2018 season with the worst of CES and the future of smart toilets before Aaron unravels the controversial “Dear David” saga that has sparked controversy. We then look at state sponsored mind control programs and the hypnotic experiments performed on unwitting Princeton students in the 1960s."
            },

}

The Problem:

I can't seem to figure out how to return the object as above with just the one selected episode. As all the data is in episodes but I can return it if I manually use episodes[0] etc.

Here is the repo file so not to make this post too long: https://github.com/DannnB/mysterious-universe-api/blob/master/api/controllers/seasons.js

  • Line : 63
  • Name : seasons_get_episode_number

Thanks for any help!

The database - hosted on MongoDB Atlas - free tier so can't use $where and don't want to use $where as its a heavy command.

 "data": [
    {
        "_id": "5b893aff76c12c166c4e75ae",
        "season_number": 19,
        "name": "Season 19",
        "number_of_episodes": 24,
        "episodes": [
            {
                "_id": "5b895e56089a9a152c16af1f",
                "episode_number": 1,
                "name": "19.01",
                "length": "1:23:33",
                "author": "Aaron Wright",
                "release": "2018-01-13T00:00:00.000Z",
                "description": "We kick off our 2018 season with the worst of CES and the future of smart toilets before Aaron unravels the controversial “Dear David” saga that has sparked controversy. We then look at state sponsored mind control programs and the hypnotic experiments performed on unwitting Princeton students in the 1960s."
            },
            {
                "_id": "5b895fee089a9a152c16af20",
                "episode_number": 2,
                "name": "19.02",
                "length": "1:22:11",
                "author": "Benjamin Grundy",
                "release": "2018-01-20T00:00:00.000Z",
                "description": "This week we unravel the cosmic serpent to find the origins of life and the link between DNA and Ayahuasca visions. Could the building blocks of all life also be a conscious force that is capable of direct communication with our altered states of mind?"
            }
        ]
    },
    {
        "_id": "5b893b2276c12c166c4e75b0",
        "season_number": 20,
        "name": "Season 20",
        "number_of_episodes": 9
    }, *and so on...*
]

You can user $filter operator available on Aggregration . Here is the query that matches your requirement:

db.seasons.aggregate([
  { "$match": { "season_number": 19 } },
  { "$project": {
       "season_number": 1,
       "name": 1,
       "number_of_episodes": 1,
       "episodes": {
          "$filter": {
             "input":  "$episodes",
             "as": "episode",
             "cond": { "$eq": [ "$$episode.episode_number", 1 ] }
          }
       }
    } 
  }])

$filter is available from version 3.2

edit: by using $filter

found solution originally here

db.dumb.aggregate(
    {
        $match:{a:'ok', eps:{$elemMatch:{id:1}}}
    },
    {
        $project:{
            a:1, 
            eps:{
                $filter:{
                    input:'$eps', 
                    as:'ep', 
                    cond:{$eq:['$$ep.id',1]}
                }
            }
        }
    }
)

output

{ "_id" : ObjectId("5b8e3eb8e5ab5ef9a9b9b673"), "a" : "ok", "eps" : [ { "id" : 1, "comment" : "ok" } ] }
{ "_id" : ObjectId("5b8e3ec3e5ab5ef9a9b9b676"), "a" : "ok", "eps" : [ { "id" : 1, "comment" : "ok too" } ] }

maybe use unwind?

building a dataset

> db.dumb.insert({a:'ok', eps:[{id:1, comment('ok')}]})
WriteResult({ "nInserted" : 1 })
> db.dumb.insert({a:'ko', eps:[{id:1, comment:'ok'}]})
WriteResult({ "nInserted" : 1 })
> db.dumb.insert({a:'ok', eps:[{id:2, comment:'ko'}]})
WriteResult({ "nInserted" : 1 })
> db.dumb.insert({a:'ok', eps:[{id:2, comment:'ko'}, {id:1, comment:'ok too'}]})
WriteResult({ "nInserted" : 1 })

matching the serie (here named 'ok')

> db.dumb.find({a:'ok', eps:{$elemMatch:{id:1}}})
{ "_id" : ObjectId("5b8d447b21edbcdd7f3b3d24"), "a" : "ok", "eps" : [ { "id" : 1, "comment" : "ok" } ] }
{ "_id" : ObjectId("5b8d449d21edbcdd7f3b3d27"), "a" : "ok", "eps" : [ { "id" : 2, "comment" : "ko" }, { "id" : 1, "comment" : "ok too" } ] }

matching the movie of id 1 of the serie named 'ok' using unwind, every item of the array is "spread". Note the "dupplicate objectIds" (you are keeping track of your unwinded doc)

> db.dumb.aggregate({$match:{a:'ok'}},{$unwind:'$eps'})
{ "_id" : ObjectId("5b8d447b21edbcdd7f3b3d24"), "a" : "ok", "eps" : { "id" : 1, "comment" : "ok" } }
{ "_id" : ObjectId("5b8d449121edbcdd7f3b3d26"), "a" : "ok", "eps" : { "id" : 2, "comment" : "ko" } }
{ "_id" : ObjectId("5b8d449d21edbcdd7f3b3d27"), "a" : "ok", "eps" : { "id" : 2, "comment" : "ko" } }
{ "_id" : ObjectId("5b8d449d21edbcdd7f3b3d27"), "a" : "ok", "eps" : { "id" : 1, "comment" : "ok too" } }

standard match after

> db.dumb.aggregate({$match:{a:'ok'}},{$unwind:'$eps'},{$match:{'eps.id':1}})
{ "_id" : ObjectId("5b8d447b21edbcdd7f3b3d24"), "a" : "ok", "eps" : { "id" : 1, "comment" : "ok" } }
{ "_id" : ObjectId("5b8d449d21edbcdd7f3b3d27"), "a" : "ok", "eps" : { "id" : 1, "comment" : "ok too" } }
db.users.aggregate(

// Pipeline
[
    // Stage 1
    {
        $unwind: {
            path : "$episodes",
            preserveNullAndEmptyArrays : false // optional
        }
    },

    // Stage 2
    {
        $match: {
            "season_number" : 19.0,
            "episodes.episode_number" : 1
        }
    },

    // Stage 3
    {
        $group: {
            "_id":"$_id",
            "season_number" :{$last:"season_number"}, 
            "name" :{$last:"name"}, 
            "number_of_episodes" :{$last:"number_of_episodes"},
            "episodes":{$push:"$episodes"}
        }
    },
]);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM