Ticket: Projection

I modified the moviesDAO.js and added the query and projection.
When I ran the test:
npm test -t projection

I got the following error:
Determining test suites to run…Setup Mongo Connection
FAIL test/projection.test.js
Projection
✓ Can perform a country search for one country (178ms)
✕ Can perform a country search for three countries (1179ms)

● Projection › Can perform a country search for three countries

expect(received).toEqual(expected)

Expected value to equal:
  2789
Received:
  2788

  15 |     const countriesList = ["Russia", "Japan", "Mexico"]
  16 |     const movies = await MoviesDAO.getMoviesByCountry(countriesList)
> 17 |     expect(movies.length).toEqual(2789)
     |                           ^
  18 |     movies.map(movie => {
  19 |       const movieKeys = Object.keys(movie).sort()
  20 |       const expectedKeys = ["_id", "title"]

  at toEqual (test/projection.test.js:17:27)
  at tryCatch (node_modules/regenerator-runtime/runtime.js:62:40)
  at Generator.invoke [as _invoke] (node_modules/regenerator-runtime/runtime.js:296:22)
  at Generator.prototype.(anonymous function) [as next] (node_modules/regenerator-runtime/runtime.js:114:21)
  at step (node_modules/babel-runtime/helpers/asyncToGenerator.js:17:30)
  at node_modules/babel-runtime/helpers/asyncToGenerator.js:28:13

Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 passed, 2 total
Snapshots: 0 total
Time: 3.441s, estimated 5s
Ran all test suites matching /projection/i.

However, when I ran the status page I got the correct code.

I wander is it a data issue or my code need to be fixed.

Thanks.
Richard

Your implementation is most likely correct. The underlying dataset was changed slightly, and the updated test files weren’t properly distributed. If you re-download the course handout, the tests directory has been updated for the new values. You can replace the existing tests directory.

2 Likes

I am a bit confused by this one. what are we supposed to do here. Are we trying to get all countries that have a title field to show only the title field and id? And the instructions are a bit confusing to me. And this is the error I am getting:

npm test -t projection                                                                      ⏎ ✭

> server@1.0.0 test /Users/mariacam/Development/mflix-js
> jest --passWithNoTests "projection"

Determining test suites to run...Setup Mongo Connection
 FAIL  test/projection.test.js (6.419s)
  Projection
    ✕ Can perform a country search for one country (11ms)
    ✕ Can perform a country search for three countries (1ms)

  ● Projection › Can perform a country search for one country

    expect(received).toEqual(expected)

    Expected value to equal:
      2
    Received:
      undefined

    Difference:

      Comparing two different types of values. Expected number but 
received undefined.

       9 |     const kosovoList = ["Kosovo"]
      10 |     const movies = await 
MoviesDAO.getMoviesByCountry(kosovoList)
    > 11 |     expect(movies.length).toEqual(2)
         |                           ^
      12 |   })
      13 |
      14 |   test("Can perform a country search for three countries", async 
    () => {

      at toEqual (test/projection.test.js:11:27)
      at tryCatch (node_modules/regenerator-runtime/runtime.js:62:40)
      at Generator.invoke [as _invoke] (node_modules/regenerator- 
runtime/runtime.js:296:22)
      at Generator.prototype.(anonymous function) [as next] 
(node_modules/regenerator-runtime/runtime.js:114:21)
      at step (node_modules/babel- 

runtime/helpers/asyncToGenerator.js:17:30)
at node_modules/babel-runtime/helpers/asyncToGenerator.js:28:13

  ● Projection › Can perform a country search for three countries

    expect(received).toEqual(expected)

    Expected value to equal:
      2788
    Received:
      undefined

    Difference:

      Comparing two different types of values. Expected number but 
received undefined.

      15 |     const countriesList = ["Russia", "Japan", "Mexico"]
      16 |     const movies = await 
MoviesDAO.getMoviesByCountry(countriesList)
    > 17 |     expect(movies.length).toEqual(2788)
         |                           ^
      18 |     movies.map(movie => {
      19 |       const movieKeys = Object.keys(movie).sort()
      20 |       const expectedKeys = ["_id", "title"]

      at toEqual (test/projection.test.js:17:27)
      at tryCatch (node_modules/regenerator-runtime/runtime.js:62:40)
      at Generator.invoke [as _invoke] (node_modules/regenerator- 
runtime/runtime.js:296:22)
  at Generator.prototype.(anonymous function) [as next] 
(node_modules/regenerator-runtime/runtime.js:114:21) at step 
(node_modules/babel-runtime/helpers/asyncToGenerator.js:17:30) at 
node_modules/babel-runtime/helpers/asyncToGenerator.js:28:13

Test Suites: 1 failed, 1 total
Tests:       2 failed, 2 total
Snapshots:   0 total
Time:        6.585s
Ran all test suites matching /projection/i.
Teardown Mongo Connection
npm ERR! Test failed.  See above for more details.

I think you have a wrong request with a projection

 try {
   // TODO Ticket: Projection
   // Find movies matching the "countries" list
   // and _id. Do not put a limit on your own implementation
   // here it is only included 46
   // wire.

   cursor = await movies.find (
     **your code wrong here**
   )

 } catch (e) {
   console.error (`Unable to issue find command, $ {e}`)
   return []
 }

Hi Oleg! I understand. But I delved deeper and checked to see what the deal was with the movie object before I even do anything with it, and I am getting undefined. I have never set up a project in this manner, so it’s throwing me off a bit. Thanks so much for your help. I’ll figure it out. I’m determined to get this down, because I should learn this!

Hi Maria! To see what your query returns, I suggest the following construct

  cursor = await movies.find(
    { ..... }, 
    { projection: { ..... } }
  ).limit(5)  // limit will need delete later

  console.log(await cursor.toArray()) // await because cursor return Promise

During the test, the content of the response will be displayed.%D0%A1%D0%BD%D0%B8%D0%BC%D0%BE%D0%BA%20%D1%8D%D0%BA%D1%80%D0%B0%D0%BD%D0%B0%20%D0%BE%D1%82%202019-02-14%2016-37-09

1 Like

This is what I now have in my code (I tried all sorts of variations on the theme, but always got cannot read property length of undefined => movies.length):

let cursor
    try {
      cursor = await movies.find({
        countries: {
          title: 1
        }
      }, {
        projection: {
          title: 1
        }
      }).map(movie => {
        return movie._id + movie.title
      })
      let result = await cursor.toArray()
      return {
        result
      }
    } catch (e) {
      // await because cursor returns Promise
      console.error(`Unable to issue find command, $ {e}`);
    }
  }

And this is what is returned in the console:

npm test -t projection                                                                     

server@1.0.0 test /Users/mariacam/Development/mflix-js
jest --passWithNoTests "projection"

Determining test suites to run...Setup Mongo Connection
FAIL  test/projection.test.js
Projection
✕ Can perform a country search for one country (95ms)
✕ Can perform a country search for three countries (71ms)

Projection › Can perform a country search for one country

expect(received).toEqual(expected)

Expected value to equal:
  2
Received:
undefined

Difference:

Comparing two different types of values. Expected number but received undefined.

const kosovoList = ["Kosovo"]
  10 |     const movies = await MoviesDAO.getMoviesByCountry(kosovoList)
> 11 |     expect(movies.length).toEqual(2)
     |                           ^
  12 |   })
  13 |
  14 |   test("Can perform a country search for three countries", async () => {

  at toEqual (test/projection.test.js:11:27)
  at tryCatch (node_modules/regenerator-runtime/runtime.js:62:40)
  at Generator.invoke [as _invoke] (node_modules/regenerator-runtime/runtime.js:296:22)
  at Generator.prototype.(anonymous function) [as next] (node_modules/regenerator-runtime/runtime.js:114:21)
  at step (node_modules/babel-runtime/helpers/asyncToGenerator.js:17:30)
  at node_modules/babel-runtime/helpers/asyncToGenerator.js:28:13

Projection › Can perform a country search for three countries

expect(received).toEqual(expected)

Expected value to equal:
  2788
Received:
  undefined

Difference:

Comparing two different types of values. Expected number but received undefined.
  const countriesList = ["Russia", "Japan", "Mexico"]
  const movies = await MoviesDAO.getMoviesByCountry(countriesList)
  expect(movies.length).toEqual(2788)
  movies.map(movie => {
  const movieKeys = Object.keys(movie).sort()
  const expectedKeys = ["_id", "title"]
  at toEqual (test/projection.test.js:17:27)
  at tryCatch (node_modules/regenerator-runtime/runtime.js:62:40)
  at Generator.invoke [as _invoke] (node_modules/regenerator-runtime/runtime.js:296:22)
  at Generator.prototype.(anonymous function) [as next] (node_modules/regenerator-runtime/runtime.js:114:21)
  at step (node_modules/babel-runtime/helpers/asyncToGenerator.js:17:30)
  at node_modules/babel-runtime/helpers/asyncToGenerator.js:28:13

Test Suites: 1 failed, 1 total
Tests:       2 failed, 2 total
Snapshots:   0 total
Time:        1.934s, estimated 2s
Ran all test suites matching /projection/i.
Teardown Mongo Connection
npm ERR! Test failed.  See above for more details.

And then I ran npm start and this is what I got in the JS console in the browser:

TypeError: t.movies.titles.map is not a function
at t.value (CountryResults.js:44)
at Ta (react-dom.production.min.js:3793)
at Ra (react-dom.production.min.js:3976)
at pi (react-dom.production.min.js:4621)
at hi (react-dom.production.min.js:4639)
at Yi (react-dom.production.min.js:4902)
at $i (react-dom.production.min.js:4860)
at qi (react-dom.production.min.js:4851)
at Bi (react-dom.production.min.js:4810)
at bi (react-dom.production.min.js:4738)

in react-dom.production.min.js:4200

And this is what I get in Terminal:

listening on port 5000
GET / 304 6.219 ms - -
GET /static/css/main.d2c98b4b.chunk.css 304 2.079 ms - -
GET /static/js/main.81b71a0a.chunk.js 304 1.545 ms - -
GET /static/js/1.85042e97.chunk.js 200 4.494 ms - 438087
GET /static/media/mongoleaf.0ebc1843.png 304 6.214 ms - -
GET /api/v1/movies/ 304 156.061 ms - -
GET /static/css/main.d2c98b4b.chunk.css 304 0.892 ms - -
GET /static/js/main.81b71a0a.chunk.js.map 200 1.202 ms - 312569
GET /static/js/1.85042e97.chunk.js.map 200 1.291 ms - 1798655
GET /static/css/main.d2c98b4b.chunk.css.map 200 0.798 ms - 20318
GET /api/v1/movies/search?page=1 304 89.310 ms - -
GET /api/v1/movies/search?text=title 200 100.639 ms - 35029
GET /api/v1/movies/countries?countries= 200 75.395 ms - 24
GET /static/js/main.81b71a0a.chunk.js.map 200 1.128 ms - 312569
GET /static/css/main.d2c98b4b.chunk.css.map 200 0.855 ms - 20318
GET /static/js/1.85042e97.chunk.js.map 200 0.961 ms - 1798655
GET /static/js/main.81b71a0a.chunk.js.map 200 1.135 ms - 312569
GET /static/css/main.d2c98b4b.chunk.css.map 200 1.067 ms - 20318
GET /static/js/1.85042e97.chunk.js.map 200 0.943 ms - 1798655

Remember, it’s expecting documents of the shape { _id, title }. Your map operation is changing this!

Aha. Ok, so now I changed it to the following:

let cursor
    try {
        cursor = await movies.find({
            countries: {
                 $in: countries
            }
        }, {
        projection: {
          title: 1
        }
      })
    } catch (e) {
      // await because cursor returns Promise
      console.error(`Unable to issue find command, $ {e}`);
    }
    console.log(await cursor.toArray());
}

And I get the following the Terminal console:

npm test -t projection                                                                     

> server@1.0.0 test /Users/mariacam/Development/mflix-js
> jest --passWithNoTests "projection"

Determining test suites to run...Setup Mongo Connection
console.log src/dao/moviesDAO.js:205
[ { _id: 573a13eff29313caabdd7dca, title: 'Sworn Virgin' },
  { _id: 573a13faf29313caabdec74f, title: 'Babai' } ]

FAIL  test/projection.test.js
Projection
✕ Can perform a country search for one country (119ms)
✕ Can perform a country search for three countries (320ms)

● Projection › Can perform a country search for one country

TypeError: Cannot read property 'length' of undefined

  const kosovoList = ["Kosovo"]
  const movies = await MoviesDAO.getMoviesByCountry(kosovoList)
      expect(movies.length).toEqual(2)
  })
  test("Can perform a country search for three countries", async () => {

  at length (test/projection.test.js:11:19)
  at tryCatch (node_modules/regenerator-runtime/runtime.js:62:40)
  at Generator.invoke [as _invoke] (node_modules/regenerator-runtime/runtime.js:296:22)
  at Generator.prototype.(anonymous function) [as next] (node_modules/regenerator-runtime/runtime.js:114:21)
  at step (node_modules/babel-runtime/helpers/asyncToGenerator.js:17:30)
  at node_modules/babel-runtime/helpers/asyncToGenerator.js:28:13

● Projection › Can perform a country search for three countries

TypeError: Cannot read property 'length' of undefined

  const countriesList = ["Russia", "Japan", "Mexico"]
  const movies = await MoviesDAO.getMoviesByCountry(countriesList)
  expect(movies.length).toEqual(2788)
  movies.map(movie => {
  const movieKeys = Object.keys(movie).sort()
  const expectedKeys = ["_id", "title"]

  at length (test/projection.test.js:17:19)
  at tryCatch (node_modules/regenerator-runtime/runtime.js:62:40)
  at Generator.invoke [as _invoke] (node_modules/regenerator-runtime/runtime.js:296:22)
  at Generator.prototype.(anonymous function) [as next] (node_modules/regenerator-runtime/runtime.js:114:21)
  at step (node_modules/babel-runtime/helpers/asyncToGenerator.js:17:30)
  at node_modules/babel-runtime/helpers/asyncToGenerator.js:28:13

Test Suites: 1 failed, 1 total
Tests:       2 failed, 2 total
Snapshots:   0 total
Time:        1.955s, estimated 3s
Ran all test suites matching /projection/i.
Teardown Mongo Connection
console.log src/dao/moviesDAO.js:205
[ { _id: 573a1390f29313caabcd471c,
    title: 'The Revenge of a Kinematograph Cameraman' },
  { _id: 573a1390f29313caabcd5715, title: 'Liliya Belgii' },
  { _id: 573a1390f29313caabcd5846, title: 'Posle smerti' },
  { _id: 573a1390f29313caabcd5d38, title: 'The Queen of Spades' },
  { _id: 573a1390f29313caabcd61e7, title: 'Father Sergius' },
  { _id: 573a1390f29313caabcd6277, title: 'Satan Triumphant' },
  { _id: 573a1391f29313caabcd83bd, title: 'A Page of Madness' },
  { _id: 573a1391f29313caabcd92fa, title: 'Walk Cheerfully' },
  { _id: 573a1391f29313caabcd9435, title: 'I Flunked, But...' },
  { _id: 573a1391f29313caabcd94ab, title: "That Night's Wife" },
  { _id: 573a1392f29313caabcd98de, title: 'Tokyo Chorus' },
  { _id: 573a1392f29313caabcd9be4, title: 'No Blood Relation' },
  { _id: 573a1392f29313caabcd9c97,
    title: 'Where Now Are the Dreams of Youth' },
  { _id: 573a1392f29313caabcd9d4f, title: 'I Was Born, But...' },
  { _id: 573a1392f29313caabcd9e63, title: 'Godfather Mendoza' },
  { _id: 573a1392f29313caabcd9e7c, title: 'Dekigokoro' },
  { _id: 573a1392f29313caabcd9f3c, title: 'Dragnet Girl' },
  { _id: 573a1392f29313caabcd9f9b, title: 'Apart from You' },
  { _id: 573a1392f29313caabcda17b, title: 'Woman of Tokyo' },
  { _id: 573a1392f29313caabcda1db, title: 'Yogoto no yume' },
  { _id: 573a1392f29313caabcda376,
    title: 'A Mother Should Be Loved' },
  { _id: 573a1392f29313caabcda3f2, title: 'Street Without End' },
  { _id: 573a1392f29313caabcda641,
    title: 'A Story of Floating Weeds' },
  { _id: 573a1392f29313caabcdaab8,
    title: 'Sazen Tange and the Pot Worth a Million Ryo' },
  { _id: 573a1392f29313caabcdaaf7, title: 'An Inn in Tokyo' },
  { _id: 573a1392f29313caabcdab08,
    title: 'Wife! Be Like a Rose!' },
  { _id: 573a1392f29313caabcdabde, title: 'Mr. Thank You' },
  { _id: 573a1392f29313caabcdad0d, title: 'Sisters of the Gion' },
  { _id: 573a1392f29313caabcdad6f, title: 'The Only Son' },
  { _id: 573a1392f29313caabcdae67, title: 'Osaka Elegy' },
  { _id: 573a1392f29313caabcdaf03, title: 'Redes' },
  { _id: 573a1392f29313caabcdb08b,
    title: 'The Straits of Love and Hate' },
  { _id: 573a1392f29313caabcdb29d, title: 'Children in the Wind' },
  { _id: 573a1392f29313caabcdb375,
    title: 'Humanity and Paper Balloons' },
  { _id: 573a1392f29313caabcdb471,
    title: 'What Did the Lady Forget?' },
  { _id: 573a1393f29313caabcdbe87, title: 'Zangiku monogatari' },
  { _id: 573a1393f29313caabcdbeb9,
    title: "You're Missing the Point" },
  { _id: 573a1393f29313caabcdc471, title: 'The 47 Ronin' },
  { _id: 573a1393f29313caabcdc6f0,
    title: 'The Brothers and Sisters of the Toda Family' },
  { _id: 573a1393f29313caabcdc802, title: 'There Was a Father' },
  { _id: 573a1393f29313caabcdccc1, title: 'Another Dawn' },
  { _id: 573a1393f29313caabcdcd5e, title: 'Port of Flowers' },
  { _id: 573a1393f29313caabcdcda4, title: 'The Living Magoroku' },
  { _id: 573a1393f29313caabcdcf8a, title: 'Sanshiro Sugata' },
  { _id: 573a1393f29313caabcdd13c, title: 'Ichiban utsukushiku' },
  { _id: 573a1393f29313caabcdd150, title: 'Jubilation Street' },
  { _id: 573a1393f29313caabcdd1a8,
    title: 'Marèa Candelaria (Xochimilco)' },
  { _id: 573a1393f29313caabcdd255, title: 'Army' },
  { _id: 573a1393f29313caabcdd551, title: 'La perla' },
  { _id: 573a1393f29313caabcdd60f,
    title: 'Tora no o wo fumu otokotachi' },
  { _id: 573a1393f29313caabcdd670, title: 'Zoku Sugata Sanshirè' },
  { _id: 573a1393f29313caabcdd74c, title: 'Enamorada' },
  { _id: 573a1393f29313caabcdd8af,
    title: 'Morning for the Osone Family' },
  { _id: 573a1393f29313caabcdd97f,
    title: 'Utamaro and His Five Women' },
  { _id: 573a1393f29313caabcdd99a,
    title: 'No Regrets for Our Youth' },
  { _id: 573a1393f29313caabcdd9da,
    title: 'The Ball at the Anjo House' },
  { _id: 573a1393f29313caabcddaf1, title: 'The Fugitive' },
  { _id: 573a1393f29313caabcddbcb,
    title: 'Record of a Tenement Gentleman' },
  { _id: 573a1393f29313caabcddca8, title: 'One Wonderful Sunday' },
  { _id: 573a1393f29313caabcddf2b, title: 'A Hen in the Wind' },
  { _id: 573a1393f29313caabcde01b, title: 'Salèn Mèxico' },
  { _id: 573a1393f29313caabcde0fa, title: 'Drunken Angel' },
  { _id: 573a1393f29313caabcde107, title: 'Yoru no onnatachi' },
  { _id: 573a1393f29313caabcde1a0, title: 'Late Spring' },
  { _id: 573a1393f29313caabcde2b6, title: 'The Great Madcap' },
  { _id: 573a1393f29313caabcde3cb, title: 'Stray Dog' },
  { _id: 573a1393f29313caabcde47a, title: 'The Quiet Duel' },
  { _id: 573a1393f29313caabcde516, title: 'Flame of My Love' },
  { _id: 573a1394f29313caabcde7f9, title: 'The Munekata Sisters' },
  { _id: 573a1394f29313caabcde822, title: 'Los Olvidados' },
  { _id: 573a1394f29313caabcde86b, title: 'Rashomon' },
  { _id: 573a1394f29313caabcde8ae, title: 'El siete machos' },
  { _id: 573a1394f29313caabcde8b2, title: 'Shèbun' },
  { _id: 573a1394f29313caabcde8ee, title: 'Susana' },
  { _id: 573a1394f29313caabcde945, title: 'Victims of Sin' },
  { _id: 573a1394f29313caabcdea0a, title: 'Early Summer' },
  { _id: 573a1394f29313caabcdea96, title: 'Cèrcel de mujeres' },
  { _id: 573a1394f29313caabcdeb30, title: 'Ginza Cosmetics' },
  { _id: 573a1394f29313caabcdeb4c, title: 'Hakuchi' },
  { _id: 573a1394f29313caabcdeb58, title: 'El hombre sin rostro' },
  { _id: 573a1394f29313caabcdeb9f, title: 'Carmen Comes Home' },
  { _id: 573a1394f29313caabcdebf9, title: 'Repast' },
  { _id: 573a1394f29313caabcdec49, title: 'Miss Oyu' },
  { _id: 573a1394f29313caabcded08, title: 'Mexican Bus Ride' },
  { _id: 573a1394f29313caabcdee35, title: 'Robinson Crusoe' },
  { _id: 573a1394f29313caabcdee8e, title: 'El bruto' },
  { _id: 573a1394f29313caabcdeeb7,
    title: 'Children of Hiroshima' },
  { _id: 573a1394f29313caabcdef98, title: 'Ikiru' },
  { _id: 573a1394f29313caabcdf093,
    title: 'Flavor of Green Tea Over Rice' },
  { _id: 573a1394f29313caabcdf113, title: 'The Life of Oharu' },
  { _id: 573a1394f29313caabcdf201, title: 'El' },
  { _id: 573a1394f29313caabcdf29d,
    title: 'Older Brother, Younger Sister' },
  { _id: 573a1394f29313caabcdf44a,
    title: 'Illusion Travels by Streetcar' },
  { _id: 573a1394f29313caabcdf44f, title: 'Gate of Hell' },
  { _id: 573a1394f29313caabcdf513,
    title: 'The Proud and the Beautiful' },
  { _id: 573a1394f29313caabcdf63c, title: 'Tokyo Story' },
  { _id: 573a1394f29313caabcdf64f, title: 'Wife' },
  { _id: 573a1394f29313caabcdf65b, title: 'Ugetsu' },
  { _id: 573a1394f29313caabcdf757, title: 'Ana-ta-han' },
  { _id: 573a1394f29313caabcdf772, title: 'Late Chrysanthemums' },
  ... 2688 more items ]

npm ERR! Test failed.  See above for more details.

And then I ran npm start again, and at least the error in the JS console is gone! Because I removed map(), right?

So remember, if using a “raw” projection, you just specify the document. If I had a collection of exoplanets and I wanted to find exoplanets between 1 and 2 earth masses and only return their name and system, it might be something like

db.exoplanets.find({earthMasses: { $gte: 1, $lte: 2 } }, { _id: 0, name: 1, systemName: 1 })

following the pattern ({predicate}, {projection}).

However, it is more idiomatic when using the driver, as well as easier to read (imo), to be explicit about what you are doing.

let musksNextTarget = await exoplanets.find({earthMasses: { $gte: 1, $lte: 2 } }).project({_id: 0, name: 1, systemName: 1})

The particular pattern is up to you and won’t be enforced in the validator so you are free to choose!

console.log(…) was need for test output
you need to return as it was

} catch (e) {
  console.error(`Unable to issue find command, ${e}`)
  return []
}
return cursor.toArray()
1 Like

I finally figured it out. It passed! Thank you Nathan and Oleg!

2 Likes