Postgres - Проверить, содержит ли массив id - PullRequest
4 голосов
/ 20 июня 2020

Я пытаюсь определить, проголосовал ли пользователь за question. Пользователь нажимает кнопку «за» / «против», которая делает запрос к API, который извлекает их id из формы ie и затем сохраняет ее в массиве upvoters или downvoters. Logi c с положительным / отрицательным голосом работает должным образом - проблем нет - однако у меня возникают проблемы с определением, содержит ли массив upvoters или downvoters их id, когда пользователь запрашивает вопросы с помощью Speci c tag.

Модель вопроса:

 Column                  │ Type                      │  Modifiers
─────────────────────────┼───────────────────────────┼──────────────────────────────────────────────
 key                     │ Serial                    │  PRIMARY KEY
 userid                  │ UUID                      │  NOT NULL REFERENCES users(id)
 date                    │ TIMESTAMP                 │  WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP
 answered                │ BOOLEAN                   │  DEFAULT FALSE
 views                   │ INTEGER                   │  DEFAULT 0
 upvoters                | TEXT[]                    |  DEFAULT array[]::text[]
 downvoters              | TEXT[]                    |  DEFAULT array[]::text[]
 title                   | TEXT                      |  NOT NULL DEFAULT ''
 uniquetitle             | VARCHAR                   |  NOT NULL DEFAULT ''
 body                    | TEXT                      |  NOT NULL DEFAULT ''
 tags                    | TEXT[]                    |  DEFAULT array[]::text[]
 comments                | JSONB                     |  

Конечная точка API:

import get from "lodash.get";
import isEmpty from "lodash.isempty";
import db from "~db"; // connection to Postgres using pg-promise

const findAllQuestionsByTagLimitAndOffset = `
  SELECT 
     questions.key, 
     questions.userid, 
     questions.date, 
     questions.body, 
     questions.answered, 
     questions.views, 
     cardinality(questions.upvoters)-cardinality(questions.downvoters) as votes, 
     EXISTS(
       SELECT FROM questions WHERE questions.upvoters @> $4
     ) as upvoted, // check whether or not the logged in user has upvoted this question
     EXISTS(
        SELECT FROM questions WHERE questions.upvoters @> $4
     ) as downvoted, // check whether or not the logged in user has downvoted this question
     questions.title, 
     questions.uniquetitle, 
     questions.tags, 
     users.username, // appends original poster's username
     users.reputation as userrep, // appends original poster's rep
     users.key as userkey // appends original poster's id
  FROM questions 
  INNER JOIN users ON questions.userid=users.id 
  WHERE questions.tags @> $1 
  ORDER BY questions.date DESC 
  LIMIT $2 
  OFFSET $3
`;

/**
 * Fetches questions by tag.
 *
 * @function fetchNewestQuestionsByTag
 * @param {object} req - request object
 * @param {object} res - response object
 * @returns {array} data - questions that contain the queried tag with original poster populated data and some logged in user data
 * @throws {string} err
 */
const fetchNewestQuestionsByTag = async (req, res) => {
  try {
    const { tag } = req.query;
    if (!tag) throw String("Unable to locate questions because the tag is not valid.");
    const userid = get(req.session, ["id"]);

    const data = await db.any(findAllQuestionsByTagLimitAndOffset, [
      [tag],
      10,
      0,
      [userid],
    ]);
    if (isEmpty(data)) throw String("Unable to locate any questions.");

    res.status(201).send(data);
  } catch (err) {
    res.status(404).send(err);
  }
};

Вот как question выглядит после голосования за:

answered: false
body: "Test"
date: "2020-06-19T20:24:46.496Z"
downvoters: []
comments: []
title: "Test Title"
tags: ["reactjs"]
uniquetitle: "test-title"
upvoters ["d17a33f4-b26a-11ea-9372-131fa959a01b"] // logged in user id is stored
userid: "d17a33f4-b26a-11ea-9372-131fa959a018"
views: 1

После того, как пользователь проголосовал за и обновил страницу и / или запросил вопрос, за который он проголосовал с помощью тега, возвращается следующая (неверная) структура данных:

answered: false
body: "Test"
date: "2020-06-19T20:24:46.496Z"
downvoted: false
tags: ["reactjs"]
title: "Test Title"
uniquetitle: "test-title"
upvoted: false // this should be true as the logged in user is "d17a33f4-b26a-11ea-9372-131fa959a01b" and their id is contained within this array, yet it is still false
userid: "d17a33f4-b26a-11ea-9372-131fa959a018"
userkey: 1
username: "Matt"
userrep: 0
views: 1
votes: 1

Проблема состоит в том, что эти запросы всегда возвращают false:

  EXISTS(
    SELECT FROM questions WHERE questions.upvoters @> $4
  ) as upvoted,
  EXISTS(
    SELECT FROM questions WHERE questions.downvoters @> $4
  ) as downvoted,

Я пробовал следующее, и ни один из них не возвращает правильный логический статус:

Как сделать выбор с массивом, содержащим предложение значения в psql

Проверить, существует ли значение в Postgres массиве

Postgres: проверить, содержит ли поле массива значение?

Что я делаю не так?

1 Ответ

3 голосов
/ 20 июня 2020

Использование EXISTS приведет к проверке всех строк таблицы, а не только текущей строки.

Кроме того, upvoters дважды. Должно быть downvoters для downvoted.

Я предлагаю:

  SELECT 
     q.key, 
     q.userid, 
     q.date, 
     q.body, 
     q.answered, 
     q.views, 
     cardinality(q.upvoters)-cardinality(q.downvoters) AS votes, 
     $4 = ANY (q.upvoters) AS upvoted,
     $4 = ANY (q.downvoters) AS downvoted,
     q.title, 
     q.uniquetitle, 
     q.tags, 
     u.username,
     u.reputation AS userrep,
     u.key as userkey
  FROM   questions q
  JOIN   users     u ON q.userid = u.id 
  WHERE  q.tags @> $1 
  ORDER  BY q.date DESC 
  LIMIT  $2 
  OFFSET $3

Оператор массива @> ожидает массив типов с обеих сторон. Похоже, вы правильно поняли, передавая [userid] (создает массив, верно?). В моем запросе просто передайте простое строковое значение userid.

WHERE q.tags @> $1 может по-прежнему имеет смысл, как есть, если вы стремитесь к использованию индекса. См .:

Кроме того, d17a33f4-b26a-11ea-9372-131fa959a01b подозрительно выглядит как UUID . Не используйте для этого тип данных text / text[]. Используйте uuid / uuid[]. Намного лучше во многих отношениях. См .:

Но все из этих серьезных проблем все еще не может объяснить, о чем вы сообщаете. Вы должны были увидеть true для upvoted и downvoted. Может быть еще ошибок ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...