Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

숨겨진 보석:ActiveRecord Store

Rails는 특정 상황을 위한 편리한 내장 도구가 많이 포함된 대규모 프레임워크입니다. 이 시리즈에서는 Rails의 대규모 코드베이스에 숨겨진 잘 알려지지 않은 도구를 살펴보겠습니다.

이 기사에서는 ActiveRecord의 store에 중점을 둘 것입니다. 및 store_accessor 행동 양식. 이 두 가지 방법 모두 JSON 또는 YAML과 같은 데이터베이스 열에 구조화된 데이터를 저장하는 사용 사례를 목표로 합니다. store_accessor 동안 getter 메소드, store로 모델을 방해하지 않고 이러한 데이터에서 값을 가져올 수 있는 편리한 방법을 제공합니다. 한 단계 더 나아가 우리가 선택한 형식으로 데이터를 투명하게 직렬화/역직렬화합니다. 이것이 어디에 유용할 수 있는지 이해하기 위해 관계형 데이터베이스에 JSON을 저장하는 옵션과 그렇게 하려는 이유도 살펴보겠습니다.

데이터베이스의 JSON

이 기사에서 '데이터베이스'라고 말할 때 관계형 데이터베이스, 특히 PostgreSQL과 MySQL이 Rails 커뮤니티에서 가장 널리 사용되는 관계형 데이터베이스임을 분명히 밝힙니다.

JSON을 관계형 데이터베이스에 저장하려는 이유를 물을 수 있습니다. 실제로 관계형 데이터베이스의 이점을 활용하는 방법은 데이터를 분해하여 관계 그들 사이는 데이터베이스(예:외래 키)에 의해 시행될 수 있으며 데이터는 쿼리 성능을 향상시키기 위해 인덱싱될 수 있습니다.

관계형 데이터베이스 모델의 한 가지 단점은 데이터 구조가 미리 알려져야 하고 테이블의 각 행에 대해 동일합니다. 애플리케이션이 이러한 요구 사항을 충족하지 않는 데이터를 중심으로 구축된 경우 NoSQL 데이터베이스를 조사할 수 있습니다. 그러나 대부분의 웹 앱에서 우리는 대부분의 데이터에 대해 알고 있는 ~~악마~~ 관계형 데이터베이스를 고수하고 이러한 동적 데이터 구조에 신중하게 "뿌리기"를 원합니다. 이러한 경우 JSON 열과 같은 것이 많은 의미를 가질 수 있습니다.

JSON 대 JSONB

PostgreSQL에는 두 종류의 JSON 열이 있습니다. jsonjsonb . 주요 차이점은 jsonb 쓰기 시간에 구문 분석됩니다. 즉, 데이터베이스가 더 빠르게 쿼리할 수 있는 형식으로 데이터가 저장됩니다. 주의할 점은 JSON이 이미 구문 분석되었기 때문에 텍스트로 출력할 때 사용자가 입력한 내용과 더 이상 일치하지 않을 수 있다는 것입니다. 예를 들어 중복 키가 제거되거나 키 순서가 원본과 일치하지 않을 수 있습니다.

PostgreSQL 문서에서는 대부분의 경우 jsonb 특별한 이유가 없는 한 원하는 것입니다.

MySQL의 json 열은 jsonb와 유사하게 작동합니다. PostgreSQL에서. '사용자가 입력한 대로' 출력을 지원하려면 varchar를 사용해야 할 것입니다. 칼럼 또는 이와 유사한 것.

JSON 대 텍스트

데이터를 사전 구문 분석할 수 있을 뿐만 아니라 텍스트 필드에 동일한 데이터를 저장하는 대신 JSON 열을 사용하면 데이터 자체를 사용하는 쿼리가 허용됩니다. 예를 들어 열에 특정 키-값 쌍이 있는 모든 레코드를 쿼리할 수 있습니다. Rails 자체는 데이터베이스와 관련된 많은 JSON 관련 쿼리를 지원하지 않습니다. 따라서 이러한 기능을 활용하려면 SQL 쿼리를 사용해야 합니다.

Rails의 JSON 열

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가 저희를 도와줍니다.

ActiveRecord의 저장소 및 store_accessor

JSON을 데이터베이스에 저장하는 데에는 직렬화와 액세스라는 두 가지 측면이 있습니다. json을 사용하는 경우 -type 열이 데이터베이스에 있으면 직렬화 측면에 대해 걱정할 필요가 없습니다. Rails와 데이터베이스 어댑터가 알아서 처리합니다(store_accessor로 바로 건너뛸 수 있습니다. ). 데이터를 텍스트 열에 저장하는 경우 ActiveRecord의 store 열에 쓰는 데이터가 선택한 형식으로 직렬화되도록 하는 방법입니다.

ActiveRecord의 스토어

ActiveRecord에는 store가 있습니다. 열에 읽거나 쓰는 데이터를 자동으로 직렬화하는 방법:

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

여기, :user_attributes accessors는 우리가 사용하려는 열입니다. 액세스하려는 키 목록입니다(color 우리의 경우 여기), 마지막으로 데이터를 인코딩하는 방법을 지정합니다. 우리는 JSON을 사용하고 있지만 YAML 또는 사용자 정의 인코딩과 같은 것을 포함하여 여기에서 원하는 모든 것을 사용할 수 있습니다. 이 메소드는 직렬화(선택한 코더 사용)를 처리하고 store_accessor를 호출합니다. 후드 아래에서.

ActiveRecord의 store_accessor

store_accessor를 사용하여 모델에 get/set 메소드를 생성합니다. :

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 중첩 데이터는 지원하지 않으며 최상위 키-값 쌍만 지원합니다. prefixsuffix 옵션은 부울, 문자열 또는 기호를 사용합니다. 부울 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 키, 비밀 및 판매자 ID를 사용할 수 있습니다. 한 가지 접근 방식은 테이블에 열을 계속 추가하는 것입니다. 여기서 열은 대부분 null이 됩니다. 대부분의 공급자에 대해. json 사용 그러나 특정 API에 필요한 값만 저장할 수 있습니다.

내가 작업하고 있는 사이드 프로젝트도 JSON 스토리지를 사용하여 사용자가 사용자 정의 속성을 포함하여 항목에 임의의 속성을 설정할 수 있도록 합니다. 이 데이터의 유동적이고 예측할 수 없는 특성을 감안할 때 JSON 스토리지(store_accessor 포함) 알려진 속성에 대한 s)이(가) 자연스럽게 적합합니다.

요약

JSON 데이터(및 주변 ActiveRecord의 도우미)는 데이터 및 데이터 구조가 변경 가능하거나 알 수 없을 때 매우 유용할 수 있습니다. 물론 이러한 종류의 데이터 저장은 대부분의 경우와 마찬가지로 절충점입니다. 특정 레코드에 대한 데이터 구조에서 많은 유연성을 얻을 수 있지만 데이터베이스 제약 조건이 제공할 수 있는 데이터 무결성의 일부를 포기합니다. 또한 일반적인 ActiveRecord 쿼리, 조인 등을 사용하여 레코드 전체를 쿼리하는 능력도 감소합니다.

다음은 몇 가지 경험적 규칙입니다.

  1. JSON 키가 모든 행에 대해 동일함을 알거나
  2. 다른 데이터베이스 테이블의 ID(기본 키)를 저장하거나
  3. JSON의 테이블에서 레코드를 조회하는 데 사용되는 값을 저장하고 있습니다.

그런 다음 데이터베이스를 활용하여 데이터 무결성을 적용할 수 있는 새 테이블을 만드는 것이 더 나을 수 있습니다. 그러나 다른 테이블과 직접 관련이 없는 행별 데이터를 저장하는 경우 JSON이 데이터베이스 구조를 단순화하는 데 도움이 될 수 있습니다.