Отображение «многие ко многим» с помощью Mongoose

В моем дизайне есть FlashcardSchemas и PackageSchemas. Одна карточка может принадлежать разным пакетам, а пакет может содержать разные карточки.

Ниже вы можете увидеть урезанную версию моих определений схемы мангуста:

// package-schema.js
var Schema = mongoose.Schema,
    ObjectId = Schema.ObjectId;

var PackageSchema = new Schema({
    id          : ObjectId,
    title       : { type: String, required: true },
    flashcards  : [ FlashcardSchema ]
});

var exports = module.exports = mongoose.model('Package', PackageSchema);

// flashcard-schema.js
var Schema = mongoose.Schema,
    ObjectId = Schema.ObjectId;

var FlashcardSchema = new Schema({
    id      : ObjectId,
    type        : { type: String, default: '' },
    story       : { type: String, default: '' },
    packages    : [ PackageSchema ]
});

var exports = module.exports = mongoose.model('Flashcard', FlashcardSchema);

Как видно из комментариев выше, эти два определения схемы принадлежат отдельным файлам и ссылаются друг на друга.

Я получаю исключение о том, что PackageSchema не определена, как ожидалось. Как я могу сопоставить отношения «многие ко многим» с мангустом?


person Élodie Petit    schedule 20.06.2012    source источник
comment
Нет простого способа сделать это - почему у вас есть пакеты как часть схемы флэш-карты, а флэш-карты - часть схемы пакета? какие запросы вы ожидаете выполнить?   -  person Alex    schedule 20.06.2012
comment
Когда я извлекаю пакет из базы данных, я хочу заполнить массив карт, а когда я извлекаю карту из базы данных, я хочу увидеть, к каким пакетам принадлежит карта. Если нет простого способа сделать это, должен ли я использовать третью функцию схемы для хранения этих отношений?   -  person Élodie Petit    schedule 20.06.2012
comment
Здесь есть более свежий и полный ответ: stackoverflow.com/a/46020968/438970   -  person Damien    schedule 20.11.2019


Ответы (5)


Вы делаете это правильно, однако проблема в том, что вам нужно включить PackageSchema в flashcard-schema.js и наоборот. В противном случае эти файлы понятия не имеют, на что вы ссылаетесь.

var Schema = mongoose.Schema,
    ObjectId = Schema.ObjectId;
    PackageSchema = require('./path/to/package-schema.js')

var FlashcardSchema = new Schema({
    id      : ObjectId,
    type        : { type: String, default: '' },
    story       : { type: String, default: '' },
    packages    : [ PackageSchema ]
});
person Last Rose Studios    schedule 21.06.2012
comment
Не приведет ли это к бесконечной рекурсии, поскольку каждый метод require() загружает другой? - person Tony O'Hagan; 21.12.2012
comment
@TonyOHagan нет, Node обрабатывает цикл require, как описано на nodejs.org/api/all.html# все_циклы - person FGM; 13.07.2013
comment
Будьте осторожны при использовании этого метода. Поскольку вы храните объект, а не только его идентификатор, если вы измените пакет, связанный с вашей флэш-картой, он не будет изменен в вашей флэш-карте. - person Tim; 14.11.2014
comment
НЕПРАВИЛЬНЫЙ ОТВЕТ. Ответ ниже правильный. Этот ответ вводит в заблуждение и, кроме того, принимается как правильный ответ. - person hhsadiq; 16.04.2016
comment
@hhsadiq есть еще 4 ответа. Какой из них правильный? - person Hop hop; 21.02.2017
comment
Ответ от @gtsouk - person hhsadiq; 21.02.2017

Я новичок в node, mongoDB и mongoose, но я думаю, что правильный способ сделать это:

var PackageSchema = new Schema({
    id: ObjectId,
    title: { type: String, required: true },
    flashcards: [ {type : mongoose.Schema.ObjectId, ref : 'Flashcard'} ]
});

var FlashcardSchema = new Schema({
    id: ObjectId,
    type: { type: String, default: '' },
    story: { type: String, default: '' },
    packages: [ {type : mongoose.Schema.ObjectId, ref : 'Package'} ]
});

Таким образом, вы сохраняете только ссылку на объект, а не встроенный объект.

person gtsouk    schedule 14.04.2013
comment
Хорошая работа по поиску этой ссылки @Ruairi - person gtsouk; 08.03.2014
comment
Просто хочу отметить, что это правильный путь для отношений «многие ко многим». Принятый ответ неверен. - person volatilevar; 13.01.2015
comment
Должны ли ref:'Flashcard' и ref:'Package' в этом ответе совпадать с именем схемы или это произвольно? Другими словами, должно ли это быть FlashcardSchema? - person TyMayn; 02.09.2015
comment
@TyMayn Следует установить имя модели, а не имя схемы. Обычно FlashcardSchema должна генерировать модель с именем Flashcard. mongoosejs.com/docs/models.html - person gtsouk; 02.09.2015
comment
@ Élodie-petit, пожалуйста, сделайте это принятым ответом. - person Predrag Stojadinović; 13.12.2016
comment
Цитата из документа «заполнить»: It is debatable that we really want two sets of pointers as they may get out of sync. Instead we could skip populating and directly find() the stories we are interested in. Это заставляет меня думать, что в одной из схем должен быть только один массив ссылок. Если у вас есть массивы ссылок в обеих схемах, вы должны убедиться, что вы сохраняете только карты с пакетами ИЛИ пакеты с картами. Хотя я не эксперт по мангусту. - person Grant Carthew; 25.04.2018
comment
Как вы ищете в этих отношениях? например, если вы хотите найти все документы FlashcardSchema, которые имеют определенный идентификатор в списке пакетов? - person DejanG; 26.03.2020

Можно использовать метод Schema.add(), чтобы избежать проблем с прямыми ссылками.

Это (непроверенное) решение помещает схему в один файл .js

модели/index.js

var Schema = mongoose.Schema,
    ObjectId = Schema.ObjectId;

// avoid forward referencing
var PackageSchema = new Schema();
var FlashcardSchema = new Schema();

PackageSchema.add({
    id          : ObjectId,
    title       : { type: String, required: true },
    flashcards  : [ FlashcardSchema ]
});

FlashcardSchema.add({
    id      : ObjectId,
    type        : { type: String, default: '' },
    story       : { type: String, default: '' },
    packages    : [ PackageSchema ]
});

// Exports both types
module.exports = {
    Package:   mongoose.model('Package', PackageSchema),
    Flashcard: mongoose.model('Flashcard', FlashcardSchema)
};  
person Tony O'Hagan    schedule 21.12.2012

Вы слишком сильно думаете об этом как о реляционном хранилище данных. Если это то, что вы хотите, используйте MySQL (или другую СУБД)

Если это не удастся, тогда да, можно использовать третью схему, но не забывайте, что это все равно будет только идентификатор каждого объекта (без соединений, помните), поэтому вам все равно придется извлекать каждый другой элемент в отдельном запросе. .

person Alex    schedule 21.06.2012
comment
Единственное место, где я должен думать об этом как о реляционной СУБД, — это сценарий с пакетной картой. В остальном вы абсолютно правы. - person Élodie Petit; 21.06.2012
comment
Если в этом ответе нет комментариев, обратите внимание, что MongoDB, выпущенная в 2015 году, теперь имеет агрегатор $lookup (docs.mongodb.com/manual/reference/operator/aggregation/lookup), который выполняет левое внешнее соединение. - person ryanm; 11.01.2018

Это проблема циклической/круговой зависимости. Вот как вы заставляете это работать в nodejs. Дополнительные сведения см. в разделе «Циклические зависимости в CommonJS» по адресу http://exploringjs.com/es6/ch_modules.html#sec_modules-in-javascript

//------ a.js ------
var b = require('b');
function foo() {
    b.bar();
}
exports.foo = foo;

//------ b.js ------
var a = require('a'); // (i)
function bar() {
    if (Math.random()) {
        a.foo(); // (ii)
    }
}
exports.bar = bar;

person Lu Tran    schedule 07.08.2018