gregg
gregg

Reputation: 47

In Node.js and Mongoose, how can I use a variable in place of a model name?

I have a couple of collections with tons of documents, like "all books in the world" and "all movies in the world." To reduce the size of my collections and because there are so many unique properties between the two, I have them as separate collections.

But I have a lot of Node.js functions where the only difference between two functions is the model I am querying on. I would like to make more generic functions to reduce the amount of code.

To greatly simplify the example, I currently have this:

import { Book, Movie } from '../models'

const getBooks = (bookId) => Book.findById(bookId)
const getMovies = (movieId) => Movie.findById(movieId)

And I would like to be able to do something like this, where type would be either 'Book' or 'Movie':

import { Book, Movie } from '../models'
const getItems = (type, itemId) => type.findById(itemId)

I have tried all kinds of things, but the only thing I can get to work is having ternary operators all over the place (which will probably become switch statements when I add additional collections).

Is there a more elegant way of doing this? And if so, recommended? Or does this just point to schema design issues?

This would kind of be like using computed properties, but not for the name of a property, but for the name of the object itself. This does not seem to work"

import { Book, Movie } from '../models'
const getItems = (type, itemId) => [type].findById(itemId)

Upvotes: 1

Views: 756

Answers (2)

luckongas
luckongas

Reputation: 1799

If you could pass the type as the string name of your collection and the number of models doesn't grow every day (if that is not the case you should be always updating the myModels object) the following solution should do the job

import { Book, Movie } from '../models'

const myModels = {
    book: Book,
    movie: Movie,
};

const getItems = (type, itemId) => myModels[type].findById(itemId)

I would suggest doing something if there's a chance that the type passed as a parameter cannot exist.


EDIT

As mentioned via comments by @Meiogordo mapping the Models against the strings would be pointless if you are not going to use the functions outside the data access layer.

import { Book, Movie } from '../models';

const getItems = (model, itemId) => model.findById(itemId);

const book = getItems(Book, 1);
const movie = getItems(Movie, 3);

With this solution, you lose some encapsulation due to the need for requiring the models outside the data access layer, but you are not mapping the models on a separated structure.

Upvotes: 1

Meiogordo
Meiogordo

Reputation: 124

As discussed via comments, this should work for your use-case:

// Defining the function
const getItems = (model, itemId) => model.findById(itemId)

// And assuming this import
imports { Book, Movie } from '../models';

// You can just call it like so
const book_one = getItems(Book, 1);
const movie_three = getItems(Movie, 3);

The first argument to getItems can just be the Model class itself.

Upvotes: 0

Related Questions