Использовать динамическую коллекцию / модель для параллельных запросов

1

Это использует Mongoose и Async

Я async.parallel несколько функций в async.parallel вызове, и они в основном выглядят так:

var winston = require('winston'),
    async = require('async'),
    //Mongo Models
    Product = require('../models/product'),
    Runway = require('../models/runway'),
    Conversion = require('../models/conversion'),
    Engagement = require('../models/engagement');

var user = {
    email: req.params.email
};
var score = {
    product: null,
    runway: null,
    conversion: null,
    engagement: null
};
var scoreInfo = 'email score';

findProductByID = function(id, filter, callback) {
    Product.findOne(id, filter).sort({
        field: 'asc',
        _id: -1
    }).limit(1).exec(function(err, docs) {
        if (docs !== undefined && docs !== null && docs.score !== undefined && docs.score !== null) {
            score.product = docs.score;
            console.log("Product Score for " + user.email + " : " + score.product);
            callback(null, score.product);
        } else {
            console.log("Product score not found, setting to 0");
            score.product = 0;
            callback(null, score.product);
        }
    });
};

findRunwayByID = function(id, filter, callback) {
    Runway.findOne(id, filter).sort({
        field: 'asc',
        _id: -1
    }).limit(1).exec(function(err, docs) {
        if (docs !== undefined && docs !== null && docs.score !== undefined && docs.score !== null){
            score.runway = docs.score;
            console.log("Runway Score for " + user.email + " : " + score.runway);
            callback(null, score.runway);
        } else {
            console.log("Runway score not found, setting to 0");
            score.runway = 0;
            callback(null, score.runway);
        }
    });
};

findConversionByID = function(id, filter, callback) {
    Conversion.findOne(id, filter).sort({
        field: 'asc',
        _id: -1
    }).limit(1).exec(function(err, docs) {
        if (docs !== undefined && docs !== null && docs.score !== undefined && docs.score !== null){
            score.conversion = docs.score;
            console.log("Conversion Score for " + user.email + " : " + score.conversion);
            callback(null, score.conversion);
        } else {
            console.log("Conversion score not found, setting to 0");
            score.conversion = 0;
            callback(null, score.conversion);
        }
    });
};

findEngagementByID = function(id, filter, callback) {
    Engagement.findOne(id, filter).sort({
        field: 'asc',
        _id: -1
    }).limit(1).exec(function(err, docs) {
        if (docs !== undefined && docs !== null && docs.score !== undefined && docs.score !== null){
            score.engagement = docs.score;
            console.log("Engagement Score for " + user.email + " : " + score.engagement);
            callback(null, score.engagement);
        } else {
            console.log("Engagement score not found, setting to 0");
            score.engagement = 0;
            callback(null, score.engagement);
        }
    });
};


async.parallel([
    async.apply(findProductByID, user, scoreInfo),
    async.apply(findRunwayByID, user, scoreInfo),
    async.apply(findConversionByID, user, scoreInfo),
    async.apply(findEngagementByID, user, scoreInfo),
], function(err, result) {
    res.json(score);
});

};

Я попытался использовать квадратные скобки для доступа к свойствам, поэтому я могу просто написать одну функцию для выполнения 4 задач, которая выглядит так:

findScoresByID = function(type, id, filter, callback) {
    [type].findOne(id, filter).sort({
        field: 'asc',
        _id: -1
    }).limit(1).exec(function(err, docs) {
        if (docs !== undefined && docs !== null && docs.score !== undefined && docs.score !== null){
            score[type] = docs.score;
            console.log("Score for " + user.email + " : " + score[type]);
            callback(null, score[type]);
        } else {
            console.log("Score not found, setting to 0");
            score[type] = 0;
            callback(null, score[type];
        }
    });
};

async.parallel([
    async.apply(findScoresByID, "product", user, scoreInfo),
    async.apply(findRunwayByID, "runway", user, scoreInfo),
    async.apply(findRunwayByID, "conversion", user, scoreInfo),
    async.apply(findRunwayByID, "engagement", user, scoreInfo),
], function(err, result) {
    res.json(score);
});

Все в этом работает, кроме второй строки, которая читает [type].findOne...

Когда я пытаюсь использовать это (даже с жестко запрограммированным именем, например ["engagement"].findOne... а не переменная/аргумент [type]), я получаю сообщение об ошибке, которое читает TypeError: ["engagement"].findOne is not a function (Кроме того, удаление точки в целом приводит к ошибке "неожиданного идентификатора")

Я читал предыдущие примеры в StackOverflow, которые, казалось, использовали это так, как я предполагаю, но я не могу заставить его работать. Любая помощь будет принята с благодарностью! Спасибо =)

  • 0
    Много тегов на ваш вопрос. Это на самом деле использует мангуст? Какой type подается в функцию? Это строка? Это мангуст модель? Или что именно? Вы действительно должны показать свое полное предполагаемое использование, потому что есть несколько вещей, которые вы делаете здесь, которые действительно не нужны.
  • 0
    Да, collectionName.findOne(... является функцией findScoresByID("engagement",user... этом примере type будет "engagement" (поэтому findScoresByID("engagement",user... Для строки 2 это специально для модели / коллекции). (вверху у меня есть 4 модели / коллекции, которые называются - engagement = require('../models/engagement'); это одна из рассматриваемых engagement = require('../models/engagement'); .
Показать ещё 5 комментариев
Теги:
mongoose

1 ответ

2
Лучший ответ

У вас на самом деле есть основная ошибка в вашей презумпции того, как динамически использовать модели mongoose, но есть несколько других вещей, которые можно очистить во всем коде.

Так что в первую очередь вам не нужны asyncjs, поэтому просто выбросьте его, потому что nodejs делает все, что вам нужно.

Во втором случае не нужно без необходимости импортировать модели для этой цели, как только они уже зарегистрированы. Пока одна часть кода уже вызывает модели для их регистрации, то здесь нам больше не нужны импорт.

Поэтому, предполагая, что вы назвали вещи последовательно при регистрации таких моделей, как:

module.exports = mongoose.model('Product', productSchema);

Затем мы можем просто вернуть модели с тем же именем, с которыми они были зарегистрированы. Как в:

Promise.all(
  ["Product","Runway","Conversion","Engagement"].map(name =>
    mongoose.model(name).findOne(
     { email: req.params.email },
     'score -_id'
    )
    .sort({ field: 'asc', _id: -1 })
    .then( data => ({ [name.toLowerCase()]: (data) ? data.score : 0 }) )
  )
)
.then( result => res.json(result.reduce((acc,curr) => Object.assign(acc,curr),{})) )
.catch(err => console.error(err));    // Or something with error

Или, если вы действительно настаиваете на использовании async.parallel чтобы сделать то же самое

async.parallel(
  ["Product","Runway","Conversion","Engagement"].map(name => ({
    [name.toLowerCase()]: (callback) =>  mongoose.model(name).findOne(
      { email: req.params.email },
      'score -_id'
    )
    .sort({ field: 'asc', _id: -1 })
    .exec((err,data) => {
        if (err) callback(err);
        callback(null, (data) ? data.score : 0);
      }
    )
  }))
  .reduce((acc,curr) => Object.assign(acc,curr),{}),
  (err,result) => {
    if (err) throw err; // or do something
    res.json(result);
  }
)

Наиболее разумно назвать модели по имени, потому что вы хотите использовать это же имя в выходных ключах возвращаемого объекта (в нижнем регистре, конечно).

Конечно, если вы все еще намерены использовать их, то просто небольшое изменение:

  [Product,Runway,Conversion,Engagement].map(model => ({
    [model.name.toLowerCase()]: (callback) =>  model.findOne(

И это действительно все, что есть. Аналогичное использование без возврата обратного вызова в версии Promises, но вы должны получить общую идею. Он также показывает довольно ясно, каково ваше первоначальное заблуждение "переменных".

Promise.all подходы на самом деле только то, что Promise.all запускает массив обещаний, полученных из списка. Параметр async.parallel работает с объектом с именованными ключами, каждый из которых имеет завернутую функцию, вызывающую результат от каждой модели.

Таким образом, объект ответа обрабатывается "после" возвращенных результатов в случае Promise.all и "во время" в случае async.parallel. Но оба по сути выполняют запросы "параллельно".

  • 0
    Большое спасибо за подробный ответ, он был весьма полезен, и теперь я понимаю, что вы имеете в виду, когда прокомментировали «это не совсем то, как вы его кодируете». Я впал в дурную привычку вызывать модели всякий раз, когда они были нужны, как это было в предыдущем коде - я постараюсь исправить это в будущем. В настоящее время у меня нет каких-либо дополнительных вопросов, поскольку ваш ответ был совершенно ясен; но если я это сделаю, я обязательно протяну руку! Еще раз спасибо

Ещё вопросы

Сообщество Overcoder
Наверх
Меню