1

I am trying to search using node.js, ejs and mongoose. All the filter parameters are working perfectly but only categoryIds is not (stored as a collection of ObjectIDs in the mongodb document, referring to the respective document in categories collection), always giving me the empty record set.

For example:
If I need to find the a movie called Cosmos (see the attached screenshot) then I can easily find it with all or any filter except categories. Once I select any category, the record-set will go blank even if the I have selected the one which it belongs to.

model.js

const Model = mongoose.model('Movie', new Schema({
   ...    

   categoryIds: [{
      type: Schema.Types.ObjectId,
      trim: true,
      default: null,
      ref: 'Category',
   }],
   copyrightId: {
      type: Schema.Types.ObjectId,
      trim: true,
      default: null,
      ref: 'Copyright',
   },
   
   ...
}, {
   timestamps: true
});

Controller.js

Router.get('/', (req, res) => {
   const search = req.query;
   const conditions = (() => {
      let object = {};

      ['releaseYear', 'languageId', 'copyrightId'].forEach(filter => {
         if (search[filter] != '') {
            object[filter] = search[filter];
         }
      });

      if (typeof search.categoryIds !== 'undefined') {
         object.categoryIds = [];

         search.categoryIds.forEach(item => object.categoryIds.push(item));
      }

      if (search.keywords != '') {
         object.title = {
            $regex: search.keywords,
            $options: 'i'
         };
      }

      return object;
   })();
   const count = await Model.count(conditions);
   const items = await Model.find(conditions, {
      __v: false,
      imdb: false,
      trailer: false,
      createdAt: false,
      updatedAt: false,
   }).sort({
      status: -1,
      releaseYear: -1,
      title: 1
   })
   .populate('languageId', ['title'])
   .populate('copyrightId', ['title'])
   .populate('categoryIds', ['title'])
   .skip(serialNumber)
   .limit(perPage);

   ...
});

All the fields in the search form

{
  categoryIds: [
    '6332a8a2a336e8dd78e3fe30',
    '6332a899a336e8dd78e3fe2e',
    '6332a87ba336e8dd78e3fe2c',
    '634574ab339b1a6b09c1e144'
  ],
  languageId: '',
  copyrightId: '',
  releaseYear: '',
  rating: '',
  seen: '',
  status: '',
  keywords: '',
  submit: 'search' // button
}

filtered search parameters

{
  categoryIds: [
    '6332a8a2a336e8dd78e3fe30',
    '6332a899a336e8dd78e3fe2e',
    '6332a87ba336e8dd78e3fe2c',
    '634574ab339b1a6b09c1e144'
  ]
}

Here is the screenshot of mongodb document. enter image description here

Mr.Singh
  • 1,421
  • 6
  • 21
  • 46
  • Could you simplify your snippet to remove all non-essential `if`s . It's not quite clear where the `search` in `if (typeof search !== 'undefined')` comes from, what it contains, and which branch is executed. What the black screenshot represents? there are no console.logs in the code, and it's not clear there you get this output from. – Alex Blex Nov 09 '22 at 13:40
  • I have updated the question, I hope it's clear to understand now... – Mr.Singh Nov 09 '22 at 13:50
  • not really. You just removed checks for 'undefined'` type. It doesn't explain what `search` is. Your code says you build your `object.categoryIds` filter from `search.categoryIds` without any indication what is in there. – Alex Blex Nov 09 '22 at 13:59
  • I have added `search` parameter missed previously, sorry for that. This code here extracts parameters with values only to form a new object for finetuning the filtration. The rest of the parameters, other than `categoryIds`, are working completely fine and fetching accurate data from the collection. However, once I select any `category(ies)` to filter, the record-set goes blank. And as this is my first node.js application, I have no idea how to fix this, not even the solution provided by google could help. – Mr.Singh Nov 10 '22 at 04:56
  • That's great, but there is still no visibility of what's coming as the parameters. Did you try to debug it yourself? at lease `console.log` the `conditions` for the queries that do not return expected results. It will result with simple and reproducible question of `await Model.find(conditions)` called with exact known parameter. – Alex Blex Nov 10 '22 at 12:25
  • I have updated the question, I hope now I was able to clarify the issue. – Mr.Singh Nov 11 '22 at 12:16

2 Answers2

0

The filter should contain all categoryIds and in the same order to match the document. It's not quite clear from the question if it is the intended functionality. If not, most popular usecases are documented at https://www.mongodb.com/docs/manual/tutorial/query-arrays/

I don't recall how mongoose handles types when you query with array function like $all, so you may need to convert string IDs to ObjectIDs manually, e.g.:

search.categoryIds.forEach(item => object.categoryIds.push(
    mongoose.Types.ObjectId(item))
);
Mr.Singh
  • 1,421
  • 6
  • 21
  • 46
Alex Blex
  • 34,704
  • 7
  • 48
  • 75
  • Thank you @AlexBlex, but I had already tried this solution and it did not work. However, I have found the solution here https://stackoverflow.com/questions/8303900/mongodb-mongoose-findmany-find-all-documents-with-ids-listed-in-array and I will post the final, working snippet shortly. But still thank you again :) – Mr.Singh Nov 11 '22 at 12:46
0
...

if (typeof search.categoryIds !== 'undefined') {
   object.categoryIds = {
      $in: []
   };

   search.categoryIds.forEach(item => object.categoryIds.$in.push(
      mongoose.Types.ObjectId(item))
   );
}

console.log(object);

return object;

The is the final filter object

{
   categoryIds: {
      '$in': [
          new ObjectId("6332a87ba336e8dd78e3fe2c"),
          new ObjectId("634669f4a2725131e80d99f1")
      ]
   }
}

Now, all the filters are working perfectly.

Thank you everyone.

Mr.Singh
  • 1,421
  • 6
  • 21
  • 46
  • it really depends what you are searching. This edition will find all documents which has **at least one** category from the array in the query. Glad it works for you tho. – Alex Blex Nov 11 '22 at 17:55
  • Thank you and yes, you are absolutely right. This is exactly what I needed. Actually, this study application of mine is already running perfectly in `PHP`. But now I am switching to `node.js` therefore, I thought it would be great practice to recreate the same functionality in `node.js` and `mongodb`. But again, many thanks for your help – Mr.Singh Nov 14 '22 at 11:19
  • Glad you resolved your problem. I aimed my comment to other readers who might find your answer through a search. Their circumstances and requirements may differ and blindly copied approved answer might do more harm than good. It is one of the reason why "code only" answers are discouraged on SO. – Alex Blex Nov 14 '22 at 12:19
  • Agreeable... This is the same reason why I post the working code after my problem has been resolved, via someone's help or my own efforts. So someone else can save his or her time while facing the same or similar problem. – Mr.Singh Nov 14 '22 at 12:43