Лучшая практика для Mon goose обновлений базы данных в Node.js? - PullRequest
0 голосов
/ 31 марта 2020

Допустим, у меня есть следующая схема:

const mySchema = mongoose.Schema({
    _id: mongoose.Schema.Types.ObjectId,
    date: Number,
    data: {
        field1 : Number,
        field2 : Number
    }
});

И я хочу обновить field2 с "myAwesomeValue" для документа, имеющего "myAwesomeDate". Мой текущий код внутри функции async / await:

// V1
var myAwesomeDocument = await myModel.findOneAndUpdate(
    {date: myAwesomeDate},            //selector
    {$set: {                          //update
        'data.field2': myAwesomeValue
    }},
    {                                 //options
        upsert: false,
        new: true
    }
).exec();

Этот код позволяет мне работать с обновленным документом.

Если я не ошибаюсь, следующий код работает аналогично, но его следует избегать, поскольку он сначала загружает документ на клиентскую сторону (следовательно, менее эффективен) ( Пн goose) Разница между .save () и использованием update () ):

// V2
var myAwesomeDocument = await myModel.findOne({date: myAwesomeDate}).exec();
myAwesomeDocument.data.field2 = myAwesomeValue;
myAwesomeDocument = await myAwesomeDocument.save().exec();

Теперь я хотел бы сделать мой код более читабельным, используя .doSomething () fashion:

// V3 (any code mistake here ?)
var myAwesomeDocument = await myModel.findOne({date: myAwesomeDate})
    .set(
        {'data.field2': myAwesomeValue},
        {upsert: false, new: true}
    )
    .exec();

Мой вопрос в первую очередь об эффективности, а затем о читабельности кода:

  • Существует ли более эффективный код, чем V1?
  • V3 выполняет такое же обновление? Это так же эффективно, как V1?
  • Есть ли более эффективный и читаемый способ написания V3?

Спасибо за любой ответ!

1 Ответ

1 голос
/ 05 апреля 2020

Из приведенных вами примеров наиболее эффективным является v1,

  • V1. Под капотом запускается только один запрос, mon go s findAndModify .
  • V2 Требуется 2 запроса для завершения запланированного обновления. findOne затем updateOne.
  • V3 не выполняет предназначенную для этого задачу, а просто выполняет findOne без выполнения какой-либо операции обновления для найденного документа.

Правильная версия V3 будет быть следующим:

instance = await model
    .findOneAndUpdate({date})
    .set({'data.f2': f1},)
    .setOptions({new: true})
    .exec();

Объяснение: Пн goose findOneAndUpdate возвращает Запрос (см. примеры). Затем мы используем методы Query для установки операции обновления и параметры.

В заключение вы можете go использовать либо V1, либо предоставленный мной обновленный V3, поскольку они используют те же вызовы базы данных в капот.

Вы всегда можете использовать mon goose .set ('debug': true) для анализа того, какие запросы фактически отправляются в базу данных.

To Сделайте резервную копию того, что я сказал выше, вот фрагмент кода, который я использовал для запуска тестов. Вы можете вызвать его как:

const uri = 'mongodb://localhost:27017/test-sav';
const mongoose = require('mongoose');

const bombardCount = process.argv[2] ? parseInt(process.argv[2]) : 1;
const debug = process.argv[3] === 'true';

const date = 1234567;
const f1 = 1;
const f2 = 2;
let model;

(async function () {
    await mongoose.connect(uri, {useNewUrlParser: true, useUnifiedTopology: true});
    const schema = new mongoose.Schema({
        date: Number,
        data: {
            f1: Number,
            f2: Number,
        }
    });
    model = mongoose.model('model', schema);

    console.log('### START ###')
    const doc1 = await bombard(v1, bombardCount);
    console.log(doc1);
    const doc2 = await bombard(v2, bombardCount);
    console.log(doc2);
    const doc3 = await bombard(v3, bombardCount);
    console.log(doc3);
    const doc4 = await bombard(v4, bombardCount);
    console.log(doc4);
    console.log('### END ###');
})().catch(error => console.error(error)).then(() => process.exit(1));

async function v1() {
    console.log('### V1 ###\n');
    await beforeEach();
    console.time('### V1 ###');
    let instance =  await model.findOneAndUpdate(
        {date},
        {
            $set: {
                'data.f2': f1,
            },
        },
        {
            upsert: false,
            new: true
        }
    ).exec();
    console.timeEnd('### V1 ###');
    await afterEach();
    return instance;
}

async function v2() {
    console.log('### V2 ###\n');
    await beforeEach();
    console.time('### V2 ###');
    let instance = await model.findOne({date}).exec();
    instance.data.f2 = f1;
    instance = await instance.save();
    console.timeEnd('### V2 ###');
    await afterEach();
    return instance;
}

async function v3() {
    console.log('### V3 ###\n');
    console.time('### V3 ###');
    await beforeEach();
    let instance =  await model
        .findOne({date})
        .set(
            {'data.f2': f1},
            {upsert: false, new: true}
        ).exec();
    console.timeEnd('### V3 ###');
    await afterEach();
    return instance
}

async function v4() {
    console.log('### V4 ###\n');
    console.time('### V4 ###');
    await beforeEach();
    let instance = await model
        .findOneAndUpdate({date})
        .set({'data.f2': f1})
        .setOptions({new: true})
        .exec();
    console.timeEnd('### V4 ###');
    await afterEach();
    return instance;
}

async function beforeEach() {
    await new model({
        date,
        data: {
            f1,
            f2,
        },
    }).save();
    mongoose.set('debug', debug);
}

async function afterEach() {
    mongoose.set('debug', debug);
    await model.deleteMany({});
}

async function bombard(f, times) {
    let x;
    for (let i = 0; i < times; i++) {
        x = await f();
    }
    return x;
}

node index.js [repeats?=number] [debug?=true/false]

...