vte.cx 및 BigQuery 통합

50395 단어 SQLBigQueryvtecxtech
vte.cx의 데이터 저장소를 BigQuery, vte로 설정합니다.cx API를 통한 SQL 실행

0. BigQuery 준비

  • 데이터 세트 생성
  • BigQuery의 서비스 계정 개인 키 만들기
    서비스 계정은 Google의 서비스 권한이 있는 계정입니다.Google 서비스에 대한 API 요청 시 인증 정보로 사용됩니다.
  • 1. vte.cx측의 협업 설정

  • setup/_settings 바로 아래bigquery.json 배치
  • {
        "type": "XXX",
        "project_id": "XXX",
        "private_key_id": "XXX",
        "private_key": "XXX",
        "client_email": "XXX",
        "client_id": "XXX",
        "auth_uri": "XXX",
        "token_uri": "XXX",
        "auth_provider_x509_cert_url": "XXX",
        "client_x509_cert_url": "XXX"
    }
    
  • properties.xmlrights항목에 3줄 추가
  • _bigquery.projectid={プロジェクトID}
    _bigquery.dataset={データセット名}
    _bigquery.location=asia-northeast1
    
  • npm run upload
  • 등록!


    처음 등록할 때 vte입니다.cx측의 모드 항목과 key (STRING), updated (DATETIME), deleted (BOOL), postBQ(request: any, async: boolean, tablenames?: any): void 항목을 가진 표를 만듭니다.request

    API의 설명


    request:
    template.xml 발췌문
    <content>foo
     bar(string)
     baz(string)</content>
    
    link↓ 상단 모드 대응
    [
      {
        foo: { bar: 'test', baz: 'テスト' },
        link: [{ ___rel: 'self', ___href: 'エントリごとに一意' }]
      }
    ]
    
    ___rel: 'self'___href,'/{エンドポイントのキー名}/{採番処理で振られたID番号}'는 보통___hrefkey의 값은 { '第一階層の項目名' : 'テーブル名' } 항목으로 BigQuery 테이블에 등록됩니다.
    async:_
    지정getBQ(sql: string, parent?: string): any을 통해 다른 표에 등록할 수 있습니다.

    위 모드에서의 실현 예시


    /src/server/registerBQ.ts
    import * as vtecxapi from 'vtecxapi'
    
    const KEY = 'somefoo'
    const END_POINT = '/d/' + KEY
    const PARENT = 'foo'
    const TABLE = 'footable' // 'リクエストはこのテーブルに登録される'
    
    const [{ foo }] = vtecxapi.getRequest()
    
    const id = vtecxapi.allocids(END_POINT, 1) //採番
    
    const request = [
      { [PARENT]: { ...foo, id }, link: [{ ___href: `/${KEY}/${id}`, ___rel: 'self' }] }
    ]
    
    const result = vtecxapi.postBQ(request, false, { [PARENT]: TABLE }) // 冗長に引数tablenamesを記述した例
    vtecxapi.doResponse({ feed: { title: result } }) // ATOM形式のレスポンスのtitle項目に結果を持たせる
    
    ↑ 파일
    /src/components/register.ts
    const foo =  { bar: 'test', baz: 'テスト' }
    axios.put('/s/registerBQ', [{ foo }] ) // 通例としてPOSTよりPUTを使う
    

    얻다

    key실행 결과는parent의 하위 요소입니다.항목 내에서 모드 또는 ATOM 항목에 없는 항목을 지정하는 중 오류 발생
    SQL 실행 및 체크 아웃된 데이터 반환
    데이터 업데이트 없이 항상 추적되므로 동일한 updated 의 최신 및 deleted=false SQL 가져오기

    설치 예


    /src/server/readBQ.ts
    import * as vtecxapi from 'vtecxapi'
    
    const DATASET = '{BigQueryの該当データセット名}'
    const TABLE = '{BigQueryの該当テーブル名}'
    const SCHEMA = {userid:'ID',name:'名前',gender:'性別',age:'年齢',birth:'生年月日',email:'メールアドレス',tel:'電話番号',postcode:'郵便番号',address:'住所',memo:'備考欄'}
    // あらかじめ用意したテーブルから作成
    const ITEMS = Object.keys(SCHEMA)
    const TITLES = Object.values(SCHEMA)
    const ORDER = 'order by f.userid asc'
    const sql = `
    select
      ${ITEMS.join(',') /* =>'useid,name,...,memo' */}
    from
      ${DATASET}.${TABLE} as f
      right join
        ( select
            key,
            max(updated) as updated
          from
            ${DATASET}.${TABLE}
            group by
              key
        ) as k
        on
          f.updated = k.updated and
          f.key = k.key
    where
      f.deleted = false
    ${ORDER}
    `
    const result = vtecxapi.getBQ(sql, PARENT) // 冗長に引数parentを指定した例
    vtecxapi.doResponse(result)
    
    /src/components/read.ts
     const read = async () => await axios.get('/s/readBQ')
    

    편집자


    편집된 데이터key에서 무단으로 재등록합니다.

    삭제

    postBQ-파라미터requestlink___href를 항목으로 표에 등록한다.key→매개 변수deleteBQ에 표에 추가된 keys: string[] 항목
    → 등록 key인 레코드deletedkeys: trueasync:_
    tablenames: deleteBQ(keys: string[], async: boolean, tablenames?:any): void → 다른 표에서 삭제

    설치 예


    /src/components/delete.tsx
    // userエントリの形 { userid: 1 , name: 太郎, ... }
    const keys = [user1, user2, ...].map(({ userid }) => userid)
    const delete = async () => await axios.put('/s/deleteBQ', keys)
    
    /src/server/deleteBQ.ts
    const _keys = vtecxapi.getRequest()
    const keys = _keys.map(id => `/${TABLE}/${id}`)
    vtecxapi.deleteBQ(keys, false, { [PARENT]: TABLE })
    

    전체 수량 획득


    getTotal.ts
    // vtecxapi.getQueryString(param:string)で検索条件など取得できる
    const sql =`
    select
      cast(count(*) as string) as title
    from
      ${DATASET}.${TABLE} as f
      right join
        ( select
            key,
            max(updated) as updated
          from
            ${DATASET}.${TABLE}
            group by
              key
        ) as k
        on
          f.updated = k.updated and
          f.key = k.key
    where
      f.deleted = false
      ${/*検索条件など*/}
    `
    const total = getBQ(sql) // =>[ { 'title': '合計件数' } ] ←SQLクエリの'as title'によって結果が'title'に入る
    vtecxapi.doResponse(total) // レスポンスに入れるエントリにはATOM項目とユーザー定義スキーマの項目のみを入れられる
    

    페이지 및 검색을 수행하는 SQL 예


    link의 href 항목 문장의 페이지 문자와 검색 기능 사용하기


    /src/server/getUsers.ts
    import * as vtecxapi from 'vtecxapi'
    import { escape } from 'sqlstring'
    
    const DATASET = 'データセット名'
    const TABLE = 'テーブル名'
    // テーブルから取得する項目
    const ITEMS = [ 'name', 'userid', 'gender', 'birth', 'age', 'tel', 'email', 'address', 'postcode', 'memo']
    // SQLクエリを組み立てる
    // 検索のためのクエリ
    // クエリパラメータ'search'から'name'項目の検索キーワードを設定
    const search = encodeURIComponent(vtecxapi.getQueryString('search'))
    const name_search = search ? ` and name = "${escape(search)}"` : ''
    // ページネーションのためのクエリ
    // クエリパラメータ'page'からページを設定
    const page_num = Number(encodeURIComponent(vtecxapi.getQueryString('page'))) || 1
    const pagination = `limit 5 offset ${escape(page_num - 1)}` // 5件ずつ
    const conditions = `${name_search} order by f.userid desc ${pagination}`
    const total = vtecxapi.getBQ(sql_total) // =>[ { 'title': '合計数' } ] sql文で'as title'としているため。
    // ページネーションのためにフィードを数件ごとに取得
    const sql_feed = `
    select
      ${ITEMS.join(',')}
    from
      ${DATASET}.${TABLE} as f
      right join
      ( select
          key,
          max(updated) as updated
        from
          ${DATASET}.${TABLE}
          group by
            key
      ) as k
      on
        f.updated = k.updated and
        f.key = k.key
    where
      f.deleted = false
      ${conditions}
    `
    // 'select userid,name,gender,age,email,tel,postcode,address,memo, from ${DATASET}.${TABLE} as f right join (select key,max(updated) as updated from ${DATASET}.${TABLE} group by key) as k on f.updated=k.updated and f.key=k.key where f.deleted=false'
    
    const result = vtecxapi.getBQ(sql_feed, PARENT)
    vtecxapi.doResponse(result) // =>[ { 'user': { 'userid': '1', 'name': '太郎', ... } }, ... ]
    
    { '第一階層の項目名' : 'テーブル名' }, OFFSET를 사용하는 페이지 판식을 편이 방식이라고 한다.

    키를 사용하여 설정된 페이지 스타일


    각 행LIMIT에서 테이블에서 추출한 고유OFFSET 항목(→ 키 세트)을 사용하여 아무 페이지나 id의 데이터를 가져오는 SQL 발행
    클라이언트
    URL 쿼리 매개 변수LENGTH(가정)에서 페이지 번호의 바닥글 가져오기
    src/hooks/usePage.tsx
    import { useLocation } from 'react-router-dom'
    export const usePage = () => {
      const _search_params = useLocation().search
      const search_params = new URLSearchParams(_search_params)
      const page = Number(query.get('page')) || 1
      return page
    }
    
    페이지 인터럽트에 사용할 키 집합을 요청하고, 이 키 집합과 갈고리를 통해 요청한 페이지 번호 피드를 사용합니다
    /src/components/getUsers.tsx
    import { usePage } from '../hooks/usePage'
    const shouldUpdateKeyset = useRef(true)
    const Users = () => {
      const [keyset, setKeyset] = useState([])
      const page = usePage()
      const [feed, setFeed] = useState([])
      const getFeed = () => {
        if (shouldUpdateKeyset.current) {
          const _keyset = async () => await axios.get('/s/getKeyset')
          // =>[ { title: '11', subtitle: '1' }, { title: '5', subtitle: '2' }, { title: '1', subtitle: '3' } ]
          // (データが15個ある場合), 'title'に`userid`、`subtitle`にページ番号
          setKeyset(_keyset.data.map(({title})=>title))
          shouldUpdateKeyset.current = false
        }
        if (keyset.hasOwnProperty(page-1)) {
          const _feed = await axios.get(`/s/getUsers?pagekey=${keyset[page-1]?.title}`)
          setFeed(_feed)
        }
      }
      useEffect(getFeed,[])
      return // feedを描写
    }
    
    서버측
  • 추출된 페이지의 키 세트
  • /src/server/getKeyset.ts
    import * as vtecxapi from 'vtecxapi'
    const DATASET = 'データセット'
    const TABLE = 'テーブル'
    const LENGTH = 5
    const ORDER = 'order by userid desc'
    const keyset = getBQ(`
    with x as (
      select
        case
          mod(
            row_number()
            over(
              ${ORDER}
            ),
            ${LENGTH}
          )
        when 0
          then 1
        else 0
        end as page_boundary,
        f.*
      from
        ${DATASET}.${TABLE} as f
          right join (
            select
              key,
              max(updated) as updated
            from
              ${DATASET}.${TABLE}
              group by
                key
          ) as k
          on
            f.updated = k.updated and
            f.key = k.key
      where
        f.deleted = false
      ${ORDER}
    )
    select
      userid as title,
      cast (
        row_number()
        over(
          ${ORDER}
        ) + 1
        as string
      ) as subtitle
    from
      x
    where
      x.page_boundary = 1
    `)
    vtecxapi.doResponse(keyset)
    
  • 위에서 만든 키로 만들어 달라는 요청에 피드
  • 를 되돌려줍니다
    /src/server/getUsers.ts
    import * as vtecxapi from 'vtecxapi'
    import { escape } from 'sqlstring'
    
    const DATASET = 'データセット'
    const TABLE = 'テーブル'
    const LENGTH = 5
    const pagekey = escape(vtecxapi.getQueryString('pagekey')) || 0
    const ITEMS = [ 'name', 'userid', 'gender', 'birth', 'age', 'tel', 'email', 'address', 'postcode', 'memo']
    const feed = vtecxapi.getBQ(`
    select
      ${ITEMS.join(',') /* =>'useid,name,...,memo' */}
    from
      ${DATASET}.${TABLE} as f
      right join (
        select
          key,
          max(updated) as updated
        from
          ${DATASET}.${TABLE}
          group by
            key
      ) as k
      on
        f.updated = k.updated and
        f.key = k.key
    where
      f.deleted = false and
      userid >= ${pagekey/*'userid'がページキーである'userid'と等しいか、それより小さい行を'LENGTH'分だけ抽出*/}
    ${ORDER/*'userid'降順*/}
    limit ${LENGTH}
    `)
    
    + 필요에 따라 총 데이터 수, 필터 결과(→ 검색 기능) 등 획득

    좋은 웹페이지 즐겨찾기