Skip to content

Commit 5f687ae

Browse files
committed
Add count method (#121)
Co-authored By: Michel Parpaillon <[email protected]>
1 parent da8cd99 commit 5f687ae

File tree

14 files changed

+999
-968
lines changed

14 files changed

+999
-968
lines changed

Diff for: .editorconfig

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
root = true
22

33
[*]
4+
indent_size = 2
45
end_of_line = lf

Diff for: .vscode/extensions.json

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"recommendations": [
3+
"esbenp.prettier-vscode"
4+
]
5+
}

Diff for: .vscode/settings.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@
2020
"titleBar.inactiveForeground": "#e7e7e799",
2121
"commandCenter.border": "#e7e7e799"
2222
},
23-
"peacock.remoteColor": "#136881"
23+
"peacock.remoteColor": "#136881",
24+
"editor.formatOnSave": true,
2425
}

Diff for: package-lock.json

+826-955
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
"@types/webpack-env": "^1.18.0",
5151
"babel-loader": "^8.2.5",
5252
"babel-plugin-add-import-extension": "^1.6.0",
53-
"firebase": "^9.7.0",
53+
"firebase": "^9.22.1",
5454
"firebase-admin": "^11.5.0",
5555
"firebase-tools": "^11.23.1",
5656
"jest": "^28.1.3",

Diff for: src/adapter/admin/core.mjs

+15-1
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,11 @@ export class Collection {
186186
})
187187
}
188188

189+
async count() {
190+
const snap = await this.adapter().collection().count().get()
191+
return snap.data().count
192+
}
193+
189194
adapter() {
190195
return {
191196
collection: () => this.firebaseCollection(),
@@ -542,7 +547,7 @@ export function query(adapter, queries) {
542547
firestoreQuery = firestoreQuery[method](...values)
543548
})
544549

545-
return new SubscriptionPromise({
550+
const sp = new SubscriptionPromise({
546551
request: request({
547552
kind: 'query',
548553
...adapter.request(),
@@ -606,6 +611,15 @@ export function query(adapter, queries) {
606611
onResult(docs, meta)
607612
}, onError)
608613
})
614+
615+
Object.assign(sp, {
616+
count: async () => {
617+
const snap = await firestoreQuery.count().get()
618+
return snap.data().count
619+
}
620+
})
621+
622+
return sp
609623
}
610624

611625
export function queryHelpers(mode = 'helpers', acc) {

Diff for: src/adapter/admin/groups.mjs

+5
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ class Group {
3636
return all(this.adapter())
3737
}
3838

39+
async count() {
40+
const snap = await this.adapter().collection().count().get()
41+
return snap.data().count
42+
}
43+
3944
adapter() {
4045
return {
4146
collection: () => this.firebaseCollection(),

Diff for: src/adapter/web/core.mjs

+18-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import {
2+
DocumentReference,
3+
Timestamp,
24
addDoc,
35
arrayRemove,
46
arrayUnion,
@@ -7,9 +9,9 @@ import {
79
deleteField,
810
doc,
911
documentId,
10-
DocumentReference,
1112
endAt,
1213
endBefore,
14+
getCountFromServer,
1315
getDoc,
1416
getDocs,
1517
getFirestore,
@@ -22,7 +24,6 @@ import {
2224
setDoc,
2325
startAfter,
2426
startAt,
25-
Timestamp,
2627
updateDoc,
2728
where
2829
} from 'firebase/firestore'
@@ -199,6 +200,11 @@ export class Collection {
199200
})
200201
}
201202

203+
async count() {
204+
const snap = await getCountFromServer(this.firebaseCollection())
205+
return snap.data().count
206+
}
207+
202208
adapter() {
203209
return {
204210
db: () => this.firebaseDB,
@@ -574,7 +580,7 @@ export function _query(adapter, queries) {
574580
const firebaseQuery = () =>
575581
query(adapter.collection(), ...firebaseQueries, ...firebaseCursors)
576582

577-
return new SubscriptionPromise({
583+
const sp = new SubscriptionPromise({
578584
request: request({
579585
kind: 'query',
580586
...adapter.request(),
@@ -651,6 +657,15 @@ export function _query(adapter, queries) {
651657
)
652658
}
653659
})
660+
661+
Object.assign(sp, {
662+
count: async () => {
663+
const snap = await getCountFromServer(firebaseQuery())
664+
return snap.data().count
665+
}
666+
})
667+
668+
return sp
654669
}
655670

656671
export function queryHelpers(mode = 'helpers', acc) {

Diff for: src/adapter/web/groups.mjs

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
import { collectionGroup, getFirestore } from 'firebase/firestore'
2-
import { all, pathToDoc, _query, queryHelpers, wrapData } from './core.mjs'
1+
import {
2+
collectionGroup,
3+
getCountFromServer,
4+
getFirestore
5+
} from 'firebase/firestore'
6+
import { _query, all, pathToDoc, queryHelpers, wrapData } from './core.mjs'
37

48
export const groups = (rootDB) => {
59
const groups = {}
@@ -37,6 +41,11 @@ class Group {
3741
return all(this.adapter())
3842
}
3943

44+
async count() {
45+
const snap = await getCountFromServer(this.firebaseCollection())
46+
return snap.data().count
47+
}
48+
4049
adapter() {
4150
return {
4251
db: () => this.firebaseDB,

Diff for: src/groups/tysts.ts

+5
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,11 @@ async function tysts() {
888888
await groups(db4).shposts.all()
889889
// @ts-expect-error
890890
await groups(db4).shmosts.all()
891+
892+
// Count
893+
894+
const commentsCount = await groups(db2).comments.count()
895+
commentsCount.toFixed()
891896
}
892897

893898
type Assert<Type1, _Type2 extends Type1> = true

Diff for: src/tests/count.ts

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { groups, schema } from '..'
2+
3+
describe('count', () => {
4+
interface Novel {
5+
title: string
6+
}
7+
8+
interface Manga {
9+
title: string
10+
volume: number
11+
}
12+
13+
const db = schema(($) => ({
14+
novels: $.collection<Novel>().sub({
15+
feedbacks: $.collection<any>()
16+
}),
17+
mangas: $.collection<Manga>().sub({
18+
feedbacks: $.collection<any>()
19+
})
20+
}))
21+
22+
beforeEach(async () => {
23+
await Promise.all([
24+
db.novels.set(db.novels.id('sapiens'), {
25+
title: 'Sapiens'
26+
}),
27+
db.novels.set(db.novels.id('22laws'), {
28+
title: 'The 22 Immutable Laws of Marketing'
29+
}),
30+
db.mangas.set(db.mangas.id('naruto-1'), {
31+
title: 'Naruto',
32+
volume: 1
33+
}),
34+
db.mangas.set(db.mangas.id('naruto-2'), {
35+
title: 'Naruto',
36+
volume: 2
37+
})
38+
])
39+
40+
const sapiens = db.novels(db.novels.id('sapiens'))
41+
const naruto1 = db.mangas(db.mangas.id('naruto-1'))
42+
const naruto2 = db.mangas(db.mangas.id('naruto-2'))
43+
44+
await Promise.all([
45+
sapiens.feedbacks.set(sapiens.feedbacks.id('1'), { text: 'Great novel' }),
46+
naruto1.feedbacks.set(naruto1.feedbacks.id('2'), { text: 'Great manga' }),
47+
naruto2.feedbacks.set(naruto2.feedbacks.id('3'), { text: 'Great manga' }),
48+
naruto2.feedbacks.set(naruto2.feedbacks.id('4'), {
49+
text: 'Average. First one was better'
50+
})
51+
])
52+
})
53+
54+
it('counts documents in a collection', async () => {
55+
const count = await db.novels.count()
56+
expect(count).toBe(2)
57+
})
58+
59+
it('counts documents in a query', async () => {
60+
const count = await db.mangas
61+
.query(($) => $.field('volume').equal(2))
62+
.count()
63+
expect(count).toBe(1)
64+
})
65+
66+
it('counts documents in a group collection', async () => {
67+
const count = await groups(db).feedbacks.count()
68+
expect(count).toBe(4)
69+
})
70+
})

Diff for: src/types/core.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,8 @@ export namespace TypesaurusCore {
906906
>
907907

908908
query: Query.Function<Def>
909+
910+
count(): Promise<number>
909911
}
910912

911913
/**

Diff for: src/types/query.d.ts

+13-5
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,7 @@ export namespace TypesaurusQuery {
1919
// it's impossible towrap the query into a check because the check
2020
// will be invalid inside the function.
2121
undefined
22-
: Core.SubscriptionPromise<
23-
Core.QueryRequest,
24-
Core.Doc<Def, Props>[],
25-
Core.SubscriptionListMeta<Def, Props>
26-
>
22+
: SubscriptionPromise<Def, Environment, Props>
2723
: never
2824

2925
build<
@@ -34,6 +30,18 @@ export namespace TypesaurusQuery {
3430
): Builder<Def, Props>
3531
}
3632

33+
export interface SubscriptionPromise<
34+
Def extends Core.DocDef,
35+
Environment extends Core.RuntimeEnvironment,
36+
Props extends Core.DocProps & { environment: Environment }
37+
> extends Core.SubscriptionPromise<
38+
Core.QueryRequest,
39+
Core.Doc<Def, Props>[],
40+
Core.SubscriptionListMeta<Def, Props>
41+
> {
42+
count(): Promise<number>
43+
}
44+
3745
export type Data<Model extends Core.ModelType> =
3846
| Query<Model>
3947
| Array<Query<Model> | Utils.Falsy>

Diff for: src/tysts.ts

+25
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,23 @@ async function doc() {
272272
if (content) db.content.doc(contentId, content.data)
273273
}
274274

275+
async function collection() {
276+
// Count
277+
278+
const docsCount = await db.users.count()
279+
docsCount.toFixed()
280+
281+
const nestedDB = schema(($) => ({
282+
users: $.collection<User>().sub({
283+
settings: $.collection<{}>()
284+
})
285+
}))
286+
const nestedDocsCount = await nestedDB
287+
.users(nestedDB.users.id('whatever'))
288+
.settings.count()
289+
nestedDocsCount.toFixed()
290+
}
291+
275292
async function get() {
276293
const user = await db.users.get(db.users.id('sasha'))
277294
if (!user) return
@@ -768,6 +785,14 @@ async function query() {
768785
'',
769786
0
770787
])
788+
789+
// Count
790+
791+
const sashasCount = await db.users
792+
.query(($) => $.field('name').equal('Sasha'))
793+
.count()
794+
795+
sashasCount.toFixed()
771796
}
772797

773798
async function add() {

0 commit comments

Comments
 (0)