복붙노트

[MONGODB] 몽구스 재귀 채우기

MONGODB

몽구스 재귀 채우기

나는 잠시 동안 검색 한 내가 어떤 좋은 답변을 찾지 못했습니다. 제가 DB에 저장하고 있음을 N-깊은 나무를 가지고 있고 전체 트리를 얻을 그래서 결국 나는 모든 부모를 채울 싶습니다

node
 -parent
  -parent
    .
    .
    -parent

지금까지 나는 레벨 2로 채우는, 그리고 내가 언급 한 바와 같이 나는 레벨 n으로 얻을 필요가있다.

Node.find().populate('parent').exec(function (err, items) {
   if (!err) {
     Node.populate(items, {path: 'parent.parent'}, function (err, data) {
       return res.send(data);
     });
   } else {
     res.statusCode = code;
     return res.send(err.message);
   }
 });

해결법

  1. ==============================

    1.그냥하지 않습니다 :)

    그냥하지 않습니다 :)

    그렇게 할 좋은 방법은 없습니다. 당신은 몇 가지 할 경우에도 당신이 그것을 가지고거나 이제까지 필요합니다 경우 샤딩과 끔찍한 성능과 문제가있는 것,지도 - 줄일 수 있습니다.

    되는 NoSQL 데이터베이스와 몽고 나무 문서를 저장하기위한 정말 좋은 것입니다. 당신은 전체 트리를 저장 한 다음 "찾을 특히 잎"쿼리의 여지가없는 경우 일부 특정 잎을 얻을지도-줄일 수 있습니다. 이 당신을 위해 작동하지 않는 경우,이 컬렉션과 함께 할 것입니다 :

    그런 다음 두 번째의 데이터로 첫 컬렉션에서 노드 ID를 대체 할 단순 반복 기능을 쓸 수 있습니다. 2 개 쿼리 간단한 클라이언트 측 처리.

    작은 업데이트 :

    당신은 좀 더 유연하게 두 번째 모음을 확장 할 수 있습니다 :

    {_id : 2, 데이터 "무언가"아이들 [3, 7, 부모가 : [1, 12, 13}

    이 방법 당신은 어떤 잎에서 검색을 시작할 수 있습니다. 그리고, 사용은 상단 또는 나무의이 부분의 바닥에 도착하는지도-줄일 수 있습니다.

  2. ==============================

    2.당신은 (https://www.mongodb.com/blog/post/introducing-version-40-mongoose-nodejs-odm와) 지금이 작업을 수행 할 수 있습니다

    당신은 (https://www.mongodb.com/blog/post/introducing-version-40-mongoose-nodejs-odm와) 지금이 작업을 수행 할 수 있습니다

    var mongoose = require('mongoose');
    // mongoose.Promise = require('bluebird'); // it should work with native Promise
    mongoose.connect('mongodb://......');
    
    var NodeSchema = new mongoose.Schema({
        children: [{type: mongoose.Schema.Types.ObjectId, ref: 'Node'}],
        name: String
    });
    
    var autoPopulateChildren = function(next) {
        this.populate('children');
        next();
    };
    
    NodeSchema
    .pre('findOne', autoPopulateChildren)
    .pre('find', autoPopulateChildren)
    
    var Node = mongoose.model('Node', NodeSchema)
    var root=new Node({name:'1'})
    var header=new Node({name:'2'})
    var main=new Node({name:'3'})
    var foo=new Node({name:'foo'})
    var bar=new Node({name:'bar'})
    root.children=[header, main]
    main.children=[foo, bar]
    
    Node.remove({})
    .then(Promise.all([foo, bar, header, main, root].map(p=>p.save())))
    .then(_=>Node.findOne({name:'1'}))
    .then(r=>console.log(r.children[1].children[0].name)) // foo
    

    몽구스없이 간단한 대안 :

    function upsert(coll, o){ // takes object returns ids inserted
        if (o.children){
            return Promise.all(o.children.map(i=>upsert(coll,i)))
                .then(children=>Object.assign(o, {children})) // replace the objects children by their mongo ids
                .then(o=>coll.insertOne(o))
                .then(r=>r.insertedId);
        } else {
            return coll.insertOne(o)
                .then(r=>r.insertedId);
        }
    }
    
    var root = {
        name: '1',
        children: [
            {
                name: '2'
            },
            {
                name: '3',
                children: [
                    {
                        name: 'foo'
                    },
                    {
                        name: 'bar'
                    }
                ]
            }
        ]
    }
    upsert(mycoll, root)
    
    
    const populateChildren = (coll, _id) => // takes a collection and a document id and returns this document fully nested with its children
      coll.findOne({_id})
        .then(function(o){
          if (!o.children) return o;
          return Promise.all(o.children.map(i=>populateChildren(coll,i)))
            .then(children=>Object.assign(o, {children}))
        });
    
    
    const populateParents = (coll, _id) => // takes a collection and a document id and returns this document fully nested with its parents, that's more what OP wanted
      coll.findOne({_id})
        .then(function(o){
          if (!o.parent) return o;
          return populateParents(coll, o.parent))) // o.parent should be an id
            .then(parent => Object.assign(o, {parent})) // replace that id with the document
        });
    
  3. ==============================

    3.또 다른 방법은 Model.populate ()가 약속을 반환하고 다른 약속과 약속을 이행 할 수 있다는 사실을 활용하는 것입니다.

    또 다른 방법은 Model.populate ()가 약속을 반환하고 다른 약속과 약속을 이행 할 수 있다는 사실을 활용하는 것입니다.

    당신은 재귀를 통해 문제의 노드를 채울 수 있습니다 :

    Node.findOne({ "_id": req.params.id }, function(err, node) {
      populateParents(node).then(function(){
        // Do something with node
      });
    });
    

    populateParents는 다음과 같이 수 :

    var Promise = require('bluebird');
    
    function populateParents(node) {
      return Node.populate(node, { path: "parent" }).then(function(node) {
        return node.parent ? populateParents(node.parent) : Promise.fulfill(node);
      });
    }
    

    그것은뿐만 가장 성능이 좋은 방법입니다,하지만 N이 작은 경우이 작동합니다.

  4. ==============================

    4.이제 몽구스 4이 수행 할 수 있습니다. 지금 당신은 하나의 레벨보다 더 깊은 재귀 수 있습니다.

    이제 몽구스 4이 수행 할 수 있습니다. 지금 당신은 하나의 레벨보다 더 깊은 재귀 수 있습니다.

    User.findOne({ userId: userId })
        .populate({ 
            path: 'enrollments.course',
            populate: {
                path: 'playlists',
                model: 'Playlist',
                populate: {
                    path: 'videos',
                    model: 'Video'
                }
            } 
        })
        .populate('degrees')
        .exec()
    

    당신은 여기에서 몽구스 깊은 채우기위한 공식 문서를 찾을 수 있습니다.

  5. ==============================

    5.나는 fzembow의 솔루션 @ 시도하지만 깊은 인구가 경로에서 개체를 반환하는 것 같았다. 내 경우에는 내가 재귀 적으로 객체를 채우는,하지만 매우 동일한 개체를 반환 할 필요가 있었다. 나는 그런 식으로했다 :

    나는 fzembow의 솔루션 @ 시도하지만 깊은 인구가 경로에서 개체를 반환하는 것 같았다. 내 경우에는 내가 재귀 적으로 객체를 채우는,하지만 매우 동일한 개체를 반환 할 필요가 있었다. 나는 그런 식으로했다 :

    // Schema definition
    const NodeSchema = new Schema({
            name: { type: String, unique: true, required: true },
            parent: { type: Schema.Types.ObjectId, ref: 'Node' },
        });
    
    const Node =  mongoose.model('Node', NodeSchema);
    
    
    
    
    
    // method
    const Promise = require('bluebird');
    
    const recursivelyPopulatePath = (entry, path) => {
        if (entry[path]) {
            return Node.findById(entry[path])
                .then((foundPath) => {
                    return recursivelyPopulatePath(foundPath, path)
                        .then((populatedFoundPath) => {
                            entry[path] = populatedFoundPath;
                            return Promise.resolve(entry);
                        });
                });
        }
        return Promise.resolve(entry);
    };
    
    
    //sample usage
    Node.findOne({ name: 'someName' })
            .then((category) => {
                if (category) {
                    recursivelyPopulatePath(category, 'parent')
                        .then((populatedNode) => {
                            // ^^^^^^^^^^^^^^^^^ here is your object but populated recursively
                        });
                } else {
                    ...
                }
            })
    

    그것은 매우 효율적이 아니다 조심하십시오. 자주 또는 깊은 수준에서 같은 쿼리를 실행해야하는 경우에, 당신은 당신의 디자인을 재고해야

  6. ==============================

    6.이 caub의 대답과 훌륭한 솔루션에 대한보다 정직 접근 방식입니다. 내가 함께이 버전을 넣어 그래서 조금 어려운 처음의 의미를 발견.

    이 caub의 대답과 훌륭한 솔루션에 대한보다 정직 접근 방식입니다. 내가 함께이 버전을 넣어 그래서 조금 어려운 처음의 의미를 발견.

    중요한, 당신은 일이 솔루션을 장소에 모두 'findOne'과 '발견'미들웨어 후크가 필요합니다. *

    * 또한, 모델 정의는 미들웨어 정의 뒤에 와야합니다 *

    const mongoose = require('mongoose');
    
    const NodeSchema = new mongoose.Schema({
        children: [mongoose.Schema.Types.ObjectId],
        name: String
    });
    
    const autoPopulateChildren = function (next) {
        this.populate('children');
        next();
    };
    
    NodeSchema
        .pre('findOne', autoPopulateChildren)
        .pre('find', autoPopulateChildren)
    
    
    const Node = mongoose.model('Node', NodeSchema)
    
    const root = new Node({ name: '1' })
    const main = new Node({ name: '3' })
    const foo = new Node({ name: 'foo' })
    
    root.children = [main]
    main.children = [foo]
    
    
    mongoose.connect('mongodb://localhost:27017/try', { useNewUrlParser: true }, async () => {
        await Node.remove({});
    
        await foo.save();
        await main.save();
        await root.save();
    
        const result = await Node.findOne({ name: '1' });
    
        console.log(result.children[0].children[0].name);
    });
    
  7. from https://stackoverflow.com/questions/26041262/mongoose-recursive-populate by cc-by-sa and MIT license