Skip to content

Commit

Permalink
refactor!: format sort in cursor and in sort builder (#2573)
Browse files Browse the repository at this point in the history
* force from master

* multi-key test

* fix lint

* get rid of class / static

* neals simpler deep to object

* removed all reducers

* linter issue

* reduce document import
  • Loading branch information
Thomas Reggi authored Oct 19, 2020
1 parent 2704ce8 commit 8aad134
Show file tree
Hide file tree
Showing 12 changed files with 251 additions and 177 deletions.
3 changes: 2 additions & 1 deletion src/collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import {
EstimatedDocumentCountOperation,
EstimatedDocumentCountOptions
} from './operations/estimated_document_count';
import { FindOperation, FindOptions, Sort } from './operations/find';
import { FindOperation, FindOptions } from './operations/find';
import { FindOneOperation } from './operations/find_one';
import {
FindAndModifyOperation,
Expand Down Expand Up @@ -86,6 +86,7 @@ import type { PkFactory } from './mongo_client';
import type { Topology } from './sdam/topology';
import type { Logger, LoggerOptions } from './logger';
import type { OperationParent } from './operations/command';
import type { Sort } from './sort';

/** @public */
export interface Collection {
Expand Down
2 changes: 1 addition & 1 deletion src/cursor/aggregation_cursor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { MongoError } from '../error';
import { Cursor, CursorOptions, CursorState } from './cursor';
import type { AggregateOperation, AggregateOptions } from '../operations/aggregate';
import type { Document } from '../bson';
import type { Sort } from '../operations/find';
import type { Sort } from '../sort';
import type { Topology } from '../sdam/topology';

/** @public */
Expand Down
58 changes: 9 additions & 49 deletions src/cursor/cursor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,14 @@ import { Logger } from '../logger';
import { executeOperation } from '../operations/execute_operation';
import { CountOperation, CountOptions } from '../operations/count';
import { ReadPreference, ReadPreferenceLike } from '../read_preference';
import {
Callback,
emitDeprecatedOptionWarning,
formattedOrderClause,
maybePromise,
MongoDBNamespace
} from '../utils';

import { Callback, emitDeprecatedOptionWarning, maybePromise, MongoDBNamespace } from '../utils';
import { Sort, SortDirection, formatSort } from '../sort';
import type { OperationTime, ResumeToken } from '../change_stream';
import type { CloseOptions } from '../cmap/connection_pool';
import type { CollationOptions } from '../cmap/wire_protocol/write_command';
import type { Hint, OperationBase } from '../operations/operation';
import type { Topology } from '../sdam/topology';
import type { CommandOperationOptions } from '../operations/command';
import type { Sort, SortDirection } from '../operations/find';
import type { Hint, OperationBase } from '../operations/operation';
import type { ReadConcern } from '../read_concern';
import type { Server } from '../sdam/server';
import type { ClientSession } from '../sessions';
Expand Down Expand Up @@ -99,6 +92,7 @@ export interface CursorOptions extends CommandOperationOptions {
topology?: Topology;
/** Session to use for the operation */
numberOfRetries?: number;
sort?: Sort;
}

/** @public */
Expand Down Expand Up @@ -399,6 +393,10 @@ export class Cursor<
this.addCursorFlag('noCursorTimeout', true);
}

if (this.options.sort) {
this.cmd.sort = formatSort(this.options.sort);
}

// Set the batch size
this._batchSize = batchSize;
}
Expand Down Expand Up @@ -688,14 +686,6 @@ export class Cursor<
return;
}

if (this.s.state === CursorState.INIT && this.cmd.sort) {
try {
this.cmd.sort = formattedOrderClause(this.cmd.sort);
} catch (err) {
return cb(err);
}
}

nextFunction(this, (err, doc) => {
if (err) return cb(err);
this.s.state = CursorState.OPEN;
Expand Down Expand Up @@ -940,37 +930,7 @@ export class Cursor<
throw new MongoError('Cursor is closed');
}

let order = sort;

// We have an array of arrays, we need to preserve the order of the sort
// so we will us a Map
if (Array.isArray(order) && Array.isArray(order[0])) {
this.cmd.sort = new Map<string, unknown>(
(order as [string, SortDirection][]).map(([key, dir]) => {
if (dir === 'asc') {
return [key, 1];
} else if (dir === 'desc') {
return [key, -1];
} else if (dir === 1 || dir === -1 || dir.$meta) {
return [key, dir];
} else {
throw new MongoError(
"Illegal sort clause, must be of the form [['field1', '(ascending|descending)'], ['field2', '(ascending|descending)']]"
);
}

return [key, null];
})
);

return this;
}

if (direction != null) {
order = [[sort as string, direction]];
}

this.cmd.sort = order;
this.cmd.sort = formatSort(sort, direction);
return this;
}

Expand Down
3 changes: 2 additions & 1 deletion src/gridfs-stream/download.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Readable } from 'stream';
import type { AnyError } from '../error';
import type { Document } from '../bson';
import type { FindOptions, Sort } from '../operations/find';
import type { FindOptions } from '../operations/find';
import type { Sort } from '../sort';
import type { Cursor } from './../cursor/cursor';
import type { Callback } from '../utils';
import type { Collection } from '../collection';
Expand Down
3 changes: 2 additions & 1 deletion src/gridfs-stream/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import type { Db } from '../db';
import type { ReadPreference } from '../read_preference';
import type { Collection } from '../collection';
import type { Cursor } from './../cursor/cursor';
import type { FindOptions, Sort } from './../operations/find';
import type { FindOptions } from './../operations/find';
import type { Sort } from '../sort';
import type { Logger } from '../logger';

const DEFAULT_GRIDFS_BUCKET_OPTIONS: {
Expand Down
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,8 @@ export type { DistinctOptions } from './operations/distinct';
export type { DropCollectionOptions, DropDatabaseOptions } from './operations/drop';
export type { EstimatedDocumentCountOptions } from './operations/estimated_document_count';
export type { EvalOptions } from './operations/eval';
export type { FindOptions, Sort, SortDirection } from './operations/find';
export type { FindOptions } from './operations/find';
export type { Sort, SortDirection } from './sort';
export type { FindAndModifyOptions } from './operations/find_and_modify';
export type {
IndexSpecification,
Expand Down
19 changes: 3 additions & 16 deletions src/operations/find.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,14 @@
import { Aspect, defineAspects, Hint } from './operation';
import { ReadPreference } from '../read_preference';
import {
maxWireVersion,
MongoDBNamespace,
Callback,
formattedOrderClause,
normalizeHintField
} from '../utils';
import { maxWireVersion, MongoDBNamespace, Callback, normalizeHintField } from '../utils';
import { MongoError } from '../error';
import type { Document } from '../bson';
import type { Server } from '../sdam/server';
import type { Collection } from '../collection';
import type { CollationOptions } from '../cmap/wire_protocol/write_command';
import type { QueryOptions } from '../cmap/wire_protocol/query';
import { CommandOperation, CommandOperationOptions } from './command';

/** @public */
export type SortDirection = 1 | -1 | 'asc' | 'desc' | { $meta: string };
/** @public */
export type Sort =
| { [key: string]: SortDirection }
| [string, SortDirection][]
| [string, SortDirection];
import { Sort, formatSort } from '../sort';

/** @public */
export interface FindOptions extends QueryOptions, CommandOperationOptions {
Expand Down Expand Up @@ -138,7 +125,7 @@ export class FindOperation extends CommandOperation<FindOptions, Document> {
const findCommand: Document = Object.assign({}, this.cmd);

if (options.sort) {
findCommand.sort = formattedOrderClause(options.sort);
findCommand.sort = formatSort(options.sort);
}

if (options.projection) {
Expand Down
5 changes: 2 additions & 3 deletions src/operations/find_and_modify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
applyRetryableWrites,
decorateWithCollation,
applyWriteConcern,
formattedOrderClause,
hasAtomicOperators,
Callback
} from '../utils';
Expand All @@ -14,7 +13,7 @@ import { defineAspects, Aspect } from './operation';
import type { Document } from '../bson';
import type { Server } from '../sdam/server';
import type { Collection } from '../collection';
import type { Sort } from './find';
import { Sort, formatSort } from '../sort';

/** @public */
export interface FindAndModifyOptions extends CommandOperationOptions {
Expand Down Expand Up @@ -69,7 +68,7 @@ export class FindAndModifyOperation extends CommandOperation<FindAndModifyOption
execute(server: Server, callback: Callback<Document>): void {
const coll = this.collection;
const query = this.query;
const sort = formattedOrderClause(this.sort);
const sort = formatSort(this.sort);
const doc = this.doc;
let options = this.options;

Expand Down
2 changes: 1 addition & 1 deletion src/operations/map_reduce.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { ReadPreference, ReadPreferenceMode } from '../read_preference';
import { CommandOperation, CommandOperationOptions } from './command';
import type { Server } from '../sdam/server';
import type { Collection } from '../collection';
import type { Sort } from './find';
import type { Sort } from '../sort';
import { MongoError } from '../error';
import type { ObjectId } from '../bson';

Expand Down
114 changes: 114 additions & 0 deletions src/sort.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/** @public */
export type SortDirection =
| 1
| -1
| 'asc'
| 'desc'
| 'ascending'
| 'descending'
| { $meta: string };

/** @public */
export type Sort =
| string
| string[]
| { [key: string]: SortDirection }
| [string, SortDirection][]
| [string, SortDirection];

/** Below stricter types were created for sort that correspond with type that the cmd takes */

/** @internal */
type SortDirectionForCmd = 1 | -1 | { $meta: string };

/** @internal */
type SortForCmd = { [key: string]: SortDirectionForCmd };

/** @internal */
function prepareDirection(direction: any = 1): SortDirectionForCmd {
const value = ('' + direction).toLowerCase();
if (isMeta(direction)) return direction;
switch (value) {
case 'ascending':
case 'asc':
case '1':
return 1;
case 'descending':
case 'desc':
case '-1':
return -1;
default:
throw new Error(`Invalid sort direction: ${JSON.stringify(direction)}`);
}
}

/** @internal */
function isMeta(t: SortDirection): t is { $meta: string } {
return typeof t === 'object' && t !== null && '$meta' in t && typeof t.$meta === 'string';
}

/** @internal */
function isPair(t: Sort): t is [string, SortDirection] {
if (Array.isArray(t) && t.length === 2) {
try {
prepareDirection(t[1]);
return true;
} catch (e) {
return false;
}
}
return false;
}

/** @internal */
function pairToObject(v: [string, SortDirection]): SortForCmd {
return { [v[0]]: prepareDirection(v[1]) };
}

/** @internal */
function isDeep(t: Sort): t is [string, SortDirection][] {
return Array.isArray(t) && Array.isArray(t[0]);
}

/** @internal */
function deepToObject(t: [string, SortDirection][]): SortForCmd {
const sortObject: SortForCmd = {};
for (const [name, value] of t) {
sortObject[name] = prepareDirection(value);
}
return sortObject;
}

/** @internal */
function stringsToObject(t: string[]): SortForCmd {
const sortObject: SortForCmd = {};
for (const key of t) {
sortObject[key] = 1;
}
return sortObject;
}

/** @internal */
function objectToObject(t: { [key: string]: SortDirection }): SortForCmd {
const sortObject: SortForCmd = {};
for (const key in t) {
sortObject[key] = prepareDirection(t[key]);
}
return sortObject;
}

/** converts a Sort type into a type that is valid for the server (SortForCmd) */
export function formatSort(
sort: Sort | undefined,
direction?: SortDirection
): SortForCmd | undefined {
if (sort == null) return undefined;
if (Array.isArray(sort) && !sort.length) return undefined;
if (typeof sort === 'object' && !Object.keys(sort).length) return undefined;
if (typeof sort === 'string') return { [sort]: prepareDirection(direction) };
if (isPair(sort)) return pairToObject(sort);
if (isDeep(sort)) return deepToObject(sort);
if (Array.isArray(sort)) return stringsToObject(sort);
if (typeof sort === 'object') return objectToObject(sort);
throw new Error(`Invalid sort format: ${JSON.stringify(sort)}`);
}
Loading

0 comments on commit 8aad134

Please sign in to comment.