복붙노트

[MONGODB] 업데이트 arrayFilters와 MongoDB를에서 하위 문서를 중첩

MONGODB

업데이트 arrayFilters와 MongoDB를에서 하위 문서를 중첩

나는 다른 배열 안에 배열 내부 문서를 수정해야합니다. 나는 MongoDB를 동시에 여러 배열에 반복 '$'여러 지원하지 않습니다 알고 있지만 그들은 그것에 대해 arrayFilters을 소개했다. 참조 : https://jira.mongodb.org/browse/SERVER-831

MongoDB를의 샘플 코드 :

db.coll.update({}, {$set: {“a.$[i].c.$[j].d”: 2}}, {arrayFilters: [{“i.b”: 0}, {“j.d”: 0}]})
Input: {a: [{b: 0, c: [{d: 0}, {d: 1}]}, {b: 1, c: [{d: 0}, {d: 1}]}]}
Output: {a: [{b: 0, c: [{d: 2}, {d: 1}]}, {b: 1, c: [{d: 0}, {d: 1}]}]}

여기에 문서를 설정하는 방법은 다음과 같습니다

{
    "_id" : ObjectId("5a05a8b7e0ce3444f8ec5bd7"),
    "name" : "support",
    "contactTypes" : {
        "nonWorkingHours" : [],
        "workingHours" : []
    },
    "workingDays" : [],
    "people" : [ 
        {
            "enabled" : true,
            "level" : "1",
            "name" : "Someone",
            "_id" : ObjectId("5a05a8c3e0ce3444f8ec5bd8"),
            "contacts" : [ 
                {
                    "_id" : ObjectId("5a05a8dee0ce3444f8ec5bda"),
                    "retries" : "1",
                    "priority" : "1",
                    "type" : "email",
                    "data" : "some.email@email.com"
                }
            ]
        }
    ],
    "__v" : 0
}

다음 스키마는 다음과 같습니다

const ContactSchema = new Schema({
    data: String,
    type: String,
    priority: String,
    retries: String
});

const PersonSchema = new Schema({
    name: String,
    level: String,
    priority: String,
    enabled: Boolean,
    contacts: [ContactSchema]
});

const GroupSchema = new Schema({
    name: String,
    people: [PersonSchema],
    workingHours: { start: String, end: String },
    workingDays: [Number],
    contactTypes: { workingHours: [String], nonWorkingHours: [String] }
});

나는 연락처를 업데이트해야합니다. 이것은 내가 arrayFilters를 사용하여 시도 것입니다 :

Group.update(
    {},
    {'$set': {'people.$[i].contacts.$[j].data': 'new data'}},
    {arrayFilters: [
        {'i._id': mongoose.Types.ObjectId(req.params.personId)},
        {'j._id': mongoose.Types.ObjectId(req.params.contactId)}]},
    function(err, doc) {
        if (err) {
            res.status(500).send(err);
        }
        res.send(doc);
    }
);

이 문서는 업데이트되지 않습니다 나는이 응답을 얻을 :

{
    "ok": 0,
    "n": 0,
    "nModified": 0
}

내가 무엇을 잘못하고 있지?

해결법

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

    1.함께 arrayFilters 옵션을 따라서 위치 필터링 $ [<식별자>] 실제로 MongoDB를 3.5.12 이후 그리고이 실제로 정식으로 출시 될 MongoDB를 3.6 시리즈의 현재 릴리스 후보에서 개발 릴리즈 시리즈에서 제대로 작동 않습니다. 유일한 문제는 물론 것은 사용의 "드라이버가"실제로 아직까지 잡은하지 않은 것입니다.

    함께 arrayFilters 옵션을 따라서 위치 필터링 $ [<식별자>] 실제로 MongoDB를 3.5.12 이후 그리고이 실제로 정식으로 출시 될 MongoDB를 3.6 시리즈의 현재 릴리스 후보에서 개발 릴리즈 시리즈에서 제대로 작동 않습니다. 유일한 문제는 물론 것은 사용의 "드라이버가"실제로 아직까지 잡은하지 않은 것입니다.

    이미 MongoDB를 가진 중첩 된 배열을 업데이트에 배치 한 동일한 내용을 다시 반복 :

    이 모든 수단이다 arrayFilters의 정의와 .update를 현재의 "드라이버"구현 () 실제로 "제거합니다"필요한 인수가. NodeJS를 들어이 드라이버의 3.x를 릴리스 시리즈에서 해결 될 것입니다, 물론 "몽구스는"다음 가능성이 업데이트 된 드라이버에 대한 자신의 의존성을의 구현이 출시 된 후 약간의 시간이 걸릴 것이다 것 다음 더 이상 "스트립" 이러한 행동.

    이 구현 된 드라이버 방법을 우회하기 때문에 당신은 그러나 여전히 기본 "업데이트 명령"구문 사용에 다시 놓는 방법으로, 지원되는 서버 인스턴스에서이 작업을 실행할 수 있습니다 :

    const mongoose = require('mongoose'),
          Schema = mongoose.Schema,
          ObjectId = mongoose.Types.ObjectId;
    
    mongoose.Promise = global.Promise;
    mongoose.set('debug',true);
    
    const uri = 'mongodb://localhost/test',
          options = { useMongoClient: true };
    
    const contactSchema = new Schema({
      data: String,
      type: String,
      priority: String,
      retries: String
    });
    
    const personSchema = new Schema({
      name: String,
      level: String,
      priority: String,
      enabled: Boolean,
      contacts: [contactSchema]
    });
    
    const groupSchema = new Schema({
      name: String,
      people: [personSchema],
      workingHours: { start: String, end: String },
      workingDays: { type: [Number], default: undefined },
      contactTypes: {
        workingHours: { type: [String], default: undefined },
        contactTypes: { type: [String], default: undefined }
      }
    });
    
    const Group = mongoose.model('Group', groupSchema);
    
    function log(data) {
      console.log(JSON.stringify(data, undefined, 2))
    }
    
    (async function() {
    
      try {
    
        const conn = await mongoose.connect(uri,options);
    
        // Clean data
        await Promise.all(
          Object.entries(conn.models).map(([k,m]) => m.remove() )
        );
    
        // Create sample
    
        await Group.create({
          name: "support",
          people: [
            {
              "_id": ObjectId("5a05a8c3e0ce3444f8ec5bd8"),
              "enabled": true,
              "level": "1",
              "name": "Someone",
              "contacts": [
                {
                  "type": "email",
                  "data": "adifferent.email@example.com"
                },
                {
                  "_id": ObjectId("5a05a8dee0ce3444f8ec5bda"),
                  "retries": "1",
                  "priority": "1",
                  "type": "email",
                  "data": "some.email@example.com"
                }
              ]
            }
          ]
        });
    
        let result = await conn.db.command({
          "update": Group.collection.name,
          "updates": [
            {
              "q": {},
              "u": { "$set": { "people.$[i].contacts.$[j].data": "new data" } },
              "multi": true,
              "arrayFilters": [
                { "i._id": ObjectId("5a05a8c3e0ce3444f8ec5bd8") },
                { "j._id": ObjectId("5a05a8dee0ce3444f8ec5bda") }
              ]
            }
          ]
        });
    
        log(result);
    
        let group = await Group.findOne();
        log(group);
    
      } catch(e) {
        console.error(e);
      } finally {
        mongoose.disconnect();
      }
    
    })()
    

    즉 서버를 통해 직접 "명령"을 전송하기 때문에, 우리는 예상 업데이트가 사실 걸릴 장소에서하는 참조 :

    Mongoose: groups.remove({}, {})
    Mongoose: groups.insert({ name: 'support', _id: ObjectId("5a06557fb568aa0ad793c5e4"), people: [ { _id: ObjectId("5a05a8c3e0ce3444f8ec5bd8"), enabled: true, level: '1', name: 'Someone', contacts: [ { type: 'email', data: 'adifferent.email@example.com', _id: ObjectId("5a06557fb568aa0ad793c5e5") }, { _id: ObjectId("5a05a8dee0ce3444f8ec5bda"), retries: '1', priority: '1', type: 'email', data: 'some.email@example.com' } ] } ], __v: 0 })
    { n: 1,
      nModified: 1,
      opTime:
       { ts: Timestamp { _bsontype: 'Timestamp', low_: 3, high_: 1510364543 },
         t: 24 },
      electionId: 7fffffff0000000000000018,
      ok: 1,
      operationTime: Timestamp { _bsontype: 'Timestamp', low_: 3, high_: 1510364543 },
      '$clusterTime':
       { clusterTime: Timestamp { _bsontype: 'Timestamp', low_: 3, high_: 1510364543 },
         signature: { hash: [Object], keyId: 0 } } }
    Mongoose: groups.findOne({}, { fields: {} })
    {
      "_id": "5a06557fb568aa0ad793c5e4",
      "name": "support",
      "__v": 0,
      "people": [
        {
          "_id": "5a05a8c3e0ce3444f8ec5bd8",
          "enabled": true,
          "level": "1",
          "name": "Someone",
          "contacts": [
            {
              "type": "email",
              "data": "adifferent.email@example.com",
              "_id": "5a06557fb568aa0ad793c5e5"
            },
            {
              "_id": "5a05a8dee0ce3444f8ec5bda",
              "retries": "1",
              "priority": "1",
              "type": "email",
              "data": "new data"            // <-- updated here
            }
          ]
        }
      ]
    }
    

    그래서 바로 "지금"[1] "기성품"사용할 수있는 드라이버는 실제로 .update를 구현하지 않는 () 또는 실제로 필요한 arrayFilters 인수 통과와 호환되는 방법으로 다른 구현 대응을합니다. 당신이 개발 시리즈 또는 해제 candiate 서버 "노는"경우에 따라서, 당신은 정말 아니라 "최첨단"과 출시되지 않은 드라이버와 함께 일하게 준비해야한다.

    어떤 드라이버에서 설명하지만 실제로 명령을 변경하지 않을 발행되는 올바른 형태로,이 작업을 수행 할 수 있습니다.

  2. from https://stackoverflow.com/questions/47225132/update-nested-subdocuments-in-mongodb-with-arrayfilters by cc-by-sa and MIT license