Laravel красноречивый поиск в JSON

#php #mysql #json #laravel #eloquent

#php #mysql #json #laravel #красноречивый

Вопрос:

У меня проблема с поиском в столбце JSON. Laravel 8.x и MySQL 5.7.x

Учитывая следующий столбец 'industry_focus' , я пытаюсь сопоставить и вернуть все записи, содержащие любое из значений

["1","2","3","4","5","6","7","8"]

Мой запрос выглядит так:

 $related = Broker::query()
->where('broker_name', '!=', null)
->where('is_active', '=', true)
->where('state', '=', $state->code);

foreach ($same_industries as $key => $value) {
    if ($key === 0) {
        $related->whereJsonContains('industry_focus', (string) $value->id);
    } else {
        $related->orWhereJsonContains('industry_focus', (string) $value->id);
    }
}

$related = $related
->limit(10)
->get();
 

И он выдает необработанный запрос, выглядящий следующим образом:

 SELECT *
FROM `brokers`
WHERE `broker_name` IS NOT NULL
    AND `is_active` = 1
    AND `state` = 'NY'
    AND json_contains(`industry_focus`, '"2"')
    OR json_contains(`industry_focus`, '"3"')
    OR json_contains(`industry_focus`, '"4"')
    OR json_contains(`industry_focus`, '"5"')
    OR json_contains(`industry_focus`, '"6"')
    OR json_contains(`industry_focus`, '"7"')
    OR json_contains(`industry_focus`, '"8"')
    OR json_contains(`industry_focus`, '"9"')
    OR json_contains(`industry_focus`, '"10"')
    OR json_contains(`industry_focus`, '"11"')
    OR json_contains(`industry_focus`, '"12"')
    OR json_contains(`industry_focus`, '"13"')
    OR json_contains(`industry_focus`, '"14"')
    OR json_contains(`industry_focus`, '"15"')
    OR json_contains(`industry_focus`, '"16"')
limit 10
 

К сожалению, это неверно и возвращает все записи, игнорируя все условия. Я думаю, что перед первой AND json_contains и закрывающей скобкой отсутствует скобка в конце? Это ошибка, или я что-то упустил. Есть ли лучший способ написать это?

РЕДАКТИРОВАТЬ: Хорошо, итак, мне удалось как-то исправить это самостоятельно, посмотрев примеры в другом месте. Это то, что работает для меня:

 $related = Broker::query()
    ->where('broker_name', '!=', null)
    ->where('is_active', '=', true)
    ->where('state', '=', $state->code);

$related->where(function ($query) use ($same_industries) {
    foreach ($same_industries as $key => $value) {
        if ($key === 0) {
            $query->whereJsonContains('industry_focus', (string) $value->id);
        } else {
            $query->orWhereJsonContains('industry_focus', (string) $value->id);
        }
    }
});

$related = $related
    ->limit(10)
    ->get();

 

Комментарии:

1. что такое $sameIndustries ? whereJsonContains может принимать массив значений

Ответ №1:

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

 A AND B OR C OR D
 

Эквивалентно

 (A AND B) OR C OR D
 

Но то, что вам нужно, это:

 A AND (B OR C OR D)
 

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

Что еще могло бы упростить этот запрос, это то, что я говорю многим людям, которые используют JSON в MySQL:

Не используйте JSON в MySQL, если вы хотите использовать элементы внутри документа JSON в качестве отдельных элементов данных и искать их с помощью выражений SQL.

В этом случае у вас есть многозначный атрибут industry_focus . Обычный способ реализации многозначного атрибута — создать вторую таблицу и хранить по одному значению в строке.

 CREATE TABLE broker_industry_focus (
  broker_id INT NOT NULL,
  industry_focus INT NOT NULL,
  PRIMARY KEY (broker_id, industry_focus)
);
INSERT INTO broker_industry_focus VALUES (123, 7), (123, 11);
 

Как только вы это сделаете, вам будет проще искать:

 SELECT *
FROM brokers AS b
JOIN broker_industry_focus AS f 
    ON b.id = f.broker_id
WHERE b.broker_name IS NOT NULL
    AND b.is_active = 1
    AND b.state = 'NY'
    AND f.industry_focus IN (1,2,3,4,5,6,7,8);
 

Вам также может понравиться моя презентация о том, как неправильно использовать JSON в MySQL.

Комментарии:

1. Спасибо за краткое объяснение — я изучу его и постараюсь следовать лучшим практикам. На данный момент мне просто нужно было быстрое исправление, и мне как-то удалось это сделать. Смотрите мою правку выше.