Как выбрать строку postgreSQL, которая содержит определенное слово - PullRequest
1 голос
/ 13 октября 2019

Попытка построить запрос для базы данных postgreSQl на основе ключевого слова. LIKE не работает, поскольку соответствует любой строке, содержащей какие-либо буквы. Например:

SELECT * FROM таблицы WHERE столбец ilike '% jeep%';

Возвращает любую строку, содержащую aj, e или p в столбце (и одну и ту же строку несколько раз для некоторыхпричина). Не слово «джип».

Ниже приведена структура моего запроса. Использование Knex и организация нескольких таблиц в очередь:

searchAllBoardPosts(db, term) {
        return db
            .select('*')
            .from({
                a: 'messageboard_posts',
                b: 'rentals',
                c: 'market_place',
                d: 'jobs'
            })
            .where('a.title', 'ilike', `%${term}%`)
            .orWhere('b.title', 'ilike', `%${term}%`)
            .orWhere('c.title', 'ilike', `%${term}%`)
            .orWhere('d.title', 'ilike', `%${term}%`);
    },

Заранее спасибо!

ОБНОВЛЕНИЕ: Вот вывод SQL:

select * 
from "messageboard_posts" as "a", 
"rentals" as "b",
"market_place" as "c", 
"jobs" as "d" 
where "a"."title" ilike '%jeep%'
or "b"."title" ilike '%jeep%' 
or "c"."title" ilike '%jeep%' 
or "d"."title" ilike '%jeep%'

Ответы [ 3 ]

1 голос
/ 15 октября 2019

Этот запрос является перекрестным соединением

(но синтаксис Knex это немного маскирует).

Возвращает любую строку, содержащую aj, e или p в столбце (и одна и та же строка несколько раз по какой-то причине).

Он не возвращает одну и ту же строку несколько раз. Он возвращает все из каждой таблицы, названной в CROSS JOIN. Это поведение Postgres, когда в предложении FROM указано несколько таблиц (см .: docs ). Это:

db
  .select('*')
  .from({
    a: 'table_one',
    b: 'table_two'
  })

будет возвращать всю строку из каждой из названных таблиц каждый раз, когда вы получаете ILIKE совпадение. Таким образом, как минимум, вы всегда будете объектом, состоящим из двух соединенных строк (или сколько бы вы ни назвали в предложении FROM).

Сложность в том, что имена столбцов Knex должны соответствовать объектам JavaScript. Это означает, что если есть два столбца с именами, скажем, id или title, последний из них перезапишет первый в результирующем объекте.

Давайте проиллюстрируем (вомбатами)

Вот миграция и начальное число, просто для ясности:

table_one

exports.up = knex =>
  knex.schema.createTable("table_one", t => {
    t.increments("id");
    t.string("title");
  });

exports.down = knex => knex.schema.dropTable("table_one");

table_two

exports.up = knex =>
  knex.schema.createTable("table_two", t => {
    t.increments("id");
    t.string("title");
  });

exports.down = knex => knex.schema.dropTable("table_two");

Seed

exports.seed = knex =>
    knex("table_one")
      .del()
      .then(() => knex("table_two").del())
      .then(() =>
        knex("table_one").insert([
          { title: "WILLMATCHwombatblahblahblah" },
          { title: "WILLMATCHWOMBAT" }
        ])
      )
      .then(() =>
        knex("table_two").insert([
          { title: "NEVERMATCHwwwwwww" },
          { title: "wombatWILLMATCH" }
        ])
      )
  );

Запрос

Это позволяет нам немного поиграть с ILIKE соответствием. Теперь нам нужно сделать имена столбцов действительно явными:

  return db
    .select([
      "a.id as a.id",
      "a.title as a.title",
      "b.id as b.id",
      "b.title as b.title"
    ])
    .from({
      a: "table_one",
      b: "table_two"
    })
    .where("a.title", "ilike", `%${term}%`)
    .orWhere("b.title", "ilike", `%${term}%`);

Это приведет к:

[
  {
    'a.id': 1,
    'a.title': 'WILLMATCHwombatblahblahblah',
    'b.id': 1,
    'b.title': 'NEVERMATCHwwwwwww'
  },
  {
    'a.id': 1,
    'a.title': 'WILLMATCHwombatblahblahblah',
    'b.id': 2,
    'b.title': 'wombatWILLMATCH'
  },
  {
    'a.id': 2,
    'a.title': 'WILLMATCHWOMBAT',
    'b.id': 1,
    'b.title': 'NEVERMATCHwwwwwww'
  },
  {
    'a.id': 2,
    'a.title': 'WILLMATCHWOMBAT',
    'b.id': 2,
    'b.title': 'wombatWILLMATCH'
  }
]

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

Итак, каким должен быть запрос?

Я думаю, ваш (или Ry) план использовать UNION был верным, но, вероятно, стоит использовать UNION ALL, чтобы избежать ненужного удаления дубликатов. Примерно так:

  return db
    .unionAll([
      db("market_place")
        .select(db.raw("*, 'marketplace' as type"))
        .where("title", "ilike", `%${term}%`),
      db("messageboard_posts")
        .select(db.raw("*, 'post' as type"))
        .where("title", "ilike", `%${term}%`),
      db("rentals")
        .select(db.raw("*, 'rental' as type"))
        .where("title", "ilike", `%${term}%`),
      db("jobs")
        .select(db.raw("*, 'job' as type"))
        .where("title", "ilike", `%${term}%`)
    ]);

Аналогичный запрос к нашим тестовым данным дает набор результатов:

[
  { id: 1, title: 'WILLMATCHwombatblahblahblah', type: 'table_one' },
  { id: 2, title: 'WILLMATCHWOMBAT', type: 'table_one' },
  { id: 2, title: 'wombatWILLMATCH', type: 'table_two' }
]
0 голосов
/ 14 октября 2019

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

searchAllBoardPosts(db, term) {
        return db
            .union([db
                    .select('id', 'market_place_cat')
                    .from('market_place')
                    .where('title', 'ilike', `%${term}%`)
            ])
            .union([db
                    .select('id', 'board_id')
                    .from('messageboard_posts')
                    .where('title', 'ilike', `%${term}%`)
            ])
            .union([db
                    .select('id', 'rental_cat')
                    .from('rentals')
                    .where('title', 'ilike', `%${term}%`)
            ])
            .union([db
                    .select('id', 'job_cat')
                    .from('jobs')
                    .where('title', 'ilike', `%${term}%`)
            ]);
    },
0 голосов
/ 13 октября 2019

Это выражение:

 WHERE column ilike 'jeep'

Соответствует только строкам, в которых значение равно lower(column) = 'jeep', например:

  • JEEP
  • jeep
  • JeeP

Это не соответствует никаким другим выражениям.

Если вы используете подстановочные знаки:

 WHERE column ilike '%jeep%'

, то ищет 'jeep' где-нибудь в lower(column). Это не поиск символа за символом. Для этого вы должны использовать регулярные выражения и классы символов:

WHERE column ~* '[jep]'

Если вы хотите найти слово в поле, вы обычно используете регулярные выражения, а не like /ilike.

...