Запазване и намиране на Mongoose документи с вложени схеми с препратки и обещания

Имам един доста прост въпрос. Опитвам се да запазя документ, чиято схема включва вложена реф на схема и тази реф на схема включва друга реф на схема. Когато отида да извлека този документ обаче, той не включва (задължителното) вложено поле, освен ако не го попълня в същата заявка. Дори когато попълвам заявката обаче, вторият вложен документ не се попълва. Не разбирам ли нещо фундаментално тук за начина, по който референтите работят в mongoose?

JavaScript и LiveScript примерен код и изход по-долу.


JavaScript:

(function(){
  var mongoose, bookSchemaObj, authorSchemaObj, agentSchemaObj, bookSchema, authorSchema, agentSchema, Book, Author, Agent, testBookObj, testAuthorObj, testAgentObj, testAgent, testAuthor, testBook;
  mongoose = require("mongoose-bird")(require("mongoose"));
  mongoose.connect("mongodb://test:test@localhost:27017/test");
  bookSchemaObj = {
    author: {
      type: mongoose.Schema.Types.ObjectId,
      ref: "Author",
      required: true
    },
    pubYear: {
      type: Number
    }
  };
  authorSchemaObj = {
    name: {
      type: String,
      required: true
    },
    agent: {
      type: mongoose.Schema.Types.ObjectId,
      ref: "Agent",
      required: true
    }
  };
  agentSchemaObj = {
    name: {
      type: String,
      required: true
    }
  };
  bookSchema = new mongoose.Schema(bookSchemaObj);
  authorSchema = new mongoose.Schema(authorSchemaObj);
  agentSchema = new mongoose.Schema(agentSchemaObj);
  Book = mongoose.model("Book", bookSchema);
  Author = mongoose.model("Author", authorSchema);
  Agent = mongoose.model("Agent", agentSchema);
  testBookObj = {
    pubYear: 2001
  };
  testAuthorObj = {
    name: "John P. Doe"
  };
  testAgentObj = {
    name: "Alfred O. Thompson"
  };
  testAgent = new Agent(testAgentObj);
  testAuthor = new Author(testAuthorObj);
  testBook = new Book(testBookObj);
  testAuthor.agent = testAgent._id;
  testBook.author = testAuthor._id;
  testAgent.saveAsync().then(function(){
    return testAuthor.saveAsync();
  }).then(function(){
    return testBook.saveAsync();
  }).then(function(){
    console.log("book after saving agent, author, and book:");
    console.log(JSON.stringify(testBook, undefined, 2));
    console.log("");
    return Author.findById(testAuthor._id).populate("agent").execAsync();
  }).then(function(queriedAuthor){
    console.log("author after finding and populating author");
    console.log(JSON.stringify(queriedAuthor, undefined, 2));
    console.log("");
    return Book.findById(testBook._id).populate("author author.agent").execAsync();
  }).then(function(queriedBook){
    console.log("book after finding and populating book: ");
    console.log(JSON.stringify(queriedBook, undefined, 2));
    console.log("");
    return Book.findByIdAsync(testBook._id);
  }).then(function(foundBooks){
    console.log("book after finding book:");
    console.log(JSON.stringify(foundBooks, undefined, 2));
    console.log("");
    return foundBooks.populateAsync("author");
  }).then(function(populatedBook){
    console.log("book after populating book: ");
    console.log(JSON.stringify(populatedBook, undefined, 2));
    process.exit();
  })['catch'](function(err){
    console.log(err);
  });
}).call(this);

LiveScript:

mongoose = require("mongoose-bird")(require("mongoose"))
mongoose.connect "mongodb://test:test@localhost:27017/test"

bookSchemaObj   =
  author:
    type: mongoose.Schema.Types.ObjectId
    ref: "Author"
    required: true
  pubYear:
    type: Number

authorSchemaObj =
  name:
    type: String
    required: true
  agent:
    type: mongoose.Schema.Types.ObjectId
    ref: "Agent"
    required: true

agentSchemaObj  =
  name:
    type: String
    required: true

bookSchema   = new mongoose.Schema bookSchemaObj
authorSchema = new mongoose.Schema authorSchemaObj
agentSchema  = new mongoose.Schema agentSchemaObj

Book   = mongoose.model "Book", bookSchema
Author = mongoose.model "Author", authorSchema
Agent  = mongoose.model "Agent", agentSchema

testBookObj   =
  pubYear: 2001

testAuthorObj =
  name: "John P. Doe"

testAgentObj  =
  name: "Alfred O. Thompson"

testAgent  = new Agent testAgentObj
testAuthor = new Author testAuthorObj
testBook   = new Book testBookObj

testAuthor.agent = testAgent._id
testBook.author  = testAuthor._id

testAgent.saveAsync!
  .then ->
    return testAuthor.saveAsync!

  .then ->
    return testBook.saveAsync!

  .then ->
    console.log "book after saving agent, author, and book:"
    console.log JSON.stringify testBook, undefined, 2
    console.log ""
    return Author.findById(testAuthor._id).populate("agent").execAsync!

  .then (queriedAuthor) ->
    console.log "author after finding and populating author"
    console.log JSON.stringify queriedAuthor, undefined, 2
    console.log ""
    return Book.findById(testBook._id).populate("author author.agent")
      .execAsync!

  .then (queriedBook) ->
    console.log "book after finding and populating book: "
    console.log JSON.stringify queriedBook, undefined, 2
    console.log ""
    return Book.findByIdAsync testBook._id

  .then (foundBooks) ->
    console.log "book after finding book:"
    console.log JSON.stringify foundBooks, undefined, 2
    console.log ""
    return foundBooks.populateAsync "author"

  .then (populatedBook) !->
    console.log "book after populating book: "
    console.log JSON.stringify populatedBook, undefined, 2
    process.exit!

  .catch (err) !->
    console.log err

Изход:

book after saving agent, author, and book:
{
  "__v": 0,
  "author": "553a52d4cd8d2a4f5a5c4185",
  "pubYear": 2001,
  "_id": "553a52d4cd8d2a4f5a5c4186"
}

author after finding and populating author
{
  "_id": "553a52d4cd8d2a4f5a5c4185",
  "agent": {
    "_id": "553a52d4cd8d2a4f5a5c4184",
    "name": "Alfred O. Thompson",
    "__v": 0
  },
  "name": "John P. Doe",
  "__v": 0
}

book after finding and populating book: 
{
  "_id": "553a52d4cd8d2a4f5a5c4186",
  "author": {
    "_id": "553a52d4cd8d2a4f5a5c4185",
    "name": "John P. Doe",
    "__v": 0
  },
  "pubYear": 2001,
  "__v": 0
}

book after finding book:
{
  "_id": "553a52d4cd8d2a4f5a5c4186",
  "pubYear": 2001,
  "__v": 0
}

book after populating book: 
{
  "_id": "553a52d4cd8d2a4f5a5c4186",
  "pubYear": 2001,
  "__v": 0
}

person Californian    schedule 23.04.2015    source източник


Отговори (1)


Рекурсивно попълване на многостепенни дълбоки препратки може да бъде постигнато чрез извикване на Model.populate ad infinitum за попълване на структури, които обхващат повече от 2 колекции.

В случай на многостепенна дълбока препратка, множественото попълване, populate("author author.agent"), не работи. Първо трябва да попълните author и след това author.agent на Agent.

Ако не знаете агента, чийто ID искате да попълните, findById и попълнете Author, тогава използвайте променлива с обхват извън обещанието, за да съхраните попълнения ID на агент, след това го добавете отново, след което попълнете с Agent.populate.

var agentId = null;

testAgent.saveAsync().then(function(){
    return testAuthor.saveAsync();
}).then(function(){
    return testBook.saveAsync();
}).then(function(){
    return Author.findById(testAuthor._id).populate("agent").execAsync!
}).then(function(populatedAuthor){
    console.log("book after saving agent, author, and book:");
    console.log(JSON.stringify(testBook, undefined, 2));
    console.log("");
    agentId = populatedAuthor.agent._id;
    return Book.findById(testBook._id).populate("author").execAsync();
}).then(function(partiallyPopulatedBook){
    console.log("author after finding and populating author:");
    console.log(JSON.stringify(partiallyPopulatedBook, undefined, 2));
    console.log("");
    partiallyPopulatedBook.author.agent = agentId;
    return Agent.populate(partiallyPopulatedBook, {path:"author.agent"});
}).then(function(populatedBook){
    console.log("author after finding and populating author and autho.agent:");
    console.log(JSON.stringify(populatedBook, undefined, 2));
    process.exit();
})['catch'](function(err){
    console.log(err);
});
person Edward Lee    schedule 23.04.2015
comment
Всяка нормална mongoose функция може да се използва с обещания чрез добавяне на Async към името. Направих промени и актуализирах въпроса, за да покажа истинския проблем, който имам. - person Californian; 24.04.2015
comment
Имате проблем с дълбока реф. Надявам се моят редактиран отговор да ви помогне. - person Edward Lee; 24.04.2015
comment
Това изглежда толкова близо, така че вече ви благодаря. Въпреки това, когато стартирам това, получавам undefined не е функция. Опитах и ​​Author.populate, но и това не проработи. - person Californian; 24.04.2015
comment
Model.populate връща Promise, така че execAsync трябва да бъде премахнат след Agent.populate. Моля, опитайте отново с моя редактиран код. - person Edward Lee; 24.04.2015
comment
Всичко това е наистина измамно и грозно и не съм сигурен дали е виновен Мангуста или птицата мангуста или синята птица, но най-накрая го разбрах. Вашият код почти работи, единствената незначителна (и изключително хакерска) промяна е, че трябва действително да добавя отново идентификатора на агента, преди да го попълня. Това обаче е ужасно, така че ако можете да ме посъветвате къде да отворя доклад за грешка или кой източник на репо да разгледам, ще бъда много благодарен. Ще редактирам отговора ви и ще добавя съответните промени в кода. - person Californian; 24.04.2015
comment
Можете да регистрирате изход и грешки. node app.js > app_log.log 2> app_err.log - person Edward Lee; 24.04.2015