Фильтрация результатов запроса по значениям настраиваемых (созданных пользователем) полей - PullRequest
1 голос
/ 06 мая 2020

Программное обеспечение, над которым я работаю, позволяет администраторам создавать настраиваемые поля для хранения произвольной информации о пользователе. Поля хранятся в таблице под названием meta, а их значения для каждого пользователя хранятся в таблице meta_value, которая ссылается на таблицы user и meta через внешние ключи.

Упрощенное определение таблиц:

CREATE TABLE `user` (
    `id` INT NOT NULL AUTO_INCREMENT,
    `username` VARCHAR(120) NOT NULL,
    PRIMARY KEY (`id`),
    INDEX `idx_username` (`username`)
);

CREATE TABLE `meta` (
    `id` INT NOT NULL AUTO_INCREMENT,
    `key` VARCHAR(120) NOT NULL UNIQUE,
    PRIMARY KEY (`id`),
    INDEX `idx_key` (`key`)
);

CREATE TABLE `meta_value` (
    `id` INT NOT NULL AUTO_INCREMENT,
    `value` VARCHAR(120) NOT NULL,
    `meta_id` INT NOT NULL,
    `user_id` INT NOT NULL,
    PRIMARY KEY (`id`),
    FOREIGN KEY (`meta_id`) REFERENCES `meta` (`id`),
    FOREIGN KEY (`user_id`) REFERENCES `user` (`id`),
    INDEX `idx_value` (`value`)
);

Я реализую функцию поиска, при которой пользователь может искать других пользователей по значениям настраиваемых полей. Поисковый запрос (упрощенный) выглядит так: ?firstName[eq]=Andrew&lastName[eq]=Johnson&cityName[like]=Minneapol.

Эта строка запроса должна выбирать всех пользователей с полями firstName = 'Andrew' И lastName = 'Johnson' И city LIKE 'Minneapol%'.

Я попробовал очевидное решение:

SELECT `user`.*
FROM `user`
INNER JOIN `meta_value` ON `meta_value`.`user_id` = `user`.`id`
INNER JOIN `meta` ON `meta`.`id` = `meta_value`.`meta_id`
WHERE (
    CASE `meta`.`key`
        WHEN 'firstName' THEN `meta_value`.`value` = 'Andrew'
        WHEN 'lastName' THEN `meta_value`.`value` = 'Johnson'
        WHEN 'cityName' THEN `meta_value`.`value` LIKE 'Minneapol%'
    END
);

Этот запрос возвращает результаты, где firstName = 'Andrew' OR lastName = 'Johnson' OR cityName LIKE 'Minneapol%', что не является желаемым результатом.

Я ценю все предложения о том, как чтобы решить эту проблему, или правильный способ сделать это, если я делаю это неправильно.

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

Ответы [ 2 ]

1 голос
/ 06 мая 2020

Добавить дополнительное условие в л oop:

$query = 'SELECT * FROM `user` 
    WHERE 1=1';
...
foreach($request as $fieldKey => $data) {
    $query .= (' AND user.id IN (
    SELECT `user_id` 
        FROM meta_value
        JOIN `meta` ON meta.`id` = meta_value.`meta_id`
        WHERE meta_value.`value` ' . $data['operand'] . ' "' . $data['value'] . '"
            AND meta.`key` = "' . $fieldKey . '")');
}
...
1 голос
/ 06 мая 2020

Вы можете использовать агрегирование и having:

SELECT u.*
FROM user u JOIN
     meta_value mv
     ON mv.user_id = u.id JOIN
     meta m
     ON m.id = mv.meta_id
WHERE ( (m.key = 'firstname' AND mv.value = 'Andrew') OR
        (m.key = 'lastName' AND mv.value = 'Johnson') OR
        (m.key = 'cityName' AND mv.value LIKE 'Minneapol%')
      )
GROUP BY u.id
HAVING COUNT(DISTINCT m.key) = 3;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...