মঙ্গুজে নেস্টেড অ্যারে বসান


111

উদাহরণ নথিতে আমি কীভাবে "উপাদানগুলি" পপুলেট করতে পারি:

  {
    "__v": 1,
    "_id": "5252875356f64d6d28000001",
    "pages": [
      {
        "__v": 1,
        "_id": "5252875a56f64d6d28000002",
        "page": {
          "components": [
            "525287a01877a68528000001"
          ]
        }
      }
    ],
    "author": "Book Author",
    "title": "Book Title"
  }

এটি আমার জেএস যেখানে আমি মঙ্গুজ দ্বারা নথি পেয়েছি:

  Project.findById(id).populate('pages').exec(function(err, project) {
    res.json(project);
  });

এখন কি খালি? আপনি কি ফলাফল পাচ্ছেন?
ওয়্যার্ডপ্রাইরি

2
যদি আমি লিখি ...populate('pages pages.page.components').exec...আমি উদাহরণ নথিতে বর্ণিত একই জিনিস পেয়েছি। কিছুই পরিবর্তন হয় না।
আন্তন শুভালভ

উত্তর:


251

মঙ্গুজ 4.5 এটি সমর্থন করে

Project.find(query)
  .populate({ 
     path: 'pages',
     populate: {
       path: 'components',
       model: 'Component'
     } 
  })
  .exec(function(err, docs) {});

এবং আপনি একাধিক গভীর স্তরে যোগদান করতে পারেন


14
আশ্চর্যজনক - এত ক্লিনার! এটি এখন আধুনিক এবং সঠিক উত্তর। এখানে নথিভুক্ত
ইশট্রাভিস

@ এনগাএনগুইনডুই গিথুব. com / অটোমেটিক / মমঙ্গুজ / উইকি / ৪.০- অনুগ্রহ করে- নোটস জানিয়েছেন যে এই বৈশিষ্ট্যটি ইতিমধ্যে ৪.০ থেকে রয়েছে। আপনি ভুল জিজ্ঞাসা পেয়েছি।
ত্রিনিহ হোয়াং নু

1
@ ত্রিনহোংনহু আমি ৪.০০ রিলিজ নোট প্রকাশ করি নি, তবে আমার চেষ্টা করা হয়েছিল। আমার ক্যোয়ারী যদি আমি এটিকে মংগুজ 4.0 হিসাবে চালিত করি তবে কিছু ফেরত দেয় না, তবে আমি 4.5.8 সংস্করণে আপগ্রেড করলে এটি ঠিকঠাক হয়েছিল। আমার প্রশ্ন: gist.github.com/NgaNguyenDuy/998f7714fb768427abf5838fafa573d7
NgaNguyenDuy

1
@ এনগাএনগুইয়েনডুয় এই কাজটি করার জন্য আমারও 4.5.4 আপডেট করার দরকার ছিল !!
ভিনিশ

4
পথটি pages.$.page.componentনা হওয়ায় এটি কীভাবে কাজ করবে তা নিয়ে আমি বিভ্রান্ত pages.$.component। পৃষ্ঠা অবজেক্টে এটি কীভাবে দেখতে পাওয়া যায়?
ডোমিনিক

111

সেটা আমার জন্য কাজ করে:

 Project.find(query)
  .lean()
  .populate({ path: 'pages' })
  .exec(function(err, docs) {

    var options = {
      path: 'pages.components',
      model: 'Component'
    };

    if (err) return res.json(500);
    Project.populate(docs, options, function (err, projects) {
      res.json(projects);
    });
  });

ডকুমেন্টেশন: মডেল.পপুলেট


9
"মডেল: 'উপাদান'" রাখা সত্যিই গুরুত্বপূর্ণ!
Totty.js

3
তবে উচিত নয় কারণ আমি যখন রেফটি সংজ্ঞায়িত করি তখন আমি মডেলটিও সংজ্ঞায়িত করি, এটি সত্যই শুভ নয়। যাইহোক, ধন্যবাদ, এটি কাজ করে;)
Totty.js

পাতলা পদ্ধতিতে সতর্কতা অবলম্বন করুন। আপনি কাস্টম পদ্ধতিগুলিতে কল করতে পারবেন না এমনকি ফিরে আসা বস্তুগুলিতে সংরক্ষণ করতে পারবেন না।
ড্যানিয়েল কুমাক

আমার ক্ষেত্রে পাতলা () প্রয়োজনীয় নয় তবে বাকীগুলি সুন্দরভাবে কাজ করে।
জন

1
আরও 'স্তর' আরও গভীরতর করা কি সম্ভব?
টিএমএইচসি 22

35

অন্যরা যেমন উল্লেখ করেছে, এটি Mongoose 4সমর্থন করে। এটি লক্ষ করা খুব জরুরী যে আপনি যদি প্রয়োজন হয় তবে আপনি এক স্তর থেকেও গভীর পুনরাবৃত্তি করতে পারেন — যদিও ডক্সে এটি উল্লেখ করা হয়নি:

Project.findOne({name: req.query.name})
    .populate({
        path: 'threads',
        populate: {
            path: 'messages', 
            model: 'Message',
            populate: {
                path: 'user',
                model: 'User'
            }
        }
    })

28

আপনি এর মতো একাধিক নেস্টেড ডকুমেন্টগুলি পপুলেট করতে পারেন।

   Project.find(query)
    .populate({ 
      path: 'pages',
      populate: [{
       path: 'components',
       model: 'Component'
      },{
        path: 'AnotherRef',
        model: 'AnotherRef',
        select: 'firstname lastname'
      }] 
   })
   .exec(function(err, docs) {});

1
অ্যারেতে populate: ['components','AnotherRef']
পপুলেট করা পথগুলিও

5.5.7 সংস্করণে আমার জন্য ইয়াসিন উল্লিখিত অ্যারে স্বরলিপিটি কাজ করে না, পরিবর্তে একটি স্ট্রিংয়ে যোগাযোগ করে কাজ করে। অর্থাত্populate: 'components AnotherRef'
সামিহ এ

8

এটি সর্বোত্তম সমাধান:

Car
 .find()
 .populate({
   path: 'pages.page.components'
})

অন্য সমস্ত উত্তর অযথা জটিল, এটি গ্রহণযোগ্য সমাধান হওয়া উচিত।
SeedyROM

এবং এটি সেই ক্ষেত্রে সমাধান করে যেখানে pageঅন্যান্য অ-জন-বাসযোগ্য-সক্ষম বৈশিষ্ট্য রয়েছে।
সিরা লাম

4

আমি 2 টি রেফারেন্স স্তর গভীর সম্পর্ক স্থাপনের জন্য হুকের আগে একটি পালক তৈরি করতে এটি খুব সহায়ক বলে মনে করেছি। মঙ্গুসের মডেলগুলি কেবল আছে

tables = new Schema({
  ..
  tableTypesB: { type: Schema.Types.ObjectId, ref: 'tableTypesB' },
  ..
}
tableTypesB = new Schema({
  ..
  tableType: { type: Schema.Types.ObjectId, ref: 'tableTypes' },
  ..
}

তারপরে হুকের আগে পালকগুলিতে:

module.exports = function(options = {}) {
  return function populateTables(hook) {
    hook.params.query.$populate = {
      path: 'tableTypesB',
      populate: { path: 'tableType' }
    }

    return Promise.resolve(hook)
  }
}

অন্য কয়েকটি পদ্ধতির তুলনায় এত সহজ যে আমি এটি অর্জনের চেষ্টা করছিলাম।


যদি কোনও ulate জনবহুল ক্যোয়ারীটি ওভাররাইটিং সম্পর্কে উদ্বিগ্ন না হয় তবে এই ক্ষেত্রে আপনার hook.params.query ব্যবহার করা উচিত $ populate = Object.assign (hook.params.query। $ Populate || {}, {/ * নতুন পপুলেট অবজেক্ট এখানে * /})
ট্র্যাভিস এস

1

আমি এই প্রশ্নটি অন্য একটি প্রশ্নের মাধ্যমে পেয়েছি যা কীস্টোনজজেএস নির্দিষ্ট ছিল তবে এটি সদৃশ হিসাবে চিহ্নিত হয়েছিল। এখানে যদি কেউ কীস্টোন উত্তর খুঁজছেন, আমি কীস্টোনে আমার গভীর জনসংখ্যা অনুসন্ধানটি এইভাবেই করেছি।

কীস্টোনজ ব্যবহার করে মঙ্গুজ দুই স্তরের জনসংখ্যা [সদৃশ]

exports.getStoreWithId = function (req, res) {
    Store.model
        .find()
        .populate({
            path: 'productTags productCategories',
            populate: {
                path: 'tags',
            },
        })
        .where('updateId', req.params.id)
        .exec(function (err, item) {
            if (err) return res.apiError('database error', err);
            // possibly more than one
            res.apiResponse({
                store: item,
            });
        });
};

1

আপনি $lookupএগ্রিগেশনটি ব্যবহার করে এটিও করতে পারেন এবং সম্ভবত এখন সবচেয়ে ভাল উপায় মঙ্গো থেকে বিলুপ্ত হয়ে যাচ্ছে

Project.aggregate([
  { "$match": { "_id": mongoose.Types.ObjectId(id) } },
  { "$lookup": {
    "from": Pages.collection.name,
    "let": { "pages": "$pages" },
    "pipeline": [
      { "$match": { "$expr": { "$in": [ "$_id", "$$pages" ] } } },
      { "$lookup": {
        "from": Component.collection.name,
        "let": { "components": "$components" },
        "pipeline": [
          { "$match": { "$expr": { "$in": [ "$_id", "$$components" ] } } },
        ],
        "as": "components"
      }},
    ],
    "as": "pages"
  }}
])


0

যার কারও সাথে সমস্যা রয়েছে populateএবং তিনি এটি করতে চান:

  • সাধারণ পাঠ্য এবং দ্রুত জবাব (বুদবুদ) এর সাথে চ্যাট করুন
  • চ্যাটের জন্য 4 ডাটাবেসের সংগ্রহগুলি: clients, users, rooms, messasges
  • 3 ধরণের প্রেরকের জন্য একই বার্তা ডিবি কাঠামো: বট, ব্যবহারকারী এবং ক্লায়েন্ট
  • refPathবা গতিশীল রেফারেন্স
  • populateসঙ্গে pathএবং modelঅপশন
  • ব্যবহার findOneAndReplace/ replaceOneসাথে$exists
  • ফেচ করা নথি উপস্থিত না থাকলে একটি নতুন দস্তাবেজ তৈরি করুন

প্রেক্ষাপট

লক্ষ্য

  1. ডাটাবেসে একটি নতুন সাধারণ পাঠ্য বার্তা সংরক্ষণ করুন এবং এটি ব্যবহারকারী বা ক্লায়েন্টের ডেটা (2 টি ভিন্ন মডেল) দিয়ে পপুলেট করুন।
  2. ডেটাবেজে একটি নতুন কুইক রিপ্লাইস বার্তা সংরক্ষণ করুন এবং এটি ব্যবহারকারী বা ক্লায়েন্টের ডেটা দিয়ে পপুলেট করুন।
  3. প্রত্যেক বার্তার সংরক্ষণ তার প্রেরক টাইপ: clients, usersbot
  4. প্রেরক clientsবা usersতার মঙ্গুজ মডেলগুলি সহ কেবলমাত্র সেই বার্তাগুলিই তৈরি করুন । _সেন্ডার ধরণের ক্লায়েন্ট মডেলগুলি হ'ল clients, ব্যবহারকারী users

বার্তা স্কিমা :

const messageSchema = new Schema({
    room: {
        type: Schema.Types.ObjectId,
        ref: 'rooms',
        required: [true, `Room's id`]
    },
    sender: {
         _id: { type: Schema.Types.Mixed },
        type: {
            type: String,
            enum: ['clients', 'users', 'bot'],
            required: [true, 'Only 3 options: clients, users or bot.']
        }
    },
    timetoken: {
        type: String,
        required: [true, 'It has to be a Nanosecond-precision UTC string']
    },
    data: {
        lang: String,
        // Format samples on https://docs.chatfuel.com/api/json-api/json-api
        type: {
            text: String,
            quickReplies: [
                {
                    text: String,
                    // Blocks' ids.
                    goToBlocks: [String]
                }
            ]
        }
    }

mongoose.model('messages', messageSchema);

সমাধান

আমার সার্ভার সাইড এপিআই অনুরোধ

আমার কোড

chatUtils.jsআপনি যে বার্তাটি সংরক্ষণ করতে চান তার প্রকার পেতে ইউটিলিটি ফাংশন ( ফাইলটিতে):

/**
 * We filter what type of message is.
 *
 * @param {Object} message
 * @returns {string} The type of message.
 */
const getMessageType = message => {
    const { type } = message.data;
    const text = 'text',
        quickReplies = 'quickReplies';

    if (type.hasOwnProperty(text)) return text;
    else if (type.hasOwnProperty(quickReplies)) return quickReplies;
};

/**
 * Get the Mongoose's Model of the message's sender. We use
 * the sender type to find the Model.
 *
 * @param {Object} message - The message contains the sender type.
 */
const getSenderModel = message => {
    switch (message.sender.type) {
        case 'clients':
            return 'clients';
        case 'users':
            return 'users';
        default:
            return null;
    }
};

module.exports = {
    getMessageType,
    getSenderModel
};

বার্তাটি সংরক্ষণের অনুরোধটি পাওয়ার জন্য আমার সার্ভার সাইড (নোডেজ ব্যবহার করে):

app.post('/api/rooms/:roomId/messages/new', async (req, res) => {
        const { roomId } = req.params;
        const { sender, timetoken, data } = req.body;
        const { uuid, state } = sender;
        const { type } = state;
        const { lang } = data;

        // For more info about message structure, look up Message Schema.
        let message = {
            room: new ObjectId(roomId),
            sender: {
                _id: type === 'bot' ? null : new ObjectId(uuid),
                type
            },
            timetoken,
            data: {
                lang,
                type: {}
            }
        };

        // ==========================================
        //          CONVERT THE MESSAGE
        // ==========================================
        // Convert the request to be able to save on the database.
        switch (getMessageType(req.body)) {
            case 'text':
                message.data.type.text = data.type.text;
                break;
            case 'quickReplies':
                // Save every quick reply from quickReplies[].
                message.data.type.quickReplies = _.map(
                    data.type.quickReplies,
                    quickReply => {
                        const { text, goToBlocks } = quickReply;

                        return {
                            text,
                            goToBlocks
                        };
                    }
                );
                break;
            default:
                break;
        }

        // ==========================================
        //           SAVE THE MESSAGE
        // ==========================================
        /**
         * We save the message on 2 ways:
         * - we replace the message type `quickReplies` (if it already exists on database) with the new one.
         * - else, we save the new message.
         */
        try {
            const options = {
                // If the quickRepy message is found, we replace the whole document.
                overwrite: true,
                // If the quickRepy message isn't found, we create it.
                upsert: true,
                // Update validators validate the update operation against the model's schema.
                runValidators: true,
                // Return the document already updated.
                new: true
            };

            Message.findOneAndUpdate(
                { room: roomId, 'data.type.quickReplies': { $exists: true } },
                message,
                options,
                async (err, newMessage) => {
                    if (err) {
                        throw Error(err);
                    }

                    // Populate the new message already saved on the database.
                    Message.populate(
                        newMessage,
                        {
                            path: 'sender._id',
                            model: getSenderModel(newMessage)
                        },
                        (err, populatedMessage) => {
                            if (err) {
                                throw Error(err);
                            }

                            res.send(populatedMessage);
                        }
                    );
                }
            );
        } catch (err) {
            logger.error(
                `#API Error on saving a new message on the database of roomId=${roomId}. ${err}`,
                { message: req.body }
            );

            // Bad Request
            res.status(400).send(false);
        }
    });

টিআইপি :

ডাটাবেসের জন্য:

  • প্রতিটি বার্তা নিজেই একটি নথি।
  • ব্যবহার করার পরিবর্তে refPath, আমরা util ব্যবহার getSenderModelযে ব্যবহার করা হয় populate()। এটি বটের কারণে। এটি sender.typeহতে পারে: usersতার ডাটাবেস clientsসহ, তার ডাটাবেস সহ এবং botকোনও ডাটাবেস ছাড়াই। refPathসত্য মডেল রেফারেন্স প্রয়োজন, যদি না, Mongooose একটি ত্রুটি নিক্ষেপ করা।
  • sender._idObjectIdব্যবহারকারী এবং ক্লায়েন্ট, বা nullবট জন্য টাইপ হতে পারে ।

এপিআই অনুরোধ যুক্তি জন্য:

  • আমরা quickReplyবার্তাটি প্রতিস্থাপন করি (বার্তা ডিবিতে কেবলমাত্র একটি কুইকপ্লাই থাকতে হবে তবে আপনি যতটা সহজ পাঠ্য বার্তা চান) want আমরা এর findOneAndUpdateপরিবর্তে replaceOneবা ব্যবহার করি findOneAndReplace
  • আমরা কোয়েরি অপারেশন (ও findOneAndUpdate) এবং প্রতিটিটির populateসাথে ক্রিয়াকলাপটি কার্যকর করি callback। এটি গুরুত্বপূর্ণ আপনি যদি ব্যবহার জানি না async/await, then(), exec()বা callback(err, document)। আরও তথ্যের জন্য পপুলেট ডকটি দেখুন
  • আমরা overwriteবিকল্প উত্তর ছাড়াই দ্রুত উত্তর বার্তাটি প্রতিস্থাপন করি$set কোয়েরি অপারেটর ।
  • যদি আমরা দ্রুত উত্তরটি খুঁজে না পাই তবে আমরা একটি নতুন তৈরি করি। আপনার সাথে এটি মঙ্গুজকে বলতে হবেupsertবিকল্পটির ।
  • প্রতিস্থাপিত বার্তা বা নতুন সংরক্ষিত বার্তার জন্য আমরা কেবলমাত্র একটি সময় পপুলেট করি।
  • আমরা কলব্যাকগুলিতে ফিরে আসি, এর সাথে findOneAndUpdateএবং এর জন্য আমরা যে বার্তাটি সংরক্ষণ করেছি তা যাই হোক না কেন populate()
  • ইন populate, আমরা এর সাথে একটি কাস্টম গতিশীল মডেল রেফারেন্স তৈরি করি getSenderModel। আমরা মঙ্গুজ গতিশীল রেফারেন্স ব্যবহার করতে পারি কারণ এর sender.typeজন্য botকোনও মঙ্গুজ মডেল নেই। আমরা একটি ব্যবহার ডাটাবেস জুড়ে পূর্ণ সঙ্গে modelএবং pathoptins।

আমি এখানে এবং সেখানে সামান্য সমস্যাগুলি সমাধান করার জন্য প্রচুর ঘন্টা ব্যয় করেছি এবং আমি আশা করি এটি কারও সাহায্য করবে! 😃


0

আমি পুরো রক্তাক্ত দিনের জন্য এটির সাথে লড়াই করেছি। উপরের সমাধানগুলির কোনওটিই কাজ করে না। নিম্নলিখিত ক্ষেত্রে যেমন উদাহরণ হিসাবে আমার ক্ষেত্রে কাজ করা একমাত্র:

{
  outerProp1: {
    nestedProp1: [
      { prop1: x, prop2: y, prop3: ObjectId("....")},
      ...
    ],
    nestedProp2: [
      { prop1: x, prop2: y, prop3: ObjectId("....")},
      ...
    ]
  },
  ...
}

নিম্নলিখিতটি করণীয়: (আনার পরে জনবহুল ধরে নেওয়া - মডেল শ্রেণি থেকে পপুলেট কল করার সময়ও কার্যকর হয় (এক্সিকিউট দ্বারা অনুসরণ করা))

await doc.populate({
  path: 'outerProp1.nestedProp1.prop3'
}).execPopulate()

// doc is now populated

অন্য কথায়, বহিরাগত পথের সম্পত্তিটিতে পুরো পথ থাকতে হবে। জনবহুল বৈশিষ্ট্যগুলির সাথে মিলিত কোনও আংশিকভাবে সম্পূর্ণ পাথ কাজ করবে বলে মনে হয় নি (এবং মডেল সম্পত্তিটি প্রয়োজনীয় বলে মনে হয় না; কারণ এটি স্কিমে অন্তর্ভুক্ত রয়েছে)। এটি বের করার জন্য আমাকে পুরো দিনটি নিয়েছে! অন্যান্য উদাহরণগুলি কেন কাজ করে না তা নিশ্চিত নয়।

(মঙ্গুজ 5.5.32 ব্যবহার করে)


-3

ডক্স রেফারেন্স সরান

if (err) {
    return res.json(500);
}
Project.populate(docs, options, function (err, projects) {
    res.json(projects);
});

এটি আমার পক্ষে কাজ করেছে।

if (err) {
    return res.json(500);
}
Project.populate(options, function (err, projects) {
    res.json(projects);
});
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.