1
votes

Comment sélectionner une ligne postgreSQL contenant un mot spécifique

Tentative de création d'une requête pour une base de données postgreSQl basée sur un mot-clé. LIKE ne fonctionne pas car il correspond à une ligne contenant l'une des lettres. Par exemple:

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

Ceci retourne n'importe quelle ligne aj, e ou p dans la colonne (et la même ligne plusieurs fois pour une raison quelconque) . Pas le mot «jeep».

Voici ma structure de requête. Utilisation de Knex et mise en file d'attente de plusieurs tables:

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

Merci d'avance!

MISE À JOUR: Voici la sortie SQL:

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


11 commentaires

Je suppose que .from ({all of those}) est une jointure implicite, pas une union comme vous voulez qu'elle soit - c'est-à-dire que les ILIKE fonctionnent correctement et vous ' trouver un modèle qui n'est pas présent dans les résultats dupliqués. Impossible de trouver où il est documenté, cependant. Essayez de consigner le SQL qu'il crée?


Vous dites donc que SELECT * FROM table OERE la colonne ilike ‘jeep’ correspondra à une ligne où column = “elephant” (car il y a un “e” dans la chaîne)? J'ai du mal à le croire. Pouvez-vous mettre à jour votre question avec une sortie de requête ou reformuler votre déclaration dans le paragraphe après l'exemple de requête SELECT que vous avez fourni?


@ Ry- d'après ma compréhension de Knex (nouveau), cette structure est un moyen d'aliaser les tables en colonnes lors de l'interrogation de plusieurs tables en une seule fois. Et je déconnecte les résultats SQL et renvoie plus de 800 objets, mais il n'y a que 25 objets au total sur toutes les tables. Les résultats sont rendus dans mon client comme ils le devraient, mais il y a des centaines de messages identiques.


@richyen oui je sais! C'est difficile à croire, mais c'est ce que je suppose être la conclusion. Chaque ligne qu'elle renvoie contient au moins un leet du mot-clé dans la colonne souhaitée. Je vais essayer d'ajouter plus d'informations au paragraphe.


Pas les résultats SQL, le SQL. La requête qu'il crée. S'il ressemble à FROM messageboard_posts a, locations b, market_place c, jobs d , vous effectuez une jointure implicite. github.com/knex/knex/issues/2378


@ Ry- désolé. Mise à jour du message avec la sortie SQL. On dirait un alias pour moi


@RyanCarville Oui, vous ne pouvez pas faire un SELECT * avec une union. Quels résultats attendez-vous réellement de tous ces tableaux?


J'ai juste essayé une méthode d'union chaînée et j'ai obtenu: erreur: chaque requête UNION doit avoir le même nombre de colonnes Les quatre tables ont des tailles différentes. J'ai essayé de sélectionner simplement la colonne de titre de toutes les tables et cela fonctionne! Mais je dois renvoyer toutes les colonnes de chaque table qui est vraie. Peut-être sélectionner ensuite l'identifiant de la publication et une requête supplémentaire pour renvoyer la publication correspondante?


Je ne suis pas sûr que cela fonctionnerait comme je l'ai remarqué quand il renvoie la correspondance, il nomme les clés comme les premières clés de la table et non la table dans laquelle elle provient des clés, donc je ne peux pas identifier la table dans laquelle il l'a trouvée. Ie: maessageboard_posts a board_id et market_place a market_place_cat mais s'il est trouvé dans market_place, il renvoie la clé sous la forme board_id


Vous devez sélectionner un ensemble compatible de colonnes dans chaque table avant de les unir. Si vous ne pouvez pas faire cela (enfin, vous pouvez, mais si ce serait trop gênant - beaucoup de remplissage nul), faites simplement quatre requêtes distinctes. edit: Oh, c'est PostgreSQL? Je pensais que c'était SQLite pour une raison quelconque. Vous pouvez alors convertir des lignes en JSON .


@ Ry- c'est ce que j'ai fini par faire pour le moment. Enchaîné quatre les interroge s'il y a des résultats à partir d'une table, je les ai ajoutés à un objet et renvoyé l'objet au client. Probablement la meilleure façon d'aborder cela à la fin, car ils ont tous des colonnes différentes et sont rendus dans différentes mises en page dans le client. Mais merci pour votre aide. Vraiment l'apprécier!


3 Réponses :


-2
votes

Cette expression:

WHERE column ~* '[jep]'

Correspond uniquement aux lignes dont la valeur est lower (column) = 'jeep' , comme:

  • JEEP
  • jeep
  • JeeP

Il ne correspond à aucune autre expression.

Si vous utilisez des caractères génériques:

 WHERE column ilike '%jeep%'

alors il recherche ' jeep ' n'importe où dans inférieur (colonne) . Il ne s'agit pas de rechercher caractère par caractère. Pour cela, vous utiliseriez des expressions régulières et des classes de caractères:

 WHERE column ilike 'jeep'

Si vous voulez trouver un mot dans le champ, vous utiliseriez normalement expressions, pas comme/ilike.


3 commentaires

C'est ce que je pensais mais cela ne semble pas être le cas avec celui-ci. J'ai utilisé ilike dans d'autres requêtes lors du ciblage d'une table et cela semble fonctionner correctement. Mais lorsque vous ciblez plusieurs tables, pas tellement.


@RyanCarville. . . Il ne se comporte pas comme un matcher d'expressions régulières, à moins que quelque chose dans knex ou une couche intermédiaire ne change votre code.


@RyanCarville. . . quant à votre requête, vous avez besoin de conditions JOIN parmi les tables.



0
votes

L'utilisation de .union fonctionne et renvoie les valeurs correctes, mais en utilisant la balise clé de la première table de la requête. J'ai fini par faire quatre requêtes distinctes à la fin, mais j'espère que cela peut aider d'autres!

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 commentaires

2
votes

Cette requête est une jointure croisée

(mais la syntaxe de Knex masque cela, un peu).

Ceci renvoie n'importe quelle ligne qui a j, e ou p dans la colonne (et la même ligne plusieurs fois pour une raison quelconque).

Il ne renvoie pas la même ligne plusieurs fois. Il renvoie tout de chaque table nommée dans un CROSS JOIN . C'est le comportement de Postgres lorsque plusieurs tables sont nommées dans la clause FROM (voir: docs ). Ceci:

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

renverra la ligne entière de chacune des tables nommées chaque fois que vous obtenez un Correspondance ILIKE . Ainsi, au minimum, vous obtiendrez toujours un objet composé de deux lignes jointes (ou quel que soit le nombre que vous nommez dans la clause FROM ).

La partie délicate est que les noms de colonnes Knex ont pour mapper sur des objets JavaScript. Cela signifie que s'il y a deux résultats de colonne nommés, par exemple, id ou title , le dernier écrasera le premier dans l'objet résultant.

Illustrons (avec des wombats)

Voici une migration et une graine, juste pour clarifier les choses:

table_one

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

table_two

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

Seed

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

Query

Cela nous permet de jouer un peu avec Correspondance ILIKE . Nous devons maintenant rendre les noms de colonnes vraiment explicites:

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

Cela produit:

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

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

Comme vous pouvez le voir, c'est joignant les deux tables, mais je suppose que vous ne voyiez que des résultats qui semblaient ne pas correspondre (car la correspondance était dans l'autre table et le nom de la colonne title était un dupliquer).

Alors, quelle devrait être la requête?

Je pense que votre projet (ou celui de Ry) d'utiliser UNION était correct, mais cela en vaut probablement la peine en utilisant UNION ALL pour éviter la suppression inutile des doublons. Quelque chose comme ceci:

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

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

Une requête similaire sur nos données de test produit l'ensemble de résultats:

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

0 commentaires