Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

อัญมณีที่ซ่อนอยู่:ActiveRecord Store

Rails เป็นเฟรมเวิร์กขนาดใหญ่พร้อมเครื่องมือในตัวที่มีประโยชน์มากมายสำหรับสถานการณ์เฉพาะ ในชุดนี้ เราจะมาดูเครื่องมือที่ไม่ค่อยมีใครรู้จักซึ่งซ่อนอยู่ในฐานโค้ดขนาดใหญ่ของ Rails

ในบทความนี้ เราจะเน้นที่ store . ของ ActiveRecord และ store_accessor วิธีการ ทั้งสองวิธีนี้มุ่งเป้าไปที่กรณีการใช้งานของการจัดเก็บข้อมูลที่มีโครงสร้างในคอลัมน์ฐานข้อมูล เช่น JSON หรือ YAML ขณะที่ store_accessor เป็นวิธีที่สะดวกแก่เราในการดึงค่าจากข้อมูลเหล่านี้โดยไม่ทำให้เกิดการอุดตันของโมเดลด้วยเมธอดของ getter store ก้าวไปอีกขั้นและจัดลำดับ/ดีซีเรียลไลซ์ข้อมูลให้เป็นรูปแบบที่เราเลือกอย่างโปร่งใส เพื่อให้เข้าใจว่าสิ่งนี้มีประโยชน์อย่างไร เราจะดูที่ตัวเลือกสำหรับการจัดเก็บ JSON ในฐานข้อมูลเชิงสัมพันธ์และสาเหตุบางประการที่คุณอาจต้องการทำเช่นนั้น

JSON ในฐานข้อมูล

ฉันควรชี้แจงว่าเมื่อฉันพูดว่า 'ฐานข้อมูล' ในบทความนี้ ฉันหมายถึงฐานข้อมูลเชิงสัมพันธ์ โดยเฉพาะ PostgreSQL และ MySQL เนื่องจากเป็นฐานข้อมูลที่ใช้กันอย่างแพร่หลายที่สุดในชุมชน Rails

อาจมีคนถามว่าทำไมคุณถึงต้องการเก็บ JSON ในฐานข้อมูลเชิงสัมพันธ์ แท้จริงแล้ว วิธีใช้ประโยชน์จากฐานข้อมูลเชิงสัมพันธ์คือการแบ่งข้อมูลเพื่อให้ ความสัมพันธ์ ฐานข้อมูลสามารถบังคับใช้ระหว่างกันได้ (เช่น คีย์นอก) และสามารถจัดทำดัชนีข้อมูลเพื่อปรับปรุงประสิทธิภาพการสืบค้นได้

ข้อเสียประการหนึ่งของโมเดลฐานข้อมูลเชิงสัมพันธ์คือต้องทราบโครงสร้างข้อมูลล่วงหน้า และ เหมือนกันทุกแถวในตาราง หากแอปพลิเคชันของคุณสร้างขึ้นจากข้อมูลที่ไม่ตรงตามข้อกำหนดเหล่านี้ คุณอาจต้องการตรวจสอบฐานข้อมูล NoSQL สำหรับเว็บแอปส่วนใหญ่ เราต้องการยึดติดกับฐานข้อมูลเชิงสัมพันธ์ ~~ปีศาจ~~ ที่เรารู้จักสำหรับข้อมูลส่วนใหญ่ และเพียงแค่ "โรย" ในโครงสร้างข้อมูลแบบไดนามิกเหล่านี้อย่างรอบคอบ ในกรณีเหล่านี้ บางอย่างเช่นคอลัมน์ JSON ก็สมเหตุสมผลดี

JSON เทียบกับ JSONB

PostgreSQL มีคอลัมน์ JSON สองประเภท:json และ jsonb . ความแตกต่างที่สำคัญคือ jsonb ถูกแยกวิเคราะห์ ณ เวลาเขียน ซึ่งหมายความว่าข้อมูลจะถูกจัดเก็บในรูปแบบที่ฐานข้อมูลสามารถสืบค้นได้เร็วขึ้น ข้อแม้คือเนื่องจาก JSON ถูกแยกวิเคราะห์แล้ว เมื่อส่งออกเป็นข้อความ อาจไม่ตรงกับที่ผู้ใช้ป้อนอีกต่อไป ตัวอย่างเช่น คีย์ที่ซ้ำกันอาจถูกลบ หรือลำดับคีย์อาจไม่ตรงกับต้นฉบับ

เอกสาร PostgreSQL ระบุว่าในกรณีส่วนใหญ่ jsonb คือสิ่งที่คุณต้องการเว้นแต่คุณจะมีเหตุผลเฉพาะอย่างอื่น

jsonของ MySQL คอลัมน์ทำงานคล้ายกับ jsonb ใน PostgreSQL เพื่อรองรับผลลัพธ์ 'ตามที่ผู้ใช้ป้อน' คุณอาจต้องใช้ varchar คอลัมน์หรืออะไรทำนองนั้น

JSON กับ Text

นอกเหนือจากการอนุญาตให้แยกวิเคราะห์ข้อมูลล่วงหน้าแล้ว การใช้คอลัมน์ JSON แทนการจัดเก็บข้อมูลเดียวกันในช่องข้อความยังช่วยให้สามารถสืบค้นข้อมูลที่ใช้ข้อมูลนั้นเองได้ ตัวอย่างเช่น คุณสามารถสอบถามระเบียนทั้งหมดที่มีคู่คีย์-ค่าเฉพาะอยู่ในคอลัมน์ โปรดทราบว่า Rails เองไม่รองรับการสืบค้นข้อมูลเฉพาะ JSON จำนวนมาก (ถ้ามี) เนื่องจากเป็นแบบเฉพาะฐานข้อมูล ดังนั้น หากคุณต้องการใช้ประโยชน์จากคุณลักษณะเหล่านี้ คุณจะต้องใช้การสืบค้น SQL เพื่อดำเนินการ

คอลัมน์ JSON ใน Rails

Rails รองรับการสร้าง json (และ jsonb ในคอลัมน์ PostgreSQL) ในการย้ายข้อมูล:

class CreateItems < ActiveRecord::Migration[7.0]
  def change
    create_table :items do |t|
      t.jsonb :user_attributes

    ...
    end
  end
end

เมื่ออ่านคอลัมน์นี้ ผลลัพธ์ที่ได้คือ Hash:

> Item.first.user_attributes
  Item Load (0.6ms)  SELECT "items".* FROM "items" ORDER BY "items"."id" ASC LIMIT $1  [["LIMIT", 1]]
=> {"color"=>"text-red-400"}
> Item.first.update!(user_attributes: {color: "text-blue-400"})
> Item.first.user_attributes.dig(:color)
=> "text-blue-400"

ตอนนี้เรามีแอตทริบิวต์ Hash แล้ว คุณอาจถูกล่อลวงให้เพิ่มวิธีการช่วยเหลือให้กับโมเดลเพื่ออ่าน/เขียนค่า:

class Item < ApplicationRecord
  def color=(value)
    self.user_attributes["color"] = value
  end

  def color
    user_attributes.dig("color")
  end
end

วิธีการต่างๆ เช่น ฟังก์ชันนี้ใช้ได้ดีอย่างสมบูรณ์ แต่อาจกลายเป็นเทอะทะอย่างรวดเร็ว หากคุณมีคีย์ JSON จำนวนมากที่ต้องจัดการ โชคดีที่ Rails ช่วยคุณได้

ร้านค้าและ store_accessor ของ ActiveRecord

การจัดเก็บ JSON ในฐานข้อมูลมีสองด้าน:การทำให้เป็นอนุกรมและการเข้าถึง หากคุณกำลังใช้ json -type คอลัมน์ในฐานข้อมูลของคุณ คุณไม่จำเป็นต้องกังวลเกี่ยวกับลักษณะการซีเรียลไลซ์เซชั่น Rails และตัวแปลงฐานข้อมูลจะจัดการให้คุณ (คุณสามารถข้ามไปที่ store_accessor . ได้เลย ). หากคุณกำลังจัดเก็บข้อมูลในคอลัมน์ข้อความ store . ของ ActiveRecord วิธีนี้เหมาะสำหรับคุณ ซึ่งช่วยให้มั่นใจได้ว่าข้อมูลที่คุณเขียนลงในคอลัมน์นั้นจะถูกจัดลำดับในรูปแบบที่คุณเลือก

ร้านของ ActiveRecord

ActiveRecord มี store วิธีการเรียงลำดับข้อมูลที่เราอ่านหรือเขียนลงในคอลัมน์ของเราโดยอัตโนมัติ:

class Item < ApplicationRecord
  store :user_attributes, accessors: [:color], coder: JSON
end

ที่นี่ :user_attributes เป็นคอลัมน์ที่เราต้องการใช้ ในขณะที่ accessors คือรายการคีย์ที่เราต้องการเข้าถึง (แค่ color ในกรณีของเราที่นี่) และสุดท้าย เราระบุวิธีที่เราต้องการเข้ารหัสข้อมูล เรากำลังใช้ JSON แต่คุณสามารถใช้อะไรก็ได้ที่คุณชอบที่นี่ รวมถึงสิ่งต่างๆ เช่น YAML หรือการเข้ารหัสแบบกำหนดเอง วิธีนี้จะจัดการกับการทำให้เป็นอนุกรม (ด้วย coder ที่คุณเลือก) และเรียก store_accessor ภายใต้ประทุน

store_accessor ของ ActiveRecord

เราสร้างเมธอด get/set ในโมเดลของเราโดยใช้ store_accessor :

class Item < ApplicationRecord
  store_accessor :user_attributes, :color
  store_accessor :user_attributes, :name, prefix: true
  store_accessor :user_attributes, :location, prefix: 'primary'
end

ที่นี่อีกครั้ง user_attributes คือคอลัมน์ฐานข้อมูลที่เราต้องการใช้ ตามด้วยคีย์ที่เราต้องการใช้ในข้อมูล JSON และสุดท้าย เรามีตัวเลือกให้ใช้คำนำหน้า (หรือส่วนต่อท้าย) โปรดทราบว่า store_accessor ไม่รองรับข้อมูลที่ซ้อนกัน เฉพาะคู่คีย์-ค่าระดับบนสุดเท่านั้น prefix และ suffix ตัวเลือกใช้บูลีน สตริง หรือสัญลักษณ์ หากบูลีน true ถูกส่งผ่าน จากนั้นชื่อของคอลัมน์จะถูกใช้เป็นคำนำหน้า/ส่วนต่อท้าย

=>item = Item.create!(color: 'red', user_attributes_name: 'Jonathan', primary_location: 'New Zealand')
>#<Item:0x000055d63f4f0360
 id: 4,
 user_attributes: {"color"=>"red", "name"=>"Jonathan", "location"=>"New Zealand"}>
=>item.color
>"red"
=> item.user_attributes_name
>"Jonathan"
=> item.name
>NoMethodError: undefined method `name'...
=> item.primary_location
>"New Zealand"

การใช้งานจริง

ฉันต้องการเพียงบางครั้งที่จะหลงทางจากสคีมาฐานข้อมูลเชิงสัมพันธ์ที่ทราบล่วงหน้าโดยทั่วไป หลายครั้งที่ฉันมี มันทำให้โครงสร้างฐานข้อมูลทั้งสะอาดและเรียบง่ายกว่าที่ไม่มีตัวเลือกเหล่านี้

ตัวอย่างหนึ่งที่ฉันพบคือการสนับสนุน API หลายตัวที่ผู้ใช้เชื่อมต่อบัญชีของตนเอง สิ่งนี้จะกลายเป็นเรื่องยุ่งยากเมื่อ API ไม่ได้ใช้รูปแบบการตรวจสอบสิทธิ์แบบเดียวกัน บางคนอาจใช้ชื่อผู้ใช้+รหัสผ่าน ในขณะที่บางคนอาจใช้คีย์ API และยังมีคีย์ API ข้อมูลลับ และรหัสผู้ขายอีกด้วย วิธีหนึ่งคือการเพิ่มคอลัมน์ในตารางต่อไป โดยที่หลายๆ คอลัมน์จะเป็น null สำหรับผู้ให้บริการส่วนใหญ่ การใช้ json อย่างไรก็ตาม เราสามารถเก็บได้เฉพาะค่าที่ API เฉพาะต้องการ

โปรเจ็กต์ด้านข้างที่ฉันกำลังทำงานอยู่นั้นใช้ที่เก็บข้อมูล JSON เพื่อให้ผู้ใช้สามารถตั้งค่าแอตทริบิวต์ตามอำเภอใจของไอเท็ม ซึ่งรวมถึงแอตทริบิวต์ที่ผู้ใช้กำหนด ด้วยลักษณะที่ลื่นไหลและคาดเดาไม่ได้ของข้อมูลนี้ บางอย่างเช่นที่เก็บข้อมูล JSON (พร้อม store_accessor สำหรับแอตทริบิวต์ที่รู้จัก) เป็นแบบธรรมชาติ

สรุป

ข้อมูล JSON (และตัวช่วยของ ActiveRecord รอบตัว) จะมีประโยชน์มากเมื่อข้อมูลและโครงสร้างข้อมูลเปลี่ยนแปลงได้หรือไม่สามารถรับรู้ได้ แน่นอนว่าการจัดเก็บข้อมูลประเภทนี้เป็นเหมือนการแลกเปลี่ยน ในขณะที่คุณมีความยืดหยุ่นมากในโครงสร้างข้อมูลสำหรับบันทึกเฉพาะ คุณละทิ้งความสมบูรณ์ของข้อมูลบางส่วนที่ข้อจำกัดของฐานข้อมูลสามารถมอบให้คุณได้ คุณยังลดความสามารถในการสืบค้นข้ามระเบียนด้วยการสืบค้น ActiveRecord ทั่วไป การรวม ฯลฯ

ต่อไปนี้คือข้อควรปฏิบัติบางประการ หากคุณ:

  1. รู้ว่าคีย์ JSON จะเหมือนกันทุกแถวหรือ
  2. กำลังจัดเก็บ ID (คีย์หลัก) ของตารางฐานข้อมูลอื่นๆ หรือ
  3. กำลังจัดเก็บค่าที่ใช้เพื่อค้นหาบันทึกจากตารางใน JSON

จากนั้น คุณควรสร้างตารางใหม่ที่สามารถใช้ประโยชน์จากฐานข้อมูลเพื่อบังคับใช้ความสมบูรณ์ของข้อมูลสำหรับคุณ อย่างไรก็ตาม หากคุณกำลังจัดเก็บข้อมูลเฉพาะแถวที่ไม่เกี่ยวข้องโดยตรงกับตารางอื่น JSON อาจช่วยให้คุณลดความซับซ้อนของโครงสร้างฐานข้อมูลได้