Vraag Generieke Ruby-oplossing voor SQLite3 "LIKE" of PostgreSQL "ILIKE"?


Ik gebruik SQLite3 voor ontwikkeling en PostgreSQL voor implementatie. Ik krijg echter het volgende probleem:

Mijn eenvoudige zoekopdracht met SQLite3:

def self.search(search)
    if search
      find(:all, :conditions => ["style LIKE ? OR construction LIKE ?", "%#{search}%", "%#{search}%"])
    else
      find(:all)
    end
end

Het werkt echter niet voor PostgreSQLen ik moet de. vervangen LIKE voor ILIKE het probleem oplossen:

def self.search(search)
    if search
      find(:all, :conditions => ["style ILIKE ? OR construction ILIKE ?", "%#{search}%", "%#{search}%"])
    else
      find(:all)
    end
end

Is er een "Ruby-manier" om deze zoekopdrachten in elke database uit te voeren?

BEWERK - op basis van uw antwoorden geloof ik niet dat ik daarvoor een generieke Ruby-oplossing zal vinden.

Ik heb de Ruby on Rails Handleiding: leer rails per voorbeeld - door Michael Hartl, waar de finale Gemfile toont beide databases ... nou ja, teleurstellend ...


15
2018-06-28 16:32


oorsprong


antwoorden:


De wortel van het probleem ligt hier:

Ik gebruik SQLite3 voor ontwikkeling en PostgreSQL voor implementatie.

Dat is een slecht idee. Je zult tegen onverenigbaarheden aanlopen - of erger nog: niet realiseren tot schade is aangericht.
Gebruik dezelfde RDBMS (PostgreSQL) voor ontwikkeling en productie en bespaar jezelf de zinloze problemen.


Terwijl je vastzit aan je ongelukkige opstelling, is er een eenvoudige oplossing:

lower(style) LIKE lower(?)

Werkt op beide platforms.

  • Je kunt de rechterhand laten vallen lower(), als u een zoekpatroon in kleine letters invoert.

  • In standaard SQLite lower(X) vouwt alleen ASCII-letters. Voor meer citeer ik het hoofdstuk Kernfuncties in de SQLite-handleiding:

    De onderste (X) functie retourneert een kopie van string X met alles   ASCII-tekens geconverteerd naar kleine letters. De standaard ingebouwde lagere ()   De functie werkt alleen voor ASCII-tekens. Caseconversies uitvoeren   niet-ASCII-tekens, laad de ICU-extensie.

    De nadruk ligt op mij.

  • PostgreSQL lower(X) werkt standaard met UTF-8.


41
2017-07-01 01:29



Ik denk dat Arel de beste manier is om dit probleem op te lossen. Het wordt gebruikt door Rails voor actief opnemen en is database-onafhankelijk. Je code werkt hetzelfde in sqlite3 of postgres, wat lijkt te passen in jouw situatie. Het gebruik van de matches-methode zal automatisch overschakelen naar een ilike in een postgres-omgeving. voorbeeld:

users=User.arel_table
User.where(users[:style].matches("%#{search}%").or(users[:construction].matches("%#{search}%")))

U kunt meer informatie krijgen van de github: https://github.com/rails/arel/


9
2017-07-04 05:41



Nee, er is geen "ruby way" om een ​​database te doorzoeken - Ruby on Rails (specifiek ActiveRecord) bevat hulpmethoden voor het uitvoeren van CRUD-bewerkingen op RDB's die worden ondersteund door ActiveRecord, maar er is niet veel betere manier om te zoeken met LIKE dan met de voorbeelden die u hebt opgegeven.

Het gedeelte van het Rails-document dat betrekking heeft op deze discussie zou zijn ActiveRecord :: FinderMethods.

Als een kanttekening, in plaats van doen find(:all) je kunt het gewoon doen alle.

De Rails-documenten gebruiken dezelfde syntaxis als voor het doen van LIKE-instructies, dat wil zeggen:

Person.exists?(['name LIKE ?', "%#{query}%"])

Het gebruik van de bovenstaande methode is volkomen veilig.

De reden waarom uitspraken zoals die hieronder onveilig zijn, is omdat het where string wordt direct doorgegeven aan uw databasequery zonder enige ontsmetting, waardoor uw database openstaat voor uitbuiting (d.w.z. een eenvoudige apostrof in params[:first_name]kan uw volledige vraag verpesten en uw database kwetsbaar maken - met name SQL-injectie). In het bovenstaande voorbeeld kan ActiveRecord de parameters opschonen die u doorgeeft aan de query.

Client.where("first_name LIKE '%#{params[:first_name]}%'")

5
2018-06-30 20:03



Helaas denk ik niet dat je hier een goede oplossing voor zult vinden. De enige andere syntaxis die voor u beschikbaar is, in plaats van: voorwaarden, is om een ​​.where-component te gebruiken:

where(["style ILIKE ? OR construction ILIKE ?", "%#{search}%", "%#{search}%"])

Helaas, zoals je je waarschijnlijk hebt gerealiseerd, zal deze alternatieve syntaxis precies hetzelfde probleem tegenkomen. Dit is slechts een van de ergernissen die u tegenkomt bij het hebben van een aanzienlijk verschillende ontwikkelomgeving van uw productieomgeving - er zijn ook andere vrij substantiële verschillen tussen SQLite en PostgresSQL. Ik zou aanraden om Postgres op je ontwikkelmachine te installeren en te gebruiken. Het maakt het ontwikkelen een stuk eenvoudiger en maakt je code een stuk schoner.


2
2018-06-28 16:42



Hoewel het geen goede gewoonte is om verschillende databases te gebruiken bij de productie en ontwikkeling, is het toch een goed idee om de database te gebruiken squeel edelsteen: https://github.com/ernie/squeel/

Hiermee kunt u uw vragen schrijven met een DSL die gemakkelijk en aantoonbaar veel schoner en leesbaarder is dan ruwe sql, en de edelsteen zal zijn vertaling naar SQL verwerken die specifiek is voor het gebruikte RDBMS.

Ryan Bates heeft er een goede video over: http://railscasts.com/episodes/354-squeel


2
2018-06-30 23:26



Ik kreeg een keer hetzelfde probleem, hier is mijn oplossing:

Ik schreef een kleine lib die het mogelijk maakt om de functie voor elke database te gebruiken:

class AdapterSpecific

  class << self
    def like_case_insensitive
      case ActiveRecord::Base.connection.adapter_name
      when 'PostgreSQL'
        'ILIKE'
      else
        'LIKE'
      end
    end    

    def random
      #something
    end
  end

Om het in uw model te gebruiken:

def self.search(search)
    if search
      find(:all, :conditions => ["style #{AdapterSpecific.like_case_insensitive} :query OR construction #{AdapterSpecific.like_case_insensitive} :query", {:query => "%#{search}%"}])
    else
      find(:all)
    end
end

Sinds ik de implementatiedatabase naar PostGres heb gemigreerd, is dit een veel betere setup om verschillende redenen (zie andere antwoorden).

U kunt ook denken aan het gebruik van fulltext-zoekfunctie voor Postgres, waardoor uw tekstonderzoek veel efficiënter zal zijn, zie texticle voor een basisimplementatie of pg_search als je meer maatwerk nodig hebt.


2
2017-07-02 22:10



Zoeken met LIKE kan pijnlijk zijn voor de database. Het zal zeker geen index gebruiken die behoorlijk onbetaalbaar kan zijn.

Een langer antwoord: ik zou het aanbevelen Postgres in ontwikkeling te gebruiken (sqlite3 gebruiken) en dan een Full-Text index te hebben op al uw doorzoekbare velden style, construction via Postgres ' tsvector type.

Full-text-zoekopdrachten in Postgres bij het gebruik van een index zijn erg snel.


1
2017-07-01 01:03