데이터 가져오기
데이터 가져오기 (Import Data)
DuckDB-Wasm에는 데이터 포맷에 따라 여러 방식으로 데이터를 가져올 수 있는 방법이 있어요. 가져오기는 두 단계로 이루어져요.
- 데이터 파일을 register 함수들(registerFileBuffer, registerFileHandle, registerFileText, registerFileURL) 중 하나로 가상 파일시스템에 등록해요. 등록된 파일은 모든 커넥션에 보이므로 데이터베이스 객체(
db)에서 호출돼요. - 데이터를 insert 함수들(insertArrowFromIPCStream, insertArrowTable, insertCSVFromPath, insertJSONFromPath) 중 하나로 DuckDB에 로드해요. 이 함수들은 커넥션(
conn)에서 호출돼요. 또는 Parquet, JSON, Wasm-flavored httpfs 확장이 등록된(또는 원격) 파일을FROMSQL 쿼리에서 직접 읽을 수 있고, [INSERT문]({% link docs/current/data/insert.md %})으로 값을 인라인 로드할 수 있어요.
아래 예시들은 모두 [DuckDB-Wasm을 인스턴스화할 때]({% link docs/current/clients/wasm/instantiation.md %}) 만든 db 객체에서 연 conn 커넥션을 사용해요.
출처: 문서
본문
커넥션 열고 닫기
모든 삽입과 쿼리는 커넥션에서 실행돼요. connect()로 열고, 끝나면 메모리를 해제하기 위해 닫아요.
// Create a new connection
const conn = await db.connect();
// ... import data
// Close the connection to release memory
await conn.close();
Apache Arrow
Apache Arrow는 DuckDB-Wasm의 네이티브 데이터 프로토콜이므로 Arrow 데이터를 직접 삽입할 수 있어요. insertArrowTable()에 기존 arrow.Table을 넘겨요.
Warning DuckDB-Wasm이 의존하는
apache-arrow패키지(현재^17)와 같은 메이저 버전을 사용하세요. 더 새로운 메이저 버전은insertArrowTable()·insertArrowFromIPCStream()같은 Arrow 삽입을 실패시켜요 — 종종 조용히 실패해서 대상 테이블이 만들어지지 않아요. DuckDB-Wasm의apache-arrow의존성을 확인하고apache-arrow설치를 그것에 맞게 고정하세요.
import { tableFromArrays } from 'apache-arrow';
const arrowTable = tableFromArrays({
id: [1, 2, 3],
name: ['John', 'Jane', 'Jack'],
age: [20, 21, 22],
});
await conn.insertArrowTable(arrowTable, { name: 'arrow_table' });
raw Arrow IPC 스트림으로 도착하는 데이터(예: fetch() 응답)를 삽입하려면 각 청크를 읽어 insertArrowFromIPCStream()에 넘기고, 스트림 끝(EOS) 마커를 써서 테이블이 완성됐음을 알려요.
// EOS signal according to the Arrow IPC streaming format
// See https://arrow.apache.org/docs/format/Columnar.html#ipc-streaming-format
const EOS = new Uint8Array([255, 255, 255, 255, 0, 0, 0, 0]);
const streamResponse = await fetch(`someapi`);
const streamReader = streamResponse.body.getReader();
const streamInserts = [];
while (true) {
const { value, done } = await streamReader.read();
if (done) break;
streamInserts.push(conn.insertArrowFromIPCStream(value, { name: 'streamed' }));
}
// Write the EOS marker
streamInserts.push(conn.insertArrowFromIPCStream(EOS, { name: 'streamed' }));
await Promise.all(streamInserts);
CSV
CSV 텍스트를 파일로 등록한 뒤 insertCSVFromPath()로 로드해요. insert 옵션은 대상 테이블을, 그리고 자동 감지가 꺼져 있으면 CSV 방언과 열 타입을 설명해요.
import { Int32, Utf8 } from 'apache-arrow';
const csvContent = '1|foo\n2|bar\n';
await db.registerFileText('data.csv', csvContent);
await conn.insertCSVFromPath('data.csv', {
schema: 'main',
name: 'foo',
detect: false,
header: false,
delimiter: '|',
columns: {
col1: new Int32(),
col2: new Utf8(),
},
});
JSON
insertJSONFromPath()는 행 우선(row-major)과 열 우선(column-major) JSON 둘 다 받아들여요. 문서를 먼저 등록한 뒤 삽입해요.
// Row-major format
const jsonRowContent = [
{ "col1": 1, "col2": "foo" },
{ "col1": 2, "col2": "bar" },
];
await db.registerFileText('rows.json', JSON.stringify(jsonRowContent));
await conn.insertJSONFromPath('rows.json', { name: 'rows' });
// Column-major format
const jsonColContent = {
"col1": [1, 2],
"col2": ["foo", "bar"]
};
await db.registerFileText('columns.json', JSON.stringify(jsonColContent));
await conn.insertJSONFromPath('columns.json', { name: 'columns' });
API에서 fetch한 JSON을 가져오려면 응답 바이트를 파일 버퍼로 등록해요.
const streamResponse = await fetch(`someapi/content.json`);
await db.registerFileBuffer('file.json', new Uint8Array(await streamResponse.arrayBuffer()));
await conn.insertJSONFromPath('file.json', { name: 'JSONContent' });
Parquet
Parquet 파일은 전용 insert 함수로 로드하지 않고 등록한 뒤 SQL로 읽어요. 사용자가 고른 로컬 파일은 registerFileHandle(), 원격 파일은 registerFileURL(), 이미 fetch한 바이트는 registerFileBuffer()로 등록해요.
// Local file chosen by the user
const pickedFile: File = letUserPickFile();
await db.registerFileHandle('local.parquet', pickedFile, DuckDBDataProtocol.BROWSER_FILEREADER, true);
// Remote file, read lazily over HTTP
await db.registerFileURL('remote.parquet', 'https://origin/remote.parquet', DuckDBDataProtocol.HTTP, false);
// Bytes already fetched into memory
const res = await fetch('https://origin/remote.parquet');
await db.registerFileBuffer('buffer.parquet', new Uint8Array(await res.arrayBuffer()));
파일이 등록되면 이름으로 쿼리해요.
await conn.query(`CREATE TABLE local AS SELECT * FROM 'local.parquet'`);
httpfs (Wasm-Flavored)
[Wasm-flavored httpfs 확장]({% link docs/current/clients/wasm/extensions.md %}#httpfs)을 쓰면 등록을 건너뛰고 SQL 텍스트에 URL을 넣어 원격 파일을 직접 읽을 수 있어요.
await conn.query(`
CREATE TABLE direct AS
SELECT * FROM 'https://origin/remote.parquet'
`);
Tip 원격 파일을 읽으면 브라우저가 요청을 내므로 브라우저의 CORS 정책을 따라야 해요. S3나 다른 원격 파일에 대한 쿼리가 네트워크 오류로 실패하면 [Troubleshoot]({% link docs/current/clients/wasm/troubleshoot.md %}#network-error-when-querying-remote-files)을 참고하세요.
Insert 문
마지막으로 [INSERT 문]({% link docs/current/data/insert.md %})은 파일을 등록하지 않고 값을 직접 로드해요. 소량의 데이터나 기존 테이블에 추가할 때 유용해요.
await conn.query(`
INSERT INTO existing_table
VALUES (1, 'foo'), (2, 'bar')`);
더 알아보기 (Learn more)
- [Run Queries]({% link docs/current/clients/wasm/query.md %}) — 여기서 가져온 데이터를 쿼리하고 결과를 내보내기.
- [Load Extensions]({% link docs/current/clients/wasm/extensions.md %}) — SQL에서 파일을 직접 읽는 데 쓰는 Parquet, JSON, Wasm-flavored httpfs 확장.
- [
INSERT문]({% link docs/current/data/insert.md %}) — 위 insert 함수들의 대안인 DuckDB의 SQL 레벨INSERT. - [Instantiate]({% link docs/current/clients/wasm/instantiation.md %}) — 이 가져오기들이 실행되는
db와 커넥션 만들기. - [Troubleshoot]({% link docs/current/clients/wasm/troubleshoot.md %}) — 원격 파일을 읽을 때의 CORS 네트워크 오류.