From 3a62a18887337a42c92961c41b865a0955def4a6 Mon Sep 17 00:00:00 2001 From: Benjamin DANIEL Date: Wed, 20 Sep 2023 16:10:18 +0200 Subject: [PATCH] cursor: Fix count function on aggregation cursor --- README.md | 4 ++- src/client.ts | 8 +++-- src/cursors/n9-aggregation-cursor.ts | 47 +++++++++++++--------------- src/models/aggregate.models.ts | 4 +-- src/mongo-utils.ts | 3 ++ test/aggregation-cursor.test.ts | 11 +++---- 6 files changed, 40 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 0630953..f375903 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Notable Changes Upgrade main steps -- `yarn remove @neo9/n9-mongo-client && yarn add @neo9/n9-mongodb-client@^1.0.0-rc.2` (this also upgrade all transitive dependencies) +- `yarn remove @neo9/n9-mongo-client && yarn add @neo9/n9-mongodb-client@^1.0.0-rc.8` (this also upgrade all transitive dependencies) - Rename usage : `find src/ -type f -exec sed -i -e 's#@neo9/n9-mongo-client#@neo9/n9-mongodb-client#g' {} +` - Remove old mongodb types : @@ -44,6 +44,8 @@ Upgrade main steps - Upgrade MongoDb used for tests to version 6.0+ - Change MongoDB types imports from `import ... from 'mongodb';` to `import ... from '@neo9/n9-mongodb-client/mongodb';` - Use Node.js version 16.20.2 or greater. +- Use new `count` function on `N9AggregationCursor` that wasn't available on `AggregationCursor` +- It's a good time to use new version of `@neo9/n9-mongodb-migration` V1 : `yarn upgrade @neo9/n9-mongodb-migration@^1.0.0-rc.0` ## To build diff --git a/src/client.ts b/src/client.ts index ef860bf..8530622 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1050,10 +1050,14 @@ export class MongoClient { ): N9AggregationCursor { if (readInOutputCollection) { const nativeCursor = this.collection.aggregate(aggregateSteps, options); - return new N9AggregationCursor(this.collection, nativeCursor); + return new N9AggregationCursor(this.collection, nativeCursor, aggregateSteps); } const nativeCursor = this.collectionSourceForAggregation.aggregate(aggregateSteps, options); - return new N9AggregationCursor(this.collectionSourceForAggregation, nativeCursor); + return new N9AggregationCursor( + this.collectionSourceForAggregation, + nativeCursor, + aggregateSteps, + ); } public aggregateWithBuilder( diff --git a/src/cursors/n9-aggregation-cursor.ts b/src/cursors/n9-aggregation-cursor.ts index 4c24a8d..6f6632b 100644 --- a/src/cursors/n9-aggregation-cursor.ts +++ b/src/cursors/n9-aggregation-cursor.ts @@ -1,30 +1,23 @@ -import { N9Error } from '@neo9/n9-node-utils'; -import * as _ from 'lodash'; -import { AggregationCursor, Collection, Filter } from 'mongodb'; +import { AggregationCursor, Collection } from 'mongodb'; +import { LodashReplacerUtils } from '../lodash-replacer.utils'; import { N9AbstractCursor } from './n9-abstract-cursor'; export class N9AggregationCursor extends N9AbstractCursor { - private _filterQuery: Filter; // can be edited with filter function - public constructor( private readonly collection: Collection, private readonly aggregationCursor: AggregationCursor, + private readonly aggregateSteps: object[], ) { super(aggregationCursor); } - /** - * Set the filterQuery used for count - * - * @param value - */ - set filterQuery(value: Filter) { - this._filterQuery = value; - } - clone(): N9AggregationCursor { - return new N9AggregationCursor(this.collection, this.aggregationCursor.clone()); + return new N9AggregationCursor( + this.collection, + this.aggregationCursor.clone(), + this.aggregateSteps, + ); } public async launch(): Promise { @@ -37,19 +30,23 @@ export class N9AggregationCursor extends N9AbstractCursor { } /** - * Get the count of documents for this cursor using the filterQuery. - * - * @see filterQuery + * Get the count of documents for this cursor. */ public async count(): Promise { - if (!this._filterQuery) { - throw new N9Error('filter-query-not-initialized', 400, { - hint: 'Set filterQuery on the N9AggregationCursor before calling count function.', - }); - } - if (_.isEmpty(this._filterQuery)) { + if (LodashReplacerUtils.IS_ARRAY_EMPTY(this.aggregateSteps)) { return await this.collection.estimatedDocumentCount(); } - return await this.collection.countDocuments(this._filterQuery); + const countResult = await this.collection + .aggregate([ + ...this.aggregateSteps, + { + $group: { + _id: null, + n: { $sum: 1 }, + }, + }, + ]) + .toArray(); + return countResult[0].n; } } diff --git a/src/models/aggregate.models.ts b/src/models/aggregate.models.ts index e6e4843..06fa881 100644 --- a/src/models/aggregate.models.ts +++ b/src/models/aggregate.models.ts @@ -403,8 +403,8 @@ export interface GraphLookupPipelineStage { /* GROUP */ export interface GroupPipelineStageValue { - _id: Expression; - [fieldName: string]: Expression; + _id: number | Expression; + [fieldName: string]: number | Expression; } export interface GroupPipelineStage { [AggregationPipelineStageOperator.GROUP]: GroupPipelineStageValue; diff --git a/src/mongo-utils.ts b/src/mongo-utils.ts index 3ffadea..c6a44f5 100644 --- a/src/mongo-utils.ts +++ b/src/mongo-utils.ts @@ -27,6 +27,9 @@ export class MongoUtils { const optionsWithDefaultValuesApplied: mongodb.MongoClientOptions = { heartbeatFrequencyMS: 3_000, + driverInfo: { + name: '@neo9/n9-mongodb-client', + }, ...options, }; diff --git a/test/aggregation-cursor.test.ts b/test/aggregation-cursor.test.ts index 77cc845..eaef63b 100644 --- a/test/aggregation-cursor.test.ts +++ b/test/aggregation-cursor.test.ts @@ -83,21 +83,18 @@ test('[Cursor] call hasNext before using in a for async', async (t: ExecutionCon test('[Cursor] Check count function', async (t: ExecutionContext) => { let cursor = t.context.mongoClient.aggregate([]); - cursor.filterQuery = {}; t.is(await cursor.count(), 5, 'cursor contains 5 items'); const filterQuery = { field1String: { $regex: /[24680]$/ } }; cursor = t.context.mongoClient.aggregateWithBuilder( t.context.mongoClient.newAggregationBuilder().match(filterQuery), ); + t.is(await cursor.count(), 2, 'cursor contains only odd => 2 elements'); - await t.throwsAsync( - async () => await cursor.count(), - { message: 'filter-query-not-initialized' }, - 'Should throw filter query not initialized error', + cursor = t.context.mongoClient.aggregateWithBuilder( + t.context.mongoClient.newAggregationBuilder().match(filterQuery).group({ _id: 1 }), ); - cursor.filterQuery = filterQuery; - t.is(await cursor.count(), 2, 'cursor contains only odd => 2 elements'); + t.is(await cursor.count(), 1, 'cursor only contains the group result'); }); test('[Cursor] Check cursor clone function', async (t: ExecutionContext) => {