简体   繁体   English

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

[英]How to select a postgreSQL row that contains a specific word

Trying to build a query for a postgreSQl DB based on a keyword.尝试基于关键字为 postgreSQl DB 构建查询。 LIKE doesn't work as it matches any row that contains any of the letters. LIKE 不起作用,因为它匹配包含任何字母的任何行。 For Example:例如:

SELECT * FROM table WHERE column ilike '%jeep%'; SELECT * FROM table WHERE column ilike '%jeep%';

This returns any row that aj,e or p in the column (and the same row multiple times for some reason).这将返回列中包含 aj、e 或 p 的任何行(以及由于某种原因多次出现同一行)。 Not the word 'jeep'.不是“吉普车”这个词。

Below is my query structure.下面是我的查询结构。 Using Knex and queuing multiple tables:使用 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}%`);
    },

Thanks in advance!提前致谢!

UPDATE: Here is the SQL output:更新:这是 SQL output:

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

This query is a cross join此查询是交叉连接

(But the Knex syntax masks that, a little). (但是 Knex 语法掩盖了一点)。

This returns any row that aj,e or p in the column (and the same row multiple times for some reason).这将返回列中包含 aj、e 或 p 的任何行(以及由于某种原因多次出现同一行)。

It's not returning the same row multiple times.它不会多次返回同一行。 It's returning everything from each table named in a CROSS JOIN .它从CROSS JOIN中命名的每个表中返回所有内容。 This is the behaviour of Postgres when more than one table is named in the FROM clause (see: docs ).这是在FROM子句中命名多个表时 Postgres 的行为(请参阅: docs )。 This:这个:

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

will return the entire row from each of the named tables every time you get an ILIKE match.每次获得ILIKE匹配时,都会从每个命名表中返回整行 So at minimum you'll always get an object consisting of two rows joined (or however many you name in the FROM clause).所以至少你总是会得到一个 object 由两行连接组成(或者你在FROM子句中命名的任意多行)。

The tricky part is, Knex column names have to map to JavaScript objects.棘手的部分是,Knex 列名称必须从 map 到 JavaScript 对象。 This means that if there are two column results named, say, id or title , the last one will overwrite the first one in the resulting object.这意味着如果有两个名为idtitle的列结果,最后一个将覆盖生成的 object 中的第一个。

Let's illustrate (with wombats)让我们举例说明(用袋熊)

Here's a migration and seed, just to make it clearer:这是一个迁移和种子,只是为了更清楚:

table_one 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" }
        ])
      )
  );

Query询问

This allows us to play around a bit with ILIKE matching.这让我们可以玩一下ILIKE匹配。 Now we need to make the column names really explicit:现在我们需要使列名非常明确:

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

This produces:这会产生:

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

As you can see, it's cross-joining both tables, but I suspect you were only seeing results that appeared not to match (because the match was in the other table, and the title column name was a duplicate).如您所见,它交叉连接了两个表,但我怀疑您只看到了似乎不匹配的结果(因为匹配在另一个表中,并且title列名称是重复的)。

So, what should the query be?那么,查询应该是什么?

I think your (or Ry's) plan to use UNION was correct, but it's probably worth using UNION ALL to avoid unnecessary removal of duplicates.我认为您(或 Ry 的)使用UNION的计划是正确的,但可能值得使用UNION ALL来避免不必要地删除重复项。 Something like this:像这样的东西:

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

A similar query against our test data produces the result set:针对我们的测试数据的类似查询会产生结果集:

[
  { id: 1, title: 'WILLMATCHwombatblahblahblah', type: 'table_one' },
  { id: 2, title: 'WILLMATCHWOMBAT', type: 'table_one' },
  { id: 2, title: 'wombatWILLMATCH', type: 'table_two' }
]

Using .union works and returns the correct values, however using the key tag from the first table in the query.使用.union可以工作并返回正确的值,但是使用查询中第一个表中的键标记。 Have ended up just making four separate queries in the end but hope this can help some else!最后只做了四个单独的查询,但希望这可以帮助其他人!

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

This expression:这个表达式:

 WHERE column ilike 'jeep'

Only matches rows where the value is lower(column) = 'jeep' , such as:仅匹配值为lower(column) = 'jeep'行,例如:

  • JEEP吉普车
  • jeep吉普车
  • JeeP吉普车

It does not match any other expression.它不匹配任何其他表达式。

If you use wildcards:如果使用通配符:

 WHERE column ilike '%jeep%'

then is looks for 'jeep' anywhere in lower(column) .然后是在lower(column)的任何地方寻找'jeep' It is not searching character by character.不是逐个字符搜索。 For that, you would use regular expressions and character classes:为此,您将使用正则表达式和字符类:

WHERE column ~* '[jep]'

If you want to find a word in the field, you would normally use regular expressions, not like / ilike .如果要在字段中查找单词,通常会使用正则表达式,而不是like / ilike

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM