Skip to content

Commit 3e48373

Browse files
authored
Merge pull request #199 from supabase/fix/firebase-nested-collection
fix(firebase_fdw): support nested collection
2 parents ef4b008 + d7fd6a1 commit 3e48373

File tree

10 files changed

+75
-21
lines changed

10 files changed

+75
-21
lines changed

docs/firebase.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ The full list of foreign table options are below:
101101

102102
- `object` - Object name in Firebase, required.
103103

104+
For Authenciation users, the object name is fixed to `auth/users`. For Firestore documents, its format is `firestore/<collection_id>`, note that collection id must be a full path id. For example,
105+
106+
- `firestore/my-collection`
107+
- `firestore/my-collection/my-document/another-collection`
108+
104109
## Examples
105110

106111
Some examples on how to use Firebase foreign tables.
@@ -118,7 +123,7 @@ create foreign table firebase_docs (
118123
)
119124
server firebase_server
120125
options (
121-
object 'firestore/user-profiles' -- format: 'firestore/[collection_id]'
126+
object 'firestore/user-profiles'
122127
);
123128
```
124129

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
## Start Firebase Emulator
2+
3+
ref: https://github.com/AndreySenov/firebase-tools-docker
4+
5+
```bash
6+
cd wrappers
7+
8+
docker run \
9+
--name "firebase-emu" \
10+
--rm \
11+
-p=9000:9000 \
12+
-p=8080:8080 \
13+
-p=4000:4000 \
14+
-p=9099:9099 \
15+
-p=8085:8085 \
16+
-p=5001:5001 \
17+
-p=9199:9199 \
18+
-v $PWD/dockerfiles/firebase:/home/node \
19+
andreysenov/firebase-tools:11.24.1-node-14-alpine \
20+
firebase emulators:start --project supa --only auth,firestore --import=/home/node/baseline-data
21+
```
22+
23+
## Export Firebase Data
24+
25+
```bash
26+
docker exec -it "firebase-emu" bash
27+
28+
firebase emulators:export ./baseline-data --project supa
29+
```
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"kind":"identitytoolkit#DownloadAccountResponse","users":[{"localId":"JeXJ2gUHCpYSQV6zXlRYfRYHUfMl","createdAt":"1668488830314","lastLoginAt":"1668488830310","displayName":"Bo Lu","photoUrl":"","emailVerified":false,"email":"[email protected]","salt":"fakeSaltNZhRYxXIjWaiXB75SD8z","passwordHash":"fakeHash:salt=fakeSaltNZhRYxXIjWaiXB75SD8z:password=adsf123","passwordUpdatedAt":1668488830313,"validSince":"1668488830","providerUserInfo":[{"providerId":"password","email":"[email protected]","federatedId":"[email protected]","rawId":"[email protected]","displayName":"Bo Lu","photoUrl":""}]},{"localId":"eUHS9EaQS2lZO0zbyMq28m02SUGP","createdAt":"1668488839113","lastLoginAt":"1668488839111","displayName":"Copple","photoUrl":"","emailVerified":false,"email":"[email protected]","salt":"fakeSaltL62cBYW58puy9DarO9TO","passwordHash":"fakeHash:salt=fakeSaltL62cBYW58puy9DarO9TO:password=asdf123","passwordUpdatedAt":1668488839113,"validSince":"1668488839","providerUserInfo":[{"providerId":"password","email":"[email protected]","federatedId":"[email protected]","rawId":"[email protected]","displayName":"Copple","photoUrl":""}]}]}
1+
{"kind":"identitytoolkit#DownloadAccountResponse","users":[{"localId":"JeXJ2gUHCpYSQV6zXlRYfRYHUfMl","createdAt":"1668488830314","lastLoginAt":"1668488830310","displayName":"Bo Lu","photoUrl":"","passwordHash":"fakeHash:salt=fakeSaltNZhRYxXIjWaiXB75SD8z:password=adsf123","salt":"fakeSaltNZhRYxXIjWaiXB75SD8z","passwordUpdatedAt":1701484518847,"providerUserInfo":[{"providerId":"password","email":"[email protected]","federatedId":"[email protected]","rawId":"[email protected]","displayName":"Bo Lu","photoUrl":""}],"validSince":"1701484518","email":"[email protected]","emailVerified":false,"disabled":false},{"localId":"eUHS9EaQS2lZO0zbyMq28m02SUGP","createdAt":"1668488839113","lastLoginAt":"1668488839111","displayName":"Copple","photoUrl":"","passwordHash":"fakeHash:salt=fakeSaltL62cBYW58puy9DarO9TO:password=asdf123","salt":"fakeSaltL62cBYW58puy9DarO9TO","passwordUpdatedAt":1701484518848,"providerUserInfo":[{"providerId":"password","email":"[email protected]","federatedId":"[email protected]","rawId":"[email protected]","displayName":"Copple","photoUrl":""}],"validSince":"1701484518","email":"[email protected]","emailVerified":false,"disabled":false}]}
Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,12 @@
11
{
2-
"version": "9.17.0",
2+
"version": "11.16.0",
33
"firestore": {
4-
"version": "1.13.1",
4+
"version": "1.15.1",
55
"path": "firestore_export",
66
"metadata_file": "firestore_export/firestore_export.overall_export_metadata"
77
},
8-
"database": {
9-
"version": "4.7.2",
10-
"path": "database_export"
11-
},
128
"auth": {
13-
"version": "9.17.0",
9+
"version": "11.16.0",
1410
"path": "auth_export"
15-
},
16-
"storage": {
17-
"version": "9.17.0",
18-
"path": "storage_export"
1911
}
2012
}
0 Bytes
Binary file not shown.
146 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.

wrappers/dockerfiles/firebase/baseline-data/storage_export/buckets.json

Lines changed: 0 additions & 7 deletions
This file was deleted.

wrappers/src/fdw/firebase_fdw/firebase_fdw.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ impl FirebaseFdw {
183183
_ => {
184184
// match for firestore documents
185185
// ref: https://firebase.google.com/docs/firestore/reference/rest/v1beta1/projects.databases.documents/listDocuments
186-
let re = Regex::new(r"^firestore/(?P<collection>[^/]+)").expect("regex is valid");
186+
let re = Regex::new(r"^firestore/(?P<collection>.+)").expect("regex is valid");
187187
if let Some(caps) = re.captures(obj) {
188188
let base_url =
189189
require_option_or("base_url", options, Self::DEFAULT_FIRESTORE_BASE_URL);

wrappers/src/fdw/firebase_fdw/tests.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,41 @@ mod tests {
9595
vec![
9696
("projects/supa/databases/(default)/documents/my-collection/bSMScXpZHMJe9ilE9Yqs",
9797
serde_json::json!({"id": {"integerValue": "1"}, "name": {"stringValue": "hello"}}))]);
98+
99+
c.update(
100+
r#"
101+
CREATE FOREIGN TABLE firebase_docs_nested (
102+
name text,
103+
fields jsonb,
104+
create_time timestamp,
105+
update_time timestamp
106+
)
107+
SERVER my_firebase_server
108+
OPTIONS (
109+
object 'firestore/my-collection/bSMScXpZHMJe9ilE9Yqs/my-collection2',
110+
base_url 'http://localhost:8080/v1/projects'
111+
)
112+
"#,
113+
None,
114+
None,
115+
)
116+
.unwrap();
117+
118+
let results = c
119+
.select("SELECT name,fields FROM firebase_docs_nested", None, None)
120+
.unwrap()
121+
.filter_map(|r| {
122+
r.get_by_name::<&str, _>("name")
123+
.unwrap()
124+
.zip(r.get_by_name::<JsonB, _>("fields").unwrap().map(|j| j.0))
125+
})
126+
.collect::<Vec<_>>();
127+
128+
assert_eq!(
129+
results,
130+
vec![
131+
("projects/supa/databases/(default)/documents/my-collection/bSMScXpZHMJe9ilE9Yqs/my-collection2/fkSWL4hNJ3lRc1ZIorPm",
132+
serde_json::json!({"foo": {"stringValue": "bar"}}))]);
98133
});
99134
}
100135
}

0 commit comments

Comments
 (0)