如何 select 包含特定单词的 postgreSQL 行

How to select a postgreSQL row that contains a specific word

正在尝试基于关键字为 postgreSQl 数据库构建查询。 LIKE 不起作用,因为它匹配包含任何字母的任何行。例如:

SELECT * 来自 table WHERE 列 ilike '%jeep%';

这 returns 列中包含 j、e 或 p 的任何行(由于某种原因同一行多次出现)。不是 'jeep'.

这个词

下面是我的查询结构。使用 Knex 并排队多个 tables:

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%'

这个表达式:

 WHERE column ilike 'jeep'

只匹配值为lower(column) = 'jeep'的行,例如:

  • 吉普
  • 吉普
  • 吉普车

它不匹配任何其他表达式。

如果使用通配符:

 WHERE column ilike '%jeep%'

然后在 lower(column) 中的任意位置查找 'jeep'。它是不是逐字符搜索。为此,您将使用正则表达式和字符 类:

WHERE column ~* '[jep]'

如果要在字段中查找,通常使用正则表达式,而不是like/ilike

使用 .union 有效并且 returns 正确的值,但是使用查询中第一个 table 的键标记。最后只进行了四个单独的查询,但希望这可以帮助其他人!

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}%`)
            ]);
    },

这个查询是一个交叉连接

(但是 Knex 语法稍微掩盖了这一点)。

This returns any row that a j,e or p in the column (and the same row multiple times for some reason).

这不是 return多次访问同一行。它 return 从 CROSS JOIN 中命名的每个 table 中提取所有内容。这是 Postgres 在 FROM 子句中指定多个 table 时的行为(参见:docs)。这个:

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

将 return 整行 来自 每个 的 table 每次你得到一个ILIKE 匹配。所以至少你总是会得到一个包含两行连接的对象(或者无论你在 FROM 子句中命名多少)。

棘手的部分是,Knex 列名称必须映射到 JavaScript 个对象。这意味着如果有两个名为 idtitle 的列结果,最后一个将覆盖结果对象中的第一个。

让我们举例说明(用袋熊)

这里有一个迁移和种子,只是为了更清楚:

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");

种子

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'
  }
]

如您所见,它交叉连接了两个 table,但我怀疑您只看到 出现 不匹配的结果(因为匹配是在另一个 table 中,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' }
]