MongoDB virtual fields, на пальцах, novice level

Предыстория

По роду работы периодически собираю mvp на изврат-стэках. Одним из таких случаев и стала связка из express+graphql+mongo+react. И настал момент связывания данных.

Начинаем начинать
Что же такое virtual fields и зачем они нужны?

Если кратко — связывать таблицы.

Теперь по-подробнее.
Допустим, есть у вас запись хранящая в себе описание книги:

"bookID": "1",
"name": "Книга 1",
"rented": false
И таких книг у вас, например 200шт.

Так же есть у вас запись «полка», в которой перечислены ID книг, хранящихся на ней:


"shelfID": "1",
"name": "Первая полка",
"booksIDs": ["1","2"]
В какой-то момент, вам становится необходимым получить названия книг, стоящих на первой полке. Вот тут нам и пригодятся «виртуальные» поля mongoDB. Чтобы не городить запросы .find({}) с громоздкой строкой параметров используем поля.

Этап подготовки
При объявлении схемы, необходимо передать ей параметры, без которых виртуальное поле не будет возвращать нам искомое.

var shelfSchema = new schema(ShelfSchema, {
    toObject: {
        virtuals: true
    },
    toJSON: {
        virtuals: true
    }});
Магия
Далее переходим к созданию самого поля. Виртуальные поля должны инициализироваться после объявления схемы, и перед инициализацией модели! За это у нас отвечает метод .virtual() схемы.


shelfSchema.virtual('books',{
    ref: 'Books', //Название модели, по которой будем искать объекты
    localField: 'booksIDs', //Поле "родительского" объекта, в котором перечисление искомых параметров
    foreignField: 'bookID', //"Дочернее" поле. Поле с которым сопоставляется значение "родительского" объекта
    justOne: false //Возвращать только один объект, либо массив объектов
})
Далее как обычно производим стандартную инициализацию моделей.


const Books = mongoose.model('books', bookSchema);
const Shelfs = mongoose.model('shelfs',  shelfSchema );
Теперь при вызове

Shelfs.find({shelfID:'1'}).populate('books')
Мы получим объект:


"shelfID": "1",
"name": "Первая полка",
"booksIDs": ["1","2"],
"books":{
        "bookID": "1",
        "name": "Книга 1",
        "rented": false
    },{
        "bookID": "2",
        "name": "Книга 2",
        "rented": false
    }
Поле book у нас не объявлено в схеме. Так откуда же оно взялось и содержит в себе объекты книг?
вся магия находится в методе .populate(). Оно заставляет mongo отдать объекты согласно параметрам, заданным в .virtual(). Простыми словами в .populate() указывается какое виртуальное поле необходимо вернуть, в нашем случае это 'books', оно согласно .virtual() схеме берёт поле 'booksIDs', и возвращает все объекты из таблицы 'Books', у которых значение 'BookID' совпадает с элементом из списка 'bookIDs'

Довольно простая и удобная в то же время вещь. Возможно про это уже написано 1000 и 1 раз, но информация не бывает лишней. Надеюсь, будет полезно.

Нет комментариев