Pencarian instan untuk Rails dan ActiveRecord menggunakan tampilan material SQL.
Lihat aplikasi demo (kode ditemukan di folder demo_app/
):
Tambahkan kemampuan pencarian cepat ke aplikasi Rails Anda tanpa sistem eksternal seperti ElasticSearch. Sekarang sangat mudah untuk membuat ekspresi ActiveRecord/Arel yang sudah kita kenal dan sukai, dan mengubahnya menjadi tampilan terwujud SQL: siap untuk ditanyakan dan disusun dengan ActiveRecord. Segala sesuatu yang Anda sukai tentang Rails, tetapi lebih cepat.
Apa yang membuat Rails lambat untuk dicari? Tabel besar, banyak gabungan, subkueri, indeks hilang atau tidak digunakan, dan kueri kompleks. Juga lambat? Mengkoordinasikan data dari beberapa sistem eksternal melalui Ruby untuk menghasilkan hasil pencarian.
SearchCraft memudahkan penulisan dan penggunaan tampilan material SQL yang kuat untuk menghitung terlebih dahulu hasil kueri penelusuran dan pelaporan Anda. Ini seperti indeks database, tetapi untuk kueri yang kompleks.
Tampilan terwujud adalah fitur luar biasa dari PostgreSQL, Oracle, dan SQL Server*. Ini adalah tabel hasil kueri yang telah dihitung sebelumnya. Mereka cepat dalam bertanya. Mereka luar biasa. Seperti sistem pencarian lainnya, Anda mengontrol kapan Anda ingin menyegarkannya dengan data baru.
Di dalam Rails dan ActiveRecord, Anda dapat mengakses tampilan terwujud hanya-baca seperti yang Anda lakukan pada tabel biasa. Anda bahkan dapat menggabungkannya bersama-sama. Anda dapat menggunakannya dalam model, cakupan, dan asosiasi ActiveRecord Anda.
class ProductSearch < ActiveRecord :: Base
include SearchCraft :: Model
end
Selesai. Kolom apa pun yang Anda gambarkan dalam tampilan Anda akan menjadi atribut pada model Anda.
Jika tampilan dasar memiliki kolom product_id
, product_name
, reviews_count
, dan reviews_average
, maka Anda dapat mengkuerinya seperti model ActiveRecord lainnya:
ProductSearch . all
[ #<ProductSearch product_id: 2, product_name: "iPhone 15", reviews_count: 5, reviews_average: 0.38e1>,
#<ProductSearch product_id: 1, product_name: "Laptop 3", reviews_count: 5, reviews_average: 0.28e1>,
#<ProductSearch product_id: 4, product_name: "Monopoly", reviews_count: 3, reviews_average: 0.2e1>]
ProductSearch . order ( reviews_average : :desc )
[ #<ProductSearch product_id: 2, product_name: "iPhone 15", reviews_count: 5, reviews_average: 0.38e1>,
#<ProductSearch product_id: 1, product_name: "Laptop 3", reviews_count: 5, reviews_average: 0.28e1>,
#<ProductSearch product_id: 4, product_name: "Monopoly", reviews_count: 3, reviews_average: 0.2e1>]
Jika Anda menyertakan kunci asing, maka Anda dapat menggunakan asosiasi belongs_to
. Anda dapat menambahkan cakupan. Anda dapat menambahkan metode. Anda dapat menggunakannya sebagai titik awal untuk kueri dengan database SQL Anda yang lain. Itu hanya model ActiveRecord biasa.
Semua ini sudah dimungkinkan dengan Rails dan ActiveRecord. Pencapaian SearchCraft adalah menjadikan hidup dengan pandangan material Anda menjadi hal yang sepele. Sepele untuk menyegarkan dan menulisnya.
Setiap tampilan SearchCraft yang terwujud merupakan cuplikan hasil kueri pada saat dibuat, atau terakhir kali disegarkan. Ini seperti tabel yang isinya berasal dari query.
Jika data yang mendasari tampilan terwujud SearchCraft Anda berubah dan Anda ingin menyegarkannya, panggil refresh!
di kelas model Anda. Ini disediakan oleh mixin SearchCraft::Model
.
ProductSearch . refresh!
Anda dapat meneruskan relasi/array ActiveRecord ini ke tampilan Rails Anda dan merendernya. Anda dapat menggabungkannya ke tabel lain dan menerapkan cakupan lebih lanjut.
Namun fitur terhebat SearchCraft adalah membantu Anda menulis pandangan terwujud Anda , dan kemudian mengulanginya.
Rancang dalam ekspresi ActiveRecord, ekspresi Arel, atau bahkan SQL biasa. Tidak ada migrasi untuk memutar kembali dan menjalankannya kembali. Tidak perlu melacak apakah tampilan SQL di database Anda cocok dengan kode SearchCraft di aplikasi Rails Anda. SearchCraft akan secara otomatis membuat dan memperbarui tampilan material Anda.
Perbarui tampilan SearchCraft Anda, jalankan pengujian Anda, semuanya berhasil. Perbarui tampilan SearchCraft Anda, segarkan aplikasi pengembangan Anda, dan itu berfungsi. Buka rails console
dan itu berfungsi; lalu perbarui tampilan Anda, ketik reload!
, dan itu berhasil. Terapkan ke produksi di mana saja, dan itu berhasil.
Seperti apa desain tampilan terwujud dengan SearchCraft? Untuk model ProductSearch
di atas, kami membuat kelas ProductSearchBuilder
yang mewarisi dari SearchCraft::Builder
dan menyediakan metode view_scope
atau metode view_select_sql
.
class ProductSearchBuilder < SearchCraft :: Builder
def view_scope
Product . where ( active : true )
. select (
"products.id AS product_id" ,
"products.name AS product_name" ,
"(SELECT COUNT(*) FROM product_reviews WHERE product_reviews.product_id = products.id) AS reviews_count" ,
"(SELECT AVG(rating) FROM product_reviews WHERE product_reviews.product_id = products.id) AS reviews_average"
)
end
end
Metode view_scope
harus mengembalikan relasi ActiveRecord. Ini bisa sesederhana atau serumit yang Anda suka. Itu bisa menggunakan gabungan, subkueri, dan apa pun yang dapat Anda lakukan dengan ActiveRecord. Dalam contoh di atas kita:
id
dan name
dari tabel products
; dimana nantinya kita dapat menggunakan product_id
sebagai kunci asing untuk bergabung dengan model Product
di aplikasi kitareviews_count
dan reviews_average
baru menggunakan subkueri SQL yang menghitung dan membuat rata-rata kolom rating
dari tabel product_reviews
. SearchCraft akan mengonversinya menjadi tampilan terwujud, membuatnya menjadi database Anda, dan model ProductSearch
di atas akan mulai menggunakannya saat Anda memuat ulang aplikasi pengembangan atau menjalankan pengujian berikutnya. Jika Anda melakukan perubahan, SearchCraft akan menghapus dan membuat ulang tampilan secara otomatis.
Saat kami memuat aplikasi ke konsol Rails, atau menjalankan pengujian, atau menyegarkan aplikasi pengembangan, model ProductSearch
akan diperbarui secara otomatis agar sesuai dengan perubahan apa pun di ProductSearchBuilder
.
ProductSearch . all
[ #<ProductSearch product_id: 2, product_name: "iPhone 15", reviews_count: 5, reviews_average: 0.38e1>,
#<ProductSearch product_id: 1, product_name: "Laptop 3", reviews_count: 5, reviews_average: 0.28e1>,
#<ProductSearch product_id: 4, product_name: "Monopoly", reviews_count: 3, reviews_average: 0.2e1>]
ProductSearch . order ( reviews_average : :desc )
[ #<ProductSearch product_id: 2, product_name: "iPhone 15", reviews_count: 5, reviews_average: 0.38e1>,
#<ProductSearch product_id: 1, product_name: "Laptop 3", reviews_count: 5, reviews_average: 0.28e1>,
#<ProductSearch product_id: 4, product_name: "Monopoly", reviews_count: 3, reviews_average: 0.2e1>]
Jika Anda ingin menulis SQL, Anda dapat menggunakan metode view_select_sql
sebagai gantinya.
class NumberBuilder < SearchCraft :: Builder
# Write SQL that produces 5 rows, with a 'number' column containing the number of the row
def view_select_sql
"SELECT generate_series(1, 5) AS number;"
end
end
class Number < ActiveRecord :: Base
include SearchCraft :: Model
end
Number . all
[ #<Number number: 1>, #<Number number: 2>, #<Number number: 3>, #<Number number: 4>, #<Number number: 5>]
Fitur luar biasa dari tampilan terwujud adalah Anda dapat menambahkan indeks ke dalamnya; bahkan indeks unik.
Saat ini mekanisme untuk menambahkan indeks adalah dengan menambahkan metode view_indexes
ke kelas pembangun Anda.
Misalnya, kita dapat menambahkan indeks unik pada kolom number
NumberBuilder
:
class NumberBuilder < SearchCraft :: Builder
def view_indexes
{
number : { columns : [ "number" ] , unique : true }
}
end
Atau beberapa indeks di ProductSearchBuilder
dari sebelumnya:
class ProductSearchBuilder < SearchCraft :: Builder
def view_indexes
{
id : { columns : [ "product_id" ] , unique : true } ,
product_name : { columns : [ "product_name" ] } ,
reviews_count : { columns : [ "reviews_count" ] } ,
reviews_average : { columns : [ "reviews_average" ] }
}
end
end
Secara default, indeks akan using: :btree
. Anda juga dapat menggunakan metode pengindeksan lain yang tersedia di Rails, seperti :gin
, :gist
, atau jika Anda menggunakan ekstensi trigram
Anda dapat menggunakan :gin_trgm_ops
. Ini dapat berguna saat Anda ingin menyiapkan penelusuran teks, seperti yang dibahas di bawah.
Manfaat lain dari tampilan terwujud adalah kita dapat membuat kolom yang dioptimalkan untuk pencarian. Misalnya di atas, karena kita telah menghitung sebelumnya reviews_average
di ProductSearchBuilder
, kita dapat dengan mudah menemukan produk dengan rating rata-rata tertentu.
ProductSearch . where ( "reviews_average > 4" )
Fitur luar biasa dari ActiveRecord adalah kemampuan untuk menggabungkan kueri bersama-sama. Karena tampilan material kami adalah model ActiveRecord asli, kami dapat menggabungkannya dengan kueri lainnya.
Mari kita siapkan hubungan antara ProductSearch#product_id
MV kita dan kunci utama tabel Product#id
:
class ProductSearch < ActiveRecord :: Base
include SearchCraft :: Model
belongs_to :product , foreign_key : :product_id , primary_key : :id
end
Sekarang kita dapat menggabungkan, atau ingin memuat, tabel bersama dengan kueri ActiveRecord. To following mengembalikan relasi objek ProductSearch
, dengan masing-masing asosiasi ProductSearch#product
nya dimuat sebelumnya.
ProductSearch . includes ( :product ) . where ( "reviews_average > 4" )
Berikut ini mengembalikan objek Product
, berdasarkan pencarian tampilan material ProductSearch
:
class Product
has_one :product_search , foreign_key : :product_id , primary_key : :id , class_name : "ProductSearch"
end
Product . joins ( :product_searches ) . merge (
ProductSearch . where ( "reviews_average > 4" )
)
PostgreSQL hadir dengan solusi untuk pencarian teks menggunakan kombinasi fungsi seperti to_tsvector
, ts_rank
, dan websearch_to_tsquery
.
Sambil menunggu dokumen lainnya, lihat test/searchcraft/builder/test_text_search.rb
untuk contoh cara menggunakan fungsi-fungsi ini dalam tampilan material Anda.
Saya masih berupaya mengekstraksi solusi ini dari kode kami di Store Connect.
Setelah Anda memiliki satu tampilan terwujud SearchCraft, Anda mungkin ingin membuat tampilan lain yang bergantung padanya. Anda juga dapat melakukannya dengan metode depends_on
.
class SquaredBuilder < SearchCraft :: Builder
depends_on "NumberBuilder"
def view_select_sql
"SELECT number, number * number AS squared FROM #{ Number . table_name } ;"
end
end
class Squared < ActiveRecord :: Base
include SearchCraft :: Model
end
Jika Anda membuat perubahan pada NumberBuilder
, SearchCraft akan secara otomatis menghapus dan membuat ulang tampilan material Number
dan Squared
.
Squared . all
[ #<Squared number: 1, squared: 1>,
#<Squared number: 2, squared: 4>,
#<Squared number: 3, squared: 9>,
#<Squared number: 4, squared: 16>,
#<Squared number: 5, squared: 25>]
Bukankah percaya diri menulis ekspresi SQL atau Arel yang rumit? Saya juga. Saya bertanya pada GPT4 atau GitHub Copilot. Saya menjelaskan sifat skema dan tabel saya, dan memintanya untuk menulis beberapa SQL, dan kemudian meminta untuk mengubahnya menjadi Arel. Atau saya memberikan cuplikan kecil dari SQL, dan memintanya untuk mengubahnya menjadi Arel. Saya kemudian menyalin/menempelkan hasilnya ke kelas pembuat SearchCraft saya.
Sangatlah berharga untuk belajar mengekspresikan kueri penelusuran Anda dalam SQL atau Arel, dan menempatkannya dalam tampilan nyata SearchCraft. Pengguna Anda akan mendapatkan pengalaman secepat kilat.
Di dalam aplikasi Rails Anda, tambahkan permata ke Gemfile Anda:
bundle add searchcraft
SearchCraft akan secara otomatis membuat tabel DB internal yang diperlukan, sehingga tidak ada migrasi database yang harus dijalankan. Dan tentu saja, secara otomatis akan membuat dan menciptakan kembali pandangan Anda yang terwujud.
Di dalam aplikasi Rails apa pun, Anda dapat mengikuti tutorial ini. Jika Anda tidak memiliki aplikasi Rails, gunakan aplikasi yang ada di folder demo_app
proyek ini.
Instal permata:
bundle add searchcraft
Pilih salah satu model aplikasi Anda yang ada, misalnya Product
, dan kami akan membuat tampilan nyata yang sepele untuk model tersebut. Katakanlah, kita ingin cara cepat untuk mendapatkan 5 produk terlaris dan beberapa detail yang akan kita gunakan untuk itu dalam tampilan HTML.
Buat file model ActiveRecord baru app/models/product_latest_arrival.rb
:
class ProductLatestArrival < ActiveRecord :: Base
include SearchCraft :: Model
end
Berdasarkan konvensi Rails, model ini akan mencari tabel atau tampilan SQL yang disebut product_latest_arrivals
. Ini belum ada.
Kami dapat mengonfirmasi hal ini dengan membuka rails console
dan mencoba menanyakannya:
ProductLatestArrival . all
# ActiveRecord::StatementInvalid ERROR: relation "product_latest_arrivals" does not exist
Kita dapat membuat kelas pembuat SearchCraft baru untuk menentukan tampilan material kita. Buat file baru app/searchcraft/product_latest_arrival_builder.rb
.
Saya menyarankan app/searchcraft
untuk pembuat Anda, tetapi mereka dapat masuk ke subfolder app/
apa pun yang dimuat secara otomatis oleh Rails.
class ProductLatestArrivalBuilder < SearchCraft :: Builder
def view_scope
Product . order ( created_at : :desc ) . limit ( 5 )
end
end
Di dalam rails console``, run
reload!` dan periksa kembali permintaan Anda:
reload!
ProductLatestArrival . all
ProductLatestArrival Load ( 1.3 ms ) SELECT "product_latest_arrivals" . * FROM "product_latest_arrivals"
=>
[ #<ProductLatestArrival:0x000000010a737d18
id : 1 ,
name : "Rustic Wool Coat" ,
active : true ,
created_at : Fri , 25 Aug 2023 07 :15 :16 . 995228000 UTC + 00 : 00 ,
updated_at : Fri , 25 Aug 2023 07 :15 :16 . 995228000 UTC + 00 : 00 ,
image_url : "https://loremflickr.com/g/320/320/coat?lock=1" > ,
...
Jika Anda memasang permata annotate
di Gemfile
Anda, Anda juga akan melihat bahwa model product_latest_arrival.rb
telah diperbarui untuk mencerminkan kolom dalam tampilan terwujud.
# == Schema Information
#
# Table name: product_latest_arrivals
#
# id :bigint
# name :string
# active :boolean
# created_at :datetime
# updated_at :datetime
# image_url :string
#
class ProductLatestArrival < ActiveRecord :: Base
include SearchCraft :: Model
end
Jika aplikasi Anda berada di bawah kendali sumber, Anda juga dapat melihat bahwa db/schema.rb
telah diperbarui untuk mencerminkan definisi tampilan terbaru. Jalankan git diff db/schema.rb
:
create_view "product_latest_arrivals" , materialized : true , sql_definition : <<-SQL
SELECT products.id,
products.name,
products.active,
products.created_at,
products.updated_at,
products.image_url
FROM products
LIMIT 5;
SQL
Anda sekarang dapat terus mengubah view_scope
di pembuat Anda, dan menjalankan reload!
di konsol Rails untuk menguji perubahan Anda.
Misalnya, Anda dapat select()
hanya kolom yang Anda inginkan menggunakan ekspresi SQL untuk masing-masing kolom:
class ProductLatestArrivalBuilder < SearchCraft :: Builder
def view_scope
Product
. order ( created_at : :desc )
. limit ( 5 )
. select (
"products.id as product_id" ,
"products.name as product_name" ,
"products.image_url as product_image_url" ,
)
end
end
Atau Anda dapat menggunakan ekspresi Arel untuk membuat SQL:
class ProductLatestArrivalBuilder < SearchCraft :: Builder
def view_scope
Product
. order ( created_at : :desc )
. limit ( 5 )
. select (
Product . arel_table [ :id ] . as ( "product_id" ) ,
Product . arel_table [ :name ] . as ( "product_name" ) ,
Product . arel_table [ :image_url ] . as ( "product_image_url" ) ,
)
end
end
Bagaimana dengan pembaruan data? Mari ciptakan lebih banyak Products
:
Product . create! ( name : "Starlink" )
Product . create! ( name : "Fishing Rod" )
Jika Anda memeriksa ProductLatestArrival.all
Anda tidak akan menemukan produk baru ini. Hal ini karena tampilan materialisasi merupakan cuplikan data pada saat dibuat atau terakhir kali di-refresh.
Untuk menyegarkan tampilan:
ProductLatestArrival . refresh!
Sebagai alternatif, untuk menyegarkan semua tampilan:
SearchCraft :: Model . refresh_all!
Dan konfirmasikan bahwa pendatang baru terbaru kini dalam tampilan yang terwujud:
ProductLatestArrival . pluck ( :name )
=> [ "Fishing Rod" , "Starlink" , "Sleek Steel Bag" , "Ergonomic Plastic Bench" , "Fantastic Wooden Keyboard" ]
Jika Anda ingin menghapus artefak dari tutorial ini. Pertama, hapus tampilan terwujud dari skema database Anda:
ProductLatestArrivalBuilder . new . drop_view!
Kemudian hapus file dan git checkout .
untuk mengembalikan perubahan lainnya.
rm app/searchcraft/product_latest_arrival_builder.rb
rm app/models/product_latest_arrival.rb
git checkout db/schema.rb
SearchCraft menyediakan dua tugas menyapu:
rake searchcraft:refresh
- menyegarkan semua tampilan yang terwujudrake searchcraft:rebuild
- periksa apakah ada tampilan yang perlu dibuat ulang Untuk menambahkan ini ke aplikasi Rails Anda, tambahkan yang berikut ini ke bagian bawah Rakefile
Anda :
SearchCraft . load_tasks
Builder
, dan secara otomatis mendeteksi perubahan untuk mewujudkan skema tampilan dan membuatnya kembalirefresh!
konten tampilan terwujuddb/schema.rb
setiap kali tampilan material diperbaruiannotate
dipasangrake searchcraft:refresh
, dan periksa apakah ada tampilan yang perlu dibuat ulang rake searchcraft:rebuild
Setelah memeriksa repo, jalankan bin/setup
untuk menginstal dependensi. Kemudian, jalankan rake test
untuk menjalankan pengujian. Anda juga dapat menjalankan bin/console
untuk perintah interaktif yang memungkinkan Anda bereksperimen.
Untuk memasang permata ini ke mesin lokal Anda, jalankan bundle exec rake install
. Untuk merilis versi baru, perbarui nomor versi di version.rb
, lalu jalankan bundle exec rake release
, yang akan membuat tag git untuk versi tersebut, dorong git commit dan tag yang dibuat, dan dorong file .gem
ke rubygems. organisasi.
Untuk menambah nomor versi:
gem bump
, misal gem bump -v patch
demo_app/Gemfile.lock
, misalnya (cd demo_app; bundle)
git add demo_app/Gemfile.lock; git commit --amend --no-edit
rake release
rilis gem bump -v patch
(cd demo_app; bundle)
git add demo_app/Gemfile.lock; git commit --amend --no-edit
git push
rake release
Laporan bug dan permintaan penarikan diterima di GitHub di https://github.com/drnic/searchcraft. Proyek ini dimaksudkan untuk menjadi ruang kolaborasi yang aman dan ramah, dan kontributor diharapkan mematuhi kode etik.
Permata ini tersedia sebagai sumber terbuka berdasarkan ketentuan Lisensi MIT.
Setiap orang yang berinteraksi dalam basis kode proyek Searchcraft, pelacak masalah, ruang obrolan, dan milis diharapkan mengikuti kode etik.
rails db:rollback
, membangun kembali migrasi SQL, rails db:migrate
, dan kemudian test - menjadi lambat. Ini juga menimbulkan bug - Saya lupa menjalankan langkah-langkahnya, dan kemudian melihat perilaku aneh. Jika Anda memiliki tampilan yang relatif statis atau tampilan terwujud, dan ingin menggunakan migrasi Rails, silakan coba permata scenic
. Permata searchcraft
ini masih bergantung pada scenic
untuk fitur refresh
tampilannya, dan menambahkan tampilan ke dalam schema.rb
.