Skip to content

Commit 72df400

Browse files
committed
feat(cbjs): new lookupIn().values() method
1 parent 5ff6a98 commit 72df400

File tree

10 files changed

+2152
-5494
lines changed

10 files changed

+2152
-5494
lines changed

packages/cbjs/src/clusterTypes/kv/lookup/lookupIn.types.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,22 @@ export type LookupInResultEntries<Results, ThrowOnSpecError extends boolean> =
9494
Array<LookupInResultEntry<ArrayElement<Results>, null> | LookupInResultEntry<undefined, Error>>
9595
;
9696

97+
export type ValuesFromSpecResults<Method, SpecResults, ThrowOnSpecError> =
98+
[ThrowOnSpecError] extends [true] ?
99+
Method extends 'lookupInAllReplicas' ?
100+
SpecResults[] :
101+
SpecResults :
102+
Method extends 'lookupInAllReplicas' ?
103+
ArrayElementOrUndefined<SpecResults>[] :
104+
ArrayElementOrUndefined<SpecResults>
105+
;
106+
107+
type ArrayElementOrUndefined<Arr> =
108+
Arr extends readonly [infer Head extends unknown, ...infer Rest] ?
109+
[Head | undefined, ...ArrayElementOrUndefined<Rest>] :
110+
[]
111+
;
112+
97113
/**
98114
* Lookup paths - Non-distributive.
99115
*/

packages/cbjs/src/collection.spec-d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ import { LookupInSpec, MutateInSpec } from './sdspecs.js';
3333
import { CouchbaseMap, CouchbaseSet } from './services/kv/dataStructures/index.js';
3434
import { ChainableLookupIn } from './services/kv/lookupIn/ChainableLookupIn.js';
3535
import { ChainableMutateIn } from './services/kv/mutateIn/ChainableMutateIn.js';
36-
import { StreamableReplicasPromise } from './streamablepromises.js';
3736

3837
type TestDoc = {
3938
title: string;

packages/cbjs/src/crudoptypes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ export class LookupInResult<
278278
* @category Key-Value
279279
*/
280280
export class LookupInReplicaResult<
281-
Results = any[],
281+
Results extends ReadonlyArray<any> = any[],
282282
ThrowOnSpecError extends boolean = false,
283283
> {
284284
/**
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/*
2+
* Copyright (c) 2023-Present Jonathan MASSUCHETTI <jonathan.massuchetti@dappit.fr>.
3+
* Copyright (c) 2013-Present Couchbase Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
import { describe, expectTypeOf, it } from 'vitest';
18+
19+
import { DocDef } from '../../../clusterTypes/index.js';
20+
import { Collection } from '../../../collection.js';
21+
import { ChainableLookupIn } from './ChainableLookupIn.js';
22+
23+
describe('ChainableLookupIn', function () {
24+
type BookDocDef = DocDef<
25+
`book::${string}`,
26+
{ title: string; metadata?: { tags: string[] } }
27+
>;
28+
type UserClusterTypes = {
29+
store: {
30+
library: {
31+
books: [BookDocDef];
32+
};
33+
};
34+
};
35+
36+
const collection: Collection<UserClusterTypes, 'store', 'library', 'books'> =
37+
true as any;
38+
39+
it('should pick the correct DocDef based on the matching key', () => {
40+
expectTypeOf(
41+
ChainableLookupIn.for(collection, 'lookupIn', 'book::001', {})
42+
).toEqualTypeOf<
43+
ChainableLookupIn<
44+
Collection<UserClusterTypes, 'store', 'library', 'books'>,
45+
'lookupIn',
46+
`book::001`,
47+
[],
48+
boolean,
49+
BookDocDef
50+
>
51+
>();
52+
});
53+
54+
it('should match string template path', () => {
55+
expectTypeOf(
56+
ChainableLookupIn.for(collection, 'lookupIn', 'book::001', {})
57+
).toEqualTypeOf<
58+
ChainableLookupIn<
59+
Collection<UserClusterTypes, 'store', 'library', 'books'>,
60+
'lookupIn',
61+
`book::001`,
62+
[],
63+
boolean,
64+
BookDocDef
65+
>
66+
>();
67+
});
68+
69+
it('should have .values() return type to match the specs results', async () => {
70+
const resultLookupIn = await ChainableLookupIn.for(
71+
collection,
72+
'lookupIn',
73+
'book::001',
74+
{}
75+
)
76+
.get('title')
77+
.count('metadata.tags')
78+
.exists('title')
79+
.values();
80+
81+
expectTypeOf(resultLookupIn).toEqualTypeOf<
82+
[string | undefined, number | undefined, boolean | undefined]
83+
>();
84+
85+
const resultLookupInTOSE = await ChainableLookupIn.for(
86+
collection,
87+
'lookupIn',
88+
'book::001',
89+
{
90+
throwOnSpecError: true,
91+
}
92+
)
93+
.get('title')
94+
.count('metadata.tags')
95+
.exists('title')
96+
.values();
97+
98+
expectTypeOf(resultLookupInTOSE).toEqualTypeOf<[string, number, boolean]>();
99+
100+
const resultLookupInAnyReplica = await ChainableLookupIn.for(
101+
collection,
102+
'lookupInAnyReplica',
103+
'book::001',
104+
{}
105+
)
106+
.get('title')
107+
.count('metadata.tags')
108+
.exists('title')
109+
.values();
110+
111+
expectTypeOf(resultLookupInAnyReplica).toEqualTypeOf<
112+
[string | undefined, number | undefined, boolean | undefined]
113+
>();
114+
115+
const resultLookupInAnyReplicaTOSE = await ChainableLookupIn.for(
116+
collection,
117+
'lookupInAnyReplica',
118+
'book::001',
119+
{
120+
throwOnSpecError: true,
121+
}
122+
)
123+
.get('title')
124+
.count('metadata.tags')
125+
.exists('title')
126+
.values();
127+
128+
expectTypeOf(resultLookupInAnyReplicaTOSE).toEqualTypeOf<[string, number, boolean]>();
129+
130+
const resultLookupInAllReplicas = await ChainableLookupIn.for(
131+
collection,
132+
'lookupInAllReplicas',
133+
'book::001',
134+
{}
135+
)
136+
.get('title')
137+
.count('metadata.tags')
138+
.exists('title')
139+
.values();
140+
141+
expectTypeOf(resultLookupInAllReplicas).toEqualTypeOf<
142+
[string | undefined, number | undefined, boolean | undefined][]
143+
>();
144+
145+
const resultLookupInAllReplicasTOSE = await ChainableLookupIn.for(
146+
collection,
147+
'lookupInAllReplicas',
148+
'book::001',
149+
{ throwOnSpecError: true }
150+
)
151+
.get('title')
152+
.count('metadata.tags')
153+
.exists('title')
154+
.values();
155+
156+
expectTypeOf(resultLookupInAllReplicasTOSE).toEqualTypeOf<
157+
[string, number, boolean][]
158+
>();
159+
});
160+
});

packages/cbjs/src/services/kv/lookupIn/ChainableLookupIn.spec.ts

Lines changed: 2 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,9 @@
1414
* See the License for the specific language governing permissions and
1515
* limitations under the License.
1616
*/
17-
import { describe, expectTypeOf, it } from 'vitest';
17+
import { describe, it } from 'vitest';
1818

19-
import { AnyCollection, DocDef } from '../../../clusterTypes/index.js';
20-
import { Collection } from '../../../collection.js';
21-
import { connect } from '../../../couchbase.js';
19+
import { AnyCollection } from '../../../clusterTypes/index.js';
2220
import { LookupInSpec } from '../../../sdspecs.js';
2321
import { ChainableLookupIn } from './ChainableLookupIn.js';
2422

@@ -56,58 +54,4 @@ describe('ChainableLookupIn', function () {
5654
LookupInSpec.exists('title'),
5755
]);
5856
});
59-
60-
it.skip('should pick the correct DocDef based on the matching key', ({ expect }) => {
61-
type BookDocDef = DocDef<`book::${string}`, { title: string }>;
62-
type UserClusterTypes = {
63-
store: {
64-
library: {
65-
books: [BookDocDef];
66-
};
67-
};
68-
};
69-
70-
const collection: Collection<UserClusterTypes, 'store', 'library', 'books'> =
71-
true as any;
72-
73-
expectTypeOf(
74-
ChainableLookupIn.for(collection, 'lookupIn', 'book::001', {})
75-
).toEqualTypeOf<
76-
ChainableLookupIn<
77-
Collection<UserClusterTypes, 'store', 'library', 'books'>,
78-
'lookupIn',
79-
`book::001`,
80-
[],
81-
boolean,
82-
BookDocDef
83-
>
84-
>();
85-
});
86-
87-
it.skip('should match string template path', ({ expect }) => {
88-
type BookDocDef = DocDef<`book::${string}`, { title: string }>;
89-
type UserClusterTypes = {
90-
store: {
91-
library: {
92-
books: [BookDocDef];
93-
};
94-
};
95-
};
96-
97-
const collection: Collection<UserClusterTypes, 'store', 'library', 'books'> =
98-
true as any;
99-
100-
expectTypeOf(
101-
ChainableLookupIn.for(collection, 'lookupIn', 'book::001', {})
102-
).toEqualTypeOf<
103-
ChainableLookupIn<
104-
Collection<UserClusterTypes, 'store', 'library', 'books'>,
105-
'lookupIn',
106-
`book::001`,
107-
[],
108-
boolean,
109-
BookDocDef
110-
>
111-
>();
112-
});
11357
});

packages/cbjs/src/services/kv/lookupIn/ChainableLookupIn.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@ import {
2121
ExtractCollectionJsonDocKey,
2222
} from '../../../clusterTypes/clusterTypes.js';
2323
import type { AnyCollection } from '../../../clusterTypes/index.js';
24-
import type {
24+
import {
25+
ExtractValuesFromLookupInResultEntries,
26+
LookupInResultEntries,
2527
LookupInSpecResult,
2628
MakeLookupInSpec,
29+
ValuesFromSpecResults,
2730
} from '../../../clusterTypes/kv/lookup/lookupIn.types.js';
2831
import type {
2932
LookupInCountPath,
@@ -243,4 +246,26 @@ export class ChainableLookupIn<
243246
getSpecs(): LookupInSpec[] {
244247
return this.specs;
245248
}
249+
250+
/**
251+
* Return an array containing all the values.
252+
*
253+
* @example
254+
* ```ts
255+
* const [title, tags] = await cb.collection('acme')
256+
* .get('title')
257+
* .get('tags')
258+
* .values();
259+
* ```
260+
*/
261+
async values(): Promise<ValuesFromSpecResults<Method, SpecResults, ThrowOnSpecError>> {
262+
if (this.method === 'lookupInAllReplicas') {
263+
const result = (await this.execute()) as { content: { value: unknown }[] }[];
264+
return result.map((r) => r.content.map((e) => e.value)) as never;
265+
}
266+
267+
const { content } = (await this.execute()) as { content: { value: unknown }[] };
268+
269+
return content.map((e) => e.value) as never;
270+
}
246271
}

0 commit comments

Comments
 (0)