Объединить запросы вложенного цикла с результатом родительского массива - pg-обещание - PullRequest
0 голосов
/ 17 мая 2018

Я новичок в узлах (экспресс) и pg-обещание, и не смог выяснить, как добавить результат каждого вложенного запроса (цикла) в основной запрос результата массива json.

У меня есть две таблицы: сообщения и комментарии.

CREATE TABLE post(
id serial,
content text not null,
linkExterno text,
usuario VARCHAR(50) NOT NULL REFERENCES usuarios(alias) ON UPDATE cascade ON DELETE cascade,
multimedia text,
ubicacation VARCHAR(100),
likes integer default 0,
time VARCHAR default now(),
reported boolean default false,
PRIMARY KEY (id)  );

CREATE TABLE comment(
id serial,
idPost integer NOT NULL REFERENCES post(id) ON UPDATE cascade ON DELETE cascade,
acount VARCHAR(50) NOT NULL REFERENCES users(alias) ON UPDATE cascade ON DELETE cascade,
content text NOT NULL,
date date default now(),
PRIMARY KEY (id));

Поэтому я хочу добавить результат каждого комментария к каждому сообщению и вернуть сообщения. У меня есть это, но не работает:

con.task(t => {
    return t.any('select *, avatar from post, users where user= $1 and user = alias ORDER BY time DESC LIMIT 10 OFFSET $2', [username, pos])
    .then(posts => {
        if(posts.length > 0){
            for (var post of posts){
                post.coments = t.any('select * from comment where idPost = $1 ', post.id);
            }
        }
    });
}).then(posts => {
    res.send(posts);
}).catch(error => {
    console.log(error);
});

Есть предложения? PD: я думаю, мой вопрос похож на этот: получить таблицу JOIN как массив результатов с PostgreSQL / NodeJS

ОТВЕТЫ: ​​

Вариант 1 (лучший выбор) :

Выполнение одного запроса через JSON в psql ( JSON-запрос )

См. Ответ @italy-t

OR

Асинхронное получение вложенных данных с использованием ajax.

Вариант 2 :

function buildTree(t) {
        return t.map("select *, avatar from publicacion, usuarios where usuario = $1 and usuario = alias ORDER BY hora DESC LIMIT 10 OFFSET $2", [username, cantidad], posts => {
                return t.any('select * from comentario where idPublicacion = $1', posts.id)
                    .then(coments => {
                        posts.coments = coments;
                        console.log(posts.coments);
                        return posts;
                    });
        }).then(t.batch); // settles the array of generated promises
    }

    router.get('/publicaciones', function (req, res) {
        cantidad = req.query.cantidad || 0; //num de publicaciones que hay
        username = req.session.user.alias;

        con.task(buildTree)
        .then(data => {
            res.send(data);
        })
        .catch(error => {
            console.log(error);
        });
    });

Вариант 3 (асинхронный) :

try{
    var posts = await con.any('select *, avatar from post, users where user = $1 and user = alias ORDER BY time DESC LIMIT 10 OFFSET $2', [username, q])
    for (var post of posts){
        post.coments = await con.any('select * from comment where idPublictcion = $1', post.id);
    }
}catch(e){
    console.log(e);
}

Ответы [ 3 ]

0 голосов
/ 17 мая 2018

Я автор pg-обещания ;)


con.task(t => {
    const a = post => t.any('SELECT * FROM comment WHERE idPost = $1', post.id)
        .then(comments => {
            post.comments = comments;
            return post;
        });
    return t.map('SELECT *, avatar FROM post, users WHERE user = $1 AND user = alias ORDER BY time DESC LIMIT 10 OFFSET $2', [username, pos], a)
        .then(t.batch);
})
    .then(posts => {
        res.send(posts);
    })
    .catch(error => {
        console.log(error);
    });

Также смотрите этот вопрос: получить таблицу JOIN как массив результатов с PostgreSQL / NodeJS .

UPDATE

В случае, если вы не хотите полностью использовать подход с JSON-запросами, следующее будет масштабироваться намного лучше, чем исходное решение, поскольку мы объединяем все дочерние запросы, а затем выполняем их как один запрос:

con.task(async t => {
    const posts = await t.any('SELECT *, avatar FROM post, users WHERE user = $1 AND user = alias ORDER BY time DESC LIMIT 10 OFFSET $2', [username, pos]);
    const a = post => ({query: 'SELECT * FROM comment WHERE idPost = ${id}', values: post});
    const queries = pgp.helpers.concat(posts.map(a));
    await t.multi(queries)
        .then(comments => {
            posts.forEach((p, index) => {
                p.comments = comments[index];
            });
        });
    return posts;
})
    .then(posts => {
        res.send(posts);
    })
    .catch(error => {
        console.log(error);
    });

См. API:

0 голосов
/ 12 февраля 2019

Если вы хотите структурированные (вложенные) данные, без необходимости

A) переписать свой sql, используя функцию json, или разбить его на несколько запросов задач, или

B) рефакторинг вашего кода для использования API тяжелого ORM

вы можете проверить sql-toolkit . Это библиотека узлов, созданная для pg-promise, которая позволяет вам писать обычный собственный SQL и получать обратно правильно структурированные (вложенные) чистые бизнес-объекты. Это строго набор инструментов для улучшения поверх pg-обещания, и он не стремится абстрагироваться от pg-обещания (вы все еще настраиваете pg-обещание и можете использовать его напрямую).

Например:

class Article extends BaseDAO {
  getBySlug(slug) {
    const query = `
      SELECT
        ${Article.getSQLSelectClause()},
        ${Person.getSQLSelectClause()},
        ${ArticleTag.getSQLSelectClause()},
        ${Tag.getSQLSelectClause()}
      FROM article
      JOIN person
        ON article.author_id = person.id
      LEFT JOIN article_tags
        ON article.id = article_tags.article_id
      LEFT JOIN tag
        ON article_tags.tag_id = tag.id
      WHERE article.slug = $(slug);
  `;
  return this.one(query, { slug });
  // OUTPUT: Article {person: Person, tags: Tags[Tag, Tag, Tag]}
}

Предложение select использует методы бизнес-объекта "getSQLSelectClause", чтобы сохранить скуку при вводе столбцов, а также обеспечить отсутствие коллизий имен (ничего не происходит, и вместо этого можно было бы просто выписать).

this.one - это вызов базового класса DAO sql-toolkit. Он отвечает за структурирование записей плоских результатов в красивую вложенную структуру.

(Также обратите внимание, что это «один», который соответствует нашей ментальной модели для SQL. Методы DAO для one, oneOrNone, many и any гарантируют, что их счетчик будет соответствовать количеству сгенерированных бизнес-объектов верхнего уровня, а не количеству строк возвращает выражение sql!)

Проверьте репозиторий для получения подробной информации о том, как установить его поверх pg-promise. (Disclamer, я автор sql-toolkit.)

0 голосов
/ 17 мая 2018

Вы можете использовать await, но он будет работать синхронно.

return t.any('select *, avatar from post, users where user= $1 and user = alias ORDER BY time DESC LIMIT 10 OFFSET $2', [username, pos])
    .then(posts => {
        if(posts.length > 0){
            for (var post of posts){
                post.coments = await t.any('select * from comment where idPost = ', post.id);
            }
        }
        return posts;
    });

На самом деле я рекомендую использовать такие инструменты, как книжная полка , knex , typeorm

...