PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Modellierung der großen Objekte von PostgreSQL in Rails

Wenn Sie ActiveRecord verwenden, das mit Rails mit einem seiner Adapter geliefert wird, wird die einzige formale Zuordnung des Datenbanktyps zum Rails- oder Ruby-Typ, die normalerweise stattfindet, in NATIVE_DATABASE_TYPES definiert Konstante im Adapter, die über seine native_database_types zurückgegeben wird Methode. Für PostgreSQL in Rails 3.2.x ist dies in ActiveRecord::ConnectionAdapters::PostgreSQLAdapter das ist hier . Für diesen Adapter wird also der Typ „binary“ in Rails dem Typ „bytea“ in PG zugeordnet. Bei einigen Typen können Sie diesen Datenbanktyp überschreiben, dem er zugeordnet ist, indem Sie ein Gem namens activerecord-native_db_types_override . Aber wir wollen große Objekte verwenden, also...

Migrationen

Wie Jim Deville in den Kommentaren angemerkt hat, können Sie die benutzerdefinierte Spalte in der Tabelle wie folgt angeben:

t.column :some_oid, 'blob_oid', :null => false

Wenn Sie noch mehr tun müssen, was nicht dem Standard entspricht, können Sie auch ein execute("SQL GOES HERE;") verwenden um die Tabelle mit direktem SQL zu erstellen. Und wenn Sie über ein vorhandenes Legacy-Schema oder SQL-Änderungen verfügen, die außerhalb der Migrationen vorgenommen wurden, sollten Sie die Verwendung von structure.sql (config.active_record.schema_format = :sql) in Betracht ziehen Option in config/application.rb und dann tun:rake db:structure:dump ).

Große Objekte lesen/schreiben/Länge prüfen/löschen

Mit einigen Modifikationen zur Verdeutlichung usw. kopiert von:https://github.com/diogob/carrierwave-postgresql/blob/v0.1.0/lib/carrierwave/storage/postgresql_lo.rb :

Aktualisiert :Wir können, müssen aber nicht, ein begin vor lo_read/lo_write/lo_lseek setzen und lo_close im Sicherstellungsblock ausführen, weil per PG-Dokumentation "Alle großen Objektdeskriptoren, die am Ende einer Transaktion offen bleiben, werden automatisch geschlossen." (danke an Diogo für diese Info)

    require 'pg'

    ...

    def read
      (...).transaction do
        lo = connection.lo_open(identifier)
        content = connection.lo_read(lo, file_length)
        connection.lo_close(lo)
        content
      end
    end

    def write(file)
      (...).transaction do
        lo = connection.lo_open(identifier, ::PG::INV_WRITE)
        size = connection.lo_write(lo, file.read)
        connection.lo_close(lo)
        size
      end
    end

    def delete
      connection.lo_unlink(identifier)
    end

    def file_length
      (...).transaction do
        lo = connection.lo_open(identifier)
        size = connection.lo_lseek(lo, 0, 2)
        connection.lo_close(lo)
        size
      end
    end

Statt connection , verwenden Sie die Rohverbindung vom Modell oder Sockel, z. ActiveRecord::Base.connection.raw_connection (siehe hier ).

(...).transaction ruft Transaktion auf Modell oder Basis auf, z. ActiveRecord::Base.transaction (Siehe dies ).

identifier ist die OID, die Sie entweder übergeben/setzen müssen oder die Sie erhalten, indem Sie einfach ein connection.lo_creat ausführen .

Weitere Beispiele/Infos:

Letzteres und einige Antworten hier schlagen vor, dass Sie die Speicherung großer Dateien getrennt von der DB in Betracht ziehen sollten, z. damit Sie Cloud-Speicher verwenden können. Aber wenn Sie nur die Pfade/IDs zu externen Dateien speichern, die nicht sind von der DB verwaltet werden, verlieren Sie die ACID-Konsistenz (ein oder mehrere DB-Datensätze könnten auf eine oder mehrere Dateien verweisen, die nicht vorhanden sind, oder es könnten eine oder mehrere Dateien existieren, die keinen oder mehrere zugehörige Datensätze in der Datenbank haben). Ein weiteres Argument für das Speichern von Dateien im Dateisystem ist, dass Sie Dateien streamen können, aber PG Large Object speichert Dateien im Dateisystem auf eine von Postgres verwaltete Weise, um sowohl die ACID-Konsistenz zu gewährleisten als auch das Streaming zu ermöglichen (was Sie mit einem normalen BLOB nicht tun können /Rails-Binärtyp). Es kommt also nur darauf an; einige halten das Speichern in separatem Speicher unter Verwendung von Pfadreferenzen für eine bessere Option, und einige bevorzugen ACID-Konsistenz über große Objekte.

Der einfache Weg

Verwenden Sie einfach CarrierWave und carrierwave-postgresql .