2017-01-18 52 views
2

我正試圖創建一個「findOneAndUpdate」查詢來啓動預驗證中間件。預驗證中間件後創建或更新字段?

需要這些中間件來驗證給定的座標並在請求的主體中未提供時創建'id'字段(是的,而不是的'_id')。

正如您在下面看到的(請參閱代碼中的註釋),我很接近,但不明白爲什麼mongo引發的錯誤是重複密鑰變體。

也許承諾不是去這裏的方式,雖然他們讓我來實現方式比鏈接pre中間件。

這裏是我的快遞路線:

/* POST /geolocs */ 
router.post('/', function(req, res, next) { 

    Geoloc.create(req.body, function(err, post) { 
    if (err) return next(err); 

    res.status(201).json(post); 
    }); 
}); 

這裏是我的架構:

var GeolocSchema = new mongoose.Schema({ 
    id: { 
    type: String, 
    required: true, 
    unique: true 
    }, 
    location: { 
    type: { 
     type: String, 
     default: 'Point', 
     required: true 
    }, 
    coordinates: { 
     type: [Number], 
     required: true 
    } 
    }, 
    count: Number 
}); 

GeolocSchema.index({ 
    location: '2dsphere' 
}); 

預驗證的中間件:

// Before validation, check given coordinates for errors. 
GeolocSchema.pre('validate', function(next) { 
    coord = this.location.coordinates 
    if (this.location.type && coord) { 
    if (Array.isArray(coord) && coord.length === 2) { 
     lat = coord[1]; 
     lon = coord[0]; 
     if ((-90 <= lat && lat <= 90) && (-180 <= lat && lat <= 180)) next(); 
    } 
    } 
    var err = new Error('...'); // Long error text, irrelevant here 
    err.status = 400; 
    next(err); 
}); 

// Then, if no 'id' is given, create it. 
GeolocSchema.pre('validate', function(next) { 
    if (!this.id) { 
    strLat = this.location.coordinates[1].toFixed(3).replace('.', '_'); 
    strLon = this.location.coordinates[0].toFixed(3).replace('.', '_'); 
    this.id = strLat + '-' + strLon; 
    } 
    next(); 
}); 

我很想能夠做的就是添加低於上述以下幾點:

// Here, using the validate or save hook doesn't change anything. 
GeolocSchema.pre('validate', function(next) { 
    var prom = Geoloc.findOne({ 
    'id': { 
     $eq: this.id 
    } 
    }).exec(); 

    prom.then((err, geoloc) => { // Arrow function here to access 'this' 
    if (err) next(err); 
    // If no geoloc was found, go ahead and save. 
    if (!geoloc) next(); 

    // Else, update to increment the count (THIS WORKS). 
    return Geoloc.update({'id': this.id}, {$inc: {count: 1}}).exec(); 

    }).then((toto) => {  
    // This doesn't work, the error thrown by mongo is a duplicate key error (E11000). 
    if (toto) next(new Error('204')); 
    else next(new Error("Something that shouldn't happen, happened...")); 
    }); 
}); 

回答

1

的問題是,pre() & post()中間件save & validate沒有被update()findOneAnUpdate()等,其在提到執行docs以及存在GitHub issue

然而,pre('findOneAndUpdate')post('findOneAndUpdate')掛鉤可用的(不知道,如果鉤update作品)。

希望這可以幫助你。

+0

是的,我知道。很確定我已經閱讀了幾乎所有關於這個主題的文檔:-D但是,爲了執行'find'操作,我需要在預驗證步驟中構建的'id' ...或者我錯過了什麼? – Gormador

+0

另外,我認爲確實有一個'更新'鉤子。請參閱[這裏](http://mongoosejs.com/docs/middleware.html#notes)。 – Gormador

+0

是的,但是如果你在給出的Github鏈接上閱讀了這個問題,開發人員有一個評論說,更新的鉤子不是他們的。最好的嘗試看看。如果它有效,它就在那裏。如果沒有,那麼麻煩就開始了! :-) –

1

爲了詳細說明通過Santanu比斯瓦斯接受的答案,這裏是基於它的工作代碼:

注意,仍然有一些怪癖吧,特別是關於什麼是在響應發送回客戶端,但MongoDB操作按預期發生。

快遞航線

router.post('/', function(req, res, next) { 
    Geoloc.findOneAndUpdate({ 
    id: req.body.id 
    }, req.body, { 
    runValidators: true, // MANDATORY OPTION 
    upsert: true, 
    context: 'query'  // MANDATORY OPTION 
    }, function(err, post) { 
    if (err) return next(err); 

    res.status(201).json(post); 
    }); 
}); 

findOneAndUpdate中間件

GeolocSchema.pre('findOneAndUpdate', function(next) { 

    doc = this.getUpdate(); 

    coord = doc.location.coordinates; 
    if (doc.location.type && coord) { 
    if (Array.isArray(coord) && coord.length === 2) { 
     lat = coord[1]; 
     lon = coord[0]; 
     if ((-90 <= lat && lat <= 90) && (-180 <= lat && lat <= 180)) { 
     return next(); 
     } 
    } 
    } 
    var err = new Error('...'); // Long error text, irrelevant here 
    err.status = 400; 
    next(err); 
}); 

兼:

GeolocSchema.pre('findOneAndUpdate', function(next) { 
    doc = this.getUpdate(); 
    query = this.getQuery(); 

    if (!query.id) { 
    strLat = doc.location.coordinates[1].toFixed(3).replace('.', '_'); 
    strLon = doc.location.coordinates[0].toFixed(3).replace('.', '_'); 
    query.id = strLat + '-' + strLon; 
    doc.id = query.id; 
    } 

    var prom = Geoloc.findOne({ 
    'id':query.id 
    }).exec(); 

    prom.then((geoloc, err) => { 
    if (err) return next(err); 
    if (!geoloc) return next(); 

    doc.count += geoloc.count; 

    next(); 
    }); 
});