Miguel Frias
Miguel Frias

Reputation: 2710

General search: search between multiple collections (Express / mongoose)

Im new working with node, express and mongodb, so i don´t know if this is best way to implement this, i working in movie store ( Just for study and practicing ), where the is user is gonna have toolbar in the top with a search bar and is gonna be able to make a general search to find a movie/series/what ever else, now the way i was think i can implement this in express with mongoose was this way:

router.post('/search', (req, res) => {
  let resultSearch = [];
  Movie.find({ name: { $regex: req.body.query, $options: 'i' } })
    .then(movies => {
      if (movies.length > 0) {
        const moviesSearch = movies.map(movie => {
          return {
            _id: movie._id,
            content_type: movie.content_type,
            rate: movie.rate,
            name: movie.name,
            item_img: movie.movie_img,
            itemType: 'movies'
          };
        });
        resultSearch = [...moviesSearch];
      }
      Serie.find({ name: { $regex: req.body.query, $options: 'i' } }).then(series => {
        if (series.length > 0) {
          const seriesSearch = series.map(serie => {
            return {
              _id: serie._id,
              content_type: serie.content_type,
              rate: serie.rate,
              name: serie.name,
              item_img: serie.serie_img,
              itemType: 'series'
            };
          });
          resultSearch = [...resultSearch, ...seriesSearch];
        }
        res.status(200).send(resultSearch);
      });
    })
    .catch(error => {
      console.log(error);
      res.status(500).send(error);
    });
});

so the idea here is to search between multiple collections, as you can see here what im doing is just search in one collection, in the response of the promise make the search in the other collection. Now i completely sure that is not a good way to do it, in the case that i want to search between 10 different collection is gonna be a really messy code, so is there a better way to achieve this.

Upvotes: 0

Views: 103

Answers (2)

arizafar
arizafar

Reputation: 3122

You can use Promise.all rather chaining promises when there's no dependency.

let moviesPromise = Movie.find({ name: { $regex: req.body.query, $options: 'i' } });
let seriesPromise = Serie.find({ name: { $regex: req.body.query, $options: 'i' } });
Promise.all([moviesPromise, seriesPromise]).then((res) => {
  arr = ['movies', 'series'];
  res.map((e, i)) => {
	e.map(obj => {
	  return {
		_id: obj._id,
		content_type: obj.content_type,
		rate: obj.rate,
		name: obj.name,
		item_img: obj.[`${arr[i]}_img`],
		itemType: arr[i]
	  };
	});
  })

Upvotes: 3

Cuong Le Ngoc
Cuong Le Ngoc

Reputation: 11975

You can create an array contain Models you want to find in and names(because your img field is based on the name) then loop through it and find with async/await:

router.post('/search', async (req, res) => {
  try {
    let resultSearch = [];
    let sets = [{model: Movie, name: 'movie'}, {model: Serie, name: 'serie'}, ...some more];
    for (let set of sets) {
      let results = await set.model.find({ name: { $regex: req.body.query, $options: 'i' } });
      results = results.map(result => {
        return {
          _id: result._id,
          content_type: result.content_type,
          rate: result.rate,
          name: result.name,
          item_img: result[`${set.name}_img`],
          itemType: set.name //or you can use set.model.collection.collectionName
        };
      })
      resultSearch = resultSearch.concat(results);
    }
    res.status(200).send(resultSearch);
  } catch(err) {
    console.log(error);
    res.status(500).send(error);
  }
})

Update: As AZ_ suggest, using Promise.all and then map the output to be expected response will give better performance:

router.post('/search', async (req, res) => {
  try {
    let sets = [{model: Movie, name: 'movie'}, {model: Serie, name: 'serie'}, ...some more];
    let resultSearch = await Promise.all(sets.map(set => set.model.find({ name: { $regex: req.body.query, $options: 'i' } }).exec()));
    resultSearch = resultSearch.map((results, i) => {
      return results.map(result => {
        return {
          _id: result._id,
          content_type: result.content_type,
          rate: result.rate,
          name: result.name,
          item_img: result[`${sets[i].name}_img`],
          itemType: sets[i].name //or you can use sets[i].model.collection.collectionName
        };
      })
    }
    res.status(200).send(resultSearch.flat());  //resultSearch is 2D array so need to flatten it
  } catch(err) {
    console.log(error);
    res.status(500).send(error);
  }
})

Upvotes: 2

Related Questions