Задача pg-promise и карта с несколькими вложенными запросами одного уровня

Я использую node и pg-promise для создания базового API для отдыха, и у меня возникли проблемы с запросом всех данных для конкретного пользователя. Ниже показано, как должны выглядеть возвращаемые данные. Адрес, номер телефона и навыки находятся в отдельных таблицах. У меня нет проблем с получением адресов или номеров телефонов, это просто навыки, которых я не могу получить. Не совсем уверен, как иметь несколько запросов после основного запроса, который заставляет пользователя получать все эти другие поля, см. Прикрепленный код для справки, и я буду рад ответить на любые вопросы.


{
"user_id": 1,
"first_name": "Eugene",
"last_name": "Hanson",
"display_name": "Eugene Hanson",
"email": "[email protected]",
"hash": "88a6aa27235d2e39dd9cb854cc246487147050f265578a3e1aee35be5db218ef",
"privilege_id": 14,
"seniority": 1,
"birthday": "19-11-1940 00:00:00.0",
"shift_count_total": 587,
"shift_count_year": 62,
"address_id": 1,
"street": "92 Schmedeman Lane",
"city": "Fort Smith",
"state": "AR",
"zip": 72905,
"phone_numbers": [
  {
    "phone_number": "62-(705)636-2916",
    "name": "PRIMARY"
  }
],
"skills": [
    "Head Audio",
    "Head Video",
    "Head Electrician",
    "Carpenter",
    "rigger"
    ]
}

    function getAllUsers() {
    // console.time("answer time")
    var deferred = Q.defer();
    db.task(t => {
        return t.map('SELECT * \
                        FROM users \
                        JOIN addresses \
                        ON users.address_id = addresses.address_id',[], user => {
            var user_id = user.user_id;
            // console.log(user_id)
            console.time("answer time")
            return t.manyOrNone('SELECT phone_numbers.phone_number, phone_types.name \
                                    FROM users \
                                    JOIN users_phone_numbers \
                                    ON users.user_id = users_phone_numbers.user_id \
                                    JOIN phone_numbers \
                                    ON users_phone_numbers.phone_id = phone_numbers.phone_id \
                                    JOIN phone_types \
                                    ON phone_numbers.phone_type_id = phone_types.phone_type_id \
                                    WHERE users.user_id = $1', user.user_id)
                                    .then(phone_numbers=> {
                                        // logger.log('info', phone_numbers)
                                        user.phone_numbers = phone_numbers;
                                        return user;
                                    })
        }).then(t.batch);
    })
    .then(data => {
        // console.log(data)
        console.timeEnd("answer time");
        var response = {code: "200", 
                        message: "", 
                        payload: data};
        deferred.resolve(response);
    })
    .catch(error => {
    var response = {code: error.code, 
                    message: error.message, 
                    payload: ""};
    logger.log('error', error)
    deferred.reject(response)
});


person seanpdiaz    schedule 04.04.2017    source источник
comment
И в чем проблема? Что вы получаете вместо этого? Где ваш обработчик .catch? Вы должны правильно закончить свой вопрос, прежде чем на него можно будет ответить;)   -  person vitaly-t    schedule 04.04.2017
comment
Отредактировал исходный пост, включив в него .catch, который был случайно пропущен при копировании и вставке. Извините, что мой вопрос не был ясен, я пытаюсь просмотреть всех пользователей, а затем получить все связанные с ними данные из различных других таблиц, затем объединить их обратно в запись пользователя и отправить обратно клиенту. В настоящее время я могу сделать это только для телефонных номеров с картой, не знаю, как добавить еще один запрос, который также будет использоваться на карте, чтобы сказать, получить навыки. Любые советы приветствуются и не стесняйтесь, дайте мне знать, если мне нужно уточнить больше.   -  person seanpdiaz    schedule 04.04.2017
comment
Я хотел бы попробовать усложнить свой SQL; используя некоторые LEFT JOIN и .reduce() в коде. Также можно использовать array_agg postgres lorenstewart.me/2017/12/ 04/postgresqls-array_agg-function, который, похоже, может помочь вам денормализовать реляционные данные в запросе.   -  person grantwparks    schedule 03.10.2019


Ответы (1)


Я автор pg-promise.


Упрощенная версия вашей функции будет:

function getAllUsers() {
    return db.task(t => {
        return t.map('SELECT * FROM users', [], user => {
            return t.batch([
                t.any('SELECT * FROM phones'), // plus formatting params
                t.any('SELECT * FROM skills'), // plus formatting params
            ])
                .then(data => {
                    user.phones = data[0];
                    user.skills = data[1];
                    return user;
                });
        }).then(t.batch);
    });
}

getAllUsers()
    .then(data => {
        // data tree
    })
    .catch(error => {
        // error
    });

И если вы используете bluebird в качестве библиотеки промисов, вы можете заменить этот код:

.then(data => {
    user.phones = data[0];
    user.skills = data[1];
    return user;
});

с этим:

.spread((phones, skills) => {
    user.phones = phones;
    user.skills = skills;
    return user;
});

И не используйте такие вещи, как var deferred = Q.defer();, он там не нужен. Библиотека уже основана на промисах.

Для высокопроизводительной альтернативы см.: получить таблицу JOIN как массив результатов с PostgreSQL/NodeJS.

person vitaly-t    schedule 04.04.2017
comment
Спасибо, это именно то, что мне было нужно, я рыскал по всему Интернету, как собрать эти кусочки вместе, но не смог найти четкой идеи, а вы смогли пролить на нее свет. - person seanpdiaz; 04.04.2017
comment
@seanpdiaz Добро пожаловать. Я обновил ответ ;) - person vitaly-t; 04.04.2017
comment
Спасибо, что указали мне правильное направление в том, как сделать мой вызов более производительным, я смог использовать предоставленную вами ссылку и сократить время ответа с 12 000 мс до менее 500 мс. Спасибо еще раз. - person seanpdiaz; 05.04.2017