Skip to content

Commit ac5118e

Browse files
committed
feat: remove valuesAs just use indexing
1 parent 4631e34 commit ac5118e

File tree

3 files changed

+56
-68
lines changed

3 files changed

+56
-68
lines changed

src/cmap/wire_protocol/on_demand/document.ts

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export class OnDemandDocument {
5858
private readonly indexFound: Record<number, boolean> = Object.create(null);
5959

6060
/** All bson elements in this document */
61-
private readonly elements: BSONElement[];
61+
private readonly elements: ReadonlyArray<BSONElement>;
6262

6363
constructor(
6464
/** BSON bytes, this document begins at offset */
@@ -97,14 +97,30 @@ export class OnDemandDocument {
9797
* @param name - a basic latin string name of a BSON element
9898
* @returns
9999
*/
100-
private getElement(name: string): CachedBSONElement | null {
100+
private getElement(name: string | number): CachedBSONElement | null {
101101
const cachedElement = this.cache[name];
102102
if (cachedElement === false) return null;
103103

104104
if (cachedElement != null) {
105105
return cachedElement;
106106
}
107107

108+
if (typeof name === 'number') {
109+
if (this.isArray) {
110+
if (name < this.elements.length) {
111+
const element = this.elements[name];
112+
const cachedElement = { element, value: undefined };
113+
this.cache[name] = cachedElement;
114+
this.indexFound[name] = true;
115+
return cachedElement;
116+
} else {
117+
return null;
118+
}
119+
} else {
120+
return null;
121+
}
122+
}
123+
108124
for (let index = 0; index < this.elements.length; index++) {
109125
const element = this.elements[index];
110126

@@ -229,16 +245,20 @@ export class OnDemandDocument {
229245
* @param required - whether or not the element is expected to exist, if true this function will throw if it is not present
230246
*/
231247
public get<const T extends keyof JSTypeOf>(
232-
name: string,
248+
name: string | number,
233249
as: T,
234250
required?: false | undefined
235251
): JSTypeOf[T] | null;
236252

237253
/** `required` will make `get` throw if name does not exist or is null/undefined */
238-
public get<const T extends keyof JSTypeOf>(name: string, as: T, required: true): JSTypeOf[T];
254+
public get<const T extends keyof JSTypeOf>(
255+
name: string | number,
256+
as: T,
257+
required: true
258+
): JSTypeOf[T];
239259

240260
public get<const T extends keyof JSTypeOf>(
241-
name: string,
261+
name: string | number,
242262
as: T,
243263
required?: boolean
244264
): JSTypeOf[T] | null {
@@ -315,22 +335,4 @@ export class OnDemandDocument {
315335
const size = getInt32LE(this.bson, this.offset);
316336
return this.bson.subarray(this.offset, this.offset + size);
317337
}
318-
319-
/**
320-
* Iterates through the elements of a document reviving them using the `as` BSONType.
321-
*
322-
* @param as - The type to revive all elements as
323-
*/
324-
public *valuesAs<const T extends keyof JSTypeOf>(as: T): Generator<JSTypeOf[T], void, void> {
325-
if (!this.isArray) {
326-
throw new BSONError('Unexpected conversion of non-array value to array');
327-
}
328-
let counter = 0;
329-
for (const element of this.elements) {
330-
const value = this.toJSValue<T>(element, as);
331-
this.cache[counter] = { element, value };
332-
yield value;
333-
counter += 1;
334-
}
335-
}
336338
}

src/cmap/wire_protocol/responses.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -147,11 +147,10 @@ export class CursorResponse extends MongoDBResponse {
147147
public batchSize = 0;
148148

149149
private batch: OnDemandDocument | null = null;
150-
private values: Generator<OnDemandDocument, void, void> | null = null;
151150
private iterated = 0;
152151

153-
constructor(b: Uint8Array, o?: number, a?: boolean) {
154-
super(b, o, a);
152+
constructor(bytes: Uint8Array, offset?: number, isArray?: boolean) {
153+
super(bytes, offset, isArray);
155154

156155
if (this.isError) return;
157156

@@ -175,20 +174,26 @@ export class CursorResponse extends MongoDBResponse {
175174
}
176175

177176
shift(options?: BSONSerializeOptions): any {
177+
if (this.iterated >= this.batchSize) {
178+
return null;
179+
}
180+
181+
const result = this.batch?.get(this.iterated, BSONType.object, true) ?? null;
178182
this.iterated += 1;
179-
this.values ??= this.batch?.valuesAs(BSONType.object) ?? null;
180-
const result = this.values?.next();
181-
if (!result || result.done) return null;
183+
184+
if (result == null) {
185+
throw new MongoUnexpectedServerResponseError('Cursor batch contains null values');
186+
}
187+
182188
if (options?.raw) {
183-
return result.value.toBytes();
189+
return result.toBytes();
184190
} else {
185-
return result.value.toObject(options);
191+
return result.toObject(options);
186192
}
187193
}
188194

189195
clear() {
190196
this.iterated = this.batchSize;
191-
this.values?.return();
192197
}
193198

194199
pushMany() {

test/unit/cmap/wire_protocol/on_demand/document.test.ts

Lines changed: 17 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ describe('class OnDemandDocument', () => {
7373

7474
context('get()', () => {
7575
let document: OnDemandDocument;
76+
let array: OnDemandDocument;
7677
const input = {
7778
int: 1,
7879
double: 1.2,
@@ -86,12 +87,27 @@ describe('class OnDemandDocument', () => {
8687
date: new Date(0),
8788
object: { a: 1 },
8889
array: [1, 2],
89-
unsupportedType: /abc/
90+
unsupportedType: /abc/,
91+
[233]: 3
9092
};
9193

9294
beforeEach(async function () {
9395
const bytes = BSON.serialize(input);
9496
document = new OnDemandDocument(bytes);
97+
array = new OnDemandDocument(
98+
BSON.serialize(Object.fromEntries(Object.values(input).entries())),
99+
0,
100+
true
101+
);
102+
});
103+
104+
it('supports access by number for arrays', () => {
105+
expect(array.get(1, BSONType.int)).to.equal(1);
106+
});
107+
108+
it('does not support access by number for objects', () => {
109+
expect(document.get(233, BSONType.int)).to.be.null;
110+
expect(document.get('233', BSONType.int)).to.equal(3);
95111
});
96112

97113
it('returns null if the element does not exist', () => {
@@ -277,39 +293,4 @@ describe('class OnDemandDocument', () => {
277293
expect(document.getNumber('boolTrue')).to.equal(1);
278294
});
279295
});
280-
281-
context('*valuesAs()', () => {
282-
let array: OnDemandDocument;
283-
beforeEach(async function () {
284-
const bytes = BSON.serialize(
285-
Object.fromEntries(Array.from({ length: 10 }, () => 1).entries())
286-
);
287-
array = new OnDemandDocument(bytes, 0, true);
288-
});
289-
290-
it('throws if document is not an array', () => {
291-
const bytes = BSON.serialize(
292-
Object.fromEntries(Array.from({ length: 10 }, () => 1).entries())
293-
);
294-
array = new OnDemandDocument(bytes, 0, false);
295-
expect(() => array.valuesAs(BSONType.int).next()).to.throw();
296-
});
297-
298-
it('returns a generator that yields values matching the as BSONType parameter', () => {
299-
let didRun = false;
300-
for (const item of array.valuesAs(BSONType.int)) {
301-
didRun = true;
302-
expect(item).to.equal(1);
303-
}
304-
expect(didRun).to.be.true;
305-
});
306-
307-
it('caches the results of array', () => {
308-
const generator = array.valuesAs(BSONType.int);
309-
generator.next();
310-
generator.next();
311-
expect(array).to.have.nested.property('cache.0.value', 1);
312-
expect(array).to.have.nested.property('cache.1.value', 1);
313-
});
314-
});
315296
});

0 commit comments

Comments
 (0)