Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow $meta sort on all keys, not just $vector and $vectorize #254

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions src/collections/collection.ts
Original file line number Diff line number Diff line change
@@ -18,20 +18,20 @@ import {executeOperation, omit, setDefaultIdForUpsert} from './utils';
import {InsertManyResult} from 'mongoose';
import {
DeleteOneOptions,
FindOneAndDeleteOptions,
FindOneAndDeleteOptionsForDataAPI,
FindOneAndReplaceOptionsForDataAPI,
findOneAndReplaceInternalOptionsKeys,
FindOneAndReplaceOptions,
findOneAndUpdateInternalOptionsKeys,
FindOneAndUpdateOptions,
FindOneOptions,
insertManyInternalOptionsKeys,
InsertManyOptions,
updateManyInternalOptionsKeys,
UpdateManyOptions,
updateOneInternalOptionsKeys,
UpdateOneOptions,
FindOptions,
findOneInternalOptionsKeys
findOneInternalOptionsKeys,
FindOptionsForDataAPI,
FindOneOptionsForDataAPI,
UpdateOneOptionsForDataAPI
} from './options';

export interface DataAPIUpdateResult {
@@ -119,7 +119,7 @@ export class Collection {
});
}

async updateOne(filter: Record<string, any>, update: Record<string, any>, options?: UpdateOneOptions) {
async updateOne(filter: Record<string, any>, update: Record<string, any>, options?: UpdateOneOptionsForDataAPI) {
return executeOperation(async (): Promise<DataAPIUpdateResult> => {
const command = {
updateOne: {
@@ -253,11 +253,11 @@ export class Collection {
});
}

find(filter: Record<string, any>, options?: FindOptions): FindCursor {
find(filter: Record<string, any>, options?: FindOptionsForDataAPI): FindCursor {
return new FindCursor(this, filter, options);
}

async findOne(filter: Record<string, any>, options?: FindOneOptions): Promise<Record<string, any> | null> {
async findOne(filter: Record<string, any>, options?: FindOneOptionsForDataAPI): Promise<Record<string, any> | null> {
return executeOperation(async (): Promise<Record<string, any> | null> => {
const command = {
findOne: {
@@ -280,7 +280,7 @@ export class Collection {
});
}

async findOneAndReplace(filter: Record<string, any>, replacement: Record<string, any>, options?: FindOneAndReplaceOptions): Promise<DataAPIModifyResult> {
async findOneAndReplace(filter: Record<string, any>, replacement: Record<string, any>, options?: FindOneAndReplaceOptionsForDataAPI): Promise<DataAPIModifyResult> {
return executeOperation(async (): Promise<DataAPIModifyResult> => {
const command = {
findOneAndReplace: {
@@ -336,7 +336,7 @@ export class Collection {
});
}

async findOneAndDelete(filter: Record<string, any>, options?: FindOneAndDeleteOptions): Promise<DataAPIModifyResult> {
async findOneAndDelete(filter: Record<string, any>, options?: FindOneAndDeleteOptionsForDataAPI): Promise<DataAPIModifyResult> {
const command = {
findOneAndDelete: {
filter,
6 changes: 3 additions & 3 deletions src/collections/cursor.ts
Original file line number Diff line number Diff line change
@@ -14,12 +14,12 @@

import { Collection } from './collection';
import { executeOperation, omit } from './utils';
import {findInternalOptionsKeys, FindOptions, FindOptionsInternal} from './options';
import {findInternalOptionsKeys, FindOptionsForDataAPI, FindOptionsInternal} from './options';

export class FindCursor {
collection: Collection;
filter: Record<string, any>;
options: FindOptions;
options: FindOptionsForDataAPI;
documents: Record<string, any>[] = [];
status = 'uninitialized';
nextPageState?: string;
@@ -31,7 +31,7 @@ export class FindCursor {
exhausted: boolean;
sortVector?: number[];

constructor(collection: Collection, filter: Record<string, any>, options?: FindOptions) {
constructor(collection: Collection, filter: Record<string, any>, options?: FindOptionsForDataAPI) {
this.collection = collection;
this.filter = filter;
this.options = options ?? {};
18 changes: 12 additions & 6 deletions src/collections/options.ts
Original file line number Diff line number Diff line change
@@ -12,16 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.

export type SortOption = Record<string, 1 | -1> |
{ $vector: { $meta: Array<number> } } |
{ $vector: Array<number> } |
{ $vectorize: { $meta: string } } |
{ $vectorize: string };
export type SortOption = Record<string, 1 | -1 | { $meta: Array<number> } | { $meta: string }>;
export type SortOptionInternal = Record<string, 1 | -1 | Array<number> | string>;

export type ProjectionOption = Record<string, 1 | 0 | true | false | { $slice: number }>;

export interface DeleteOneOptions {
sort?: Record<string, 1 | -1>;
sort?: SortOption;
}
export interface DeleteOneOptionsForDataAPI {
sort?: SortOptionInternal;
}

export interface VectorOptions {
@@ -37,6 +37,7 @@ export interface FindOptions {
includeSimilarity?: boolean;
includeSortVector?: boolean;
}
export type FindOptionsForDataAPI = Omit<FindOptions, 'sort'> & { sort?: SortOptionInternal };

class _FindOptionsInternal {
limit?: number = undefined;
@@ -58,6 +59,7 @@ export interface FindOneOptions {
includeSimilarity?: boolean;
includeSortVector?: boolean;
}
export type FindOneOptionsForDataAPI = Omit<FindOneOptions, 'sort'> & { sort?: SortOptionInternal };

class _FindOneOptionsInternal {
includeSimilarity?: boolean = undefined;
@@ -73,6 +75,7 @@ export interface FindOneAndDeleteOptions {
projection?: ProjectionOption;
includeResultMetadata?: boolean;
}
export type FindOneAndDeleteOptionsForDataAPI = Omit<FindOneAndDeleteOptions, 'sort'> & { sort?: SortOptionInternal };


class _FindOneAndReplaceOptions {
@@ -84,6 +87,7 @@ class _FindOneAndReplaceOptions {
}

export interface FindOneAndReplaceOptions extends _FindOneAndReplaceOptions {}
export type FindOneAndReplaceOptionsForDataAPI = Omit<FindOneAndReplaceOptions, 'sort'> & { sort?: SortOptionInternal };

export const findOneAndReplaceInternalOptionsKeys: Set<string> = new Set(
Object.keys(new _FindOneAndReplaceOptions)
@@ -98,6 +102,7 @@ class _FindOneAndUpdateOptions {
}

export interface FindOneAndUpdateOptions extends _FindOneAndUpdateOptions {}
export type FindOneAndUpdateOptionsForDataAPI = Omit<FindOneAndUpdateOptions, 'sort'> & { sort?: SortOptionInternal };

export const findOneAndUpdateInternalOptionsKeys: Set<string> = new Set(
Object.keys(new _FindOneAndUpdateOptions)
@@ -134,6 +139,7 @@ class _UpdateOneOptions {
}

export interface UpdateOneOptions extends _UpdateOneOptions {}
export type UpdateOneOptionsForDataAPI = Omit<UpdateOneOptions, 'sort'> & { sort?: SortOptionInternal };

export const updateOneInternalOptionsKeys: Set<string> = new Set(
Object.keys(new _UpdateOneOptions)
112 changes: 75 additions & 37 deletions src/driver/collection.ts
Original file line number Diff line number Diff line change
@@ -15,15 +15,23 @@
import { default as MongooseCollection } from 'mongoose/lib/collection';
import {
DeleteOneOptions,
DeleteOneOptionsForDataAPI,
FindOneAndDeleteOptions,
FindOneAndDeleteOptionsForDataAPI,
FindOneAndReplaceOptions,
FindOneAndReplaceOptionsForDataAPI,
FindOneAndUpdateOptions,
FindOneAndUpdateOptionsForDataAPI,
FindOneOptions,
FindOneOptionsForDataAPI,
FindOptions,
FindOptionsForDataAPI,
InsertManyOptions,
SortOption,
SortOptionInternal,
UpdateManyOptions,
UpdateOneOptions
UpdateOneOptions,
UpdateOneOptionsForDataAPI
} from '@/src/collections/options';
import { DataAPIDeleteResult } from '../collections/collection';

@@ -77,10 +85,14 @@ export class Collection extends MongooseCollection {
* @param callback
*/
find(filter: Record<string, any>, options?: FindOptions, callback?: NodeCallback<Record<string, any>[]>) {
if (options != null) {
processSortOption(options);
let requestOptions: FindOptionsForDataAPI | undefined = undefined;
if (options != null && options.sort != null) {
requestOptions = { ...options, sort: processSortOption(options.sort) };
} else if (options != null && options.sort == null) {
requestOptions = { ...options, sort: undefined };
delete requestOptions.sort;
}
const cursor = this.collection.find(filter, options);
const cursor = this.collection.find(filter, requestOptions);
if (callback != null) {
return callback(null, cursor);
}
@@ -93,10 +105,14 @@ export class Collection extends MongooseCollection {
* @param options
*/
findOne(filter: Record<string, any>, options?: FindOneOptions) {
if (options != null) {
processSortOption(options);
let requestOptions: FindOneOptionsForDataAPI | undefined = undefined;
if (options != null && options.sort != null) {
requestOptions = { ...options, sort: processSortOption(options.sort) };
} else if (options != null && options.sort == null) {
requestOptions = { ...options, sort: undefined };
delete requestOptions.sort;
}
return this.collection.findOne(filter, options);
return this.collection.findOne(filter, requestOptions);
}

/**
@@ -173,10 +189,14 @@ export class Collection extends MongooseCollection {
* @param options
*/
async findOneAndUpdate(filter: Record<string, any>, update: Record<string, any>, options?: FindOneAndUpdateOptions) {
if (options != null) {
processSortOption(options);
let requestOptions: FindOneAndUpdateOptionsForDataAPI | undefined = undefined;
if (options != null && options.sort != null) {
requestOptions = { ...options, sort: processSortOption(options.sort) };
} else if (options != null && options.sort == null) {
requestOptions = { ...options, sort: undefined };
delete requestOptions.sort;
}
const res = await this.collection.findOneAndUpdate(filter, update, options);
const res = await this.collection.findOneAndUpdate(filter, update, requestOptions);
if (IS_MONGOOSE_7) {
return options?.includeResultMetadata === false ? res.value : res;
} else if (options?.includeResultMetadata !== false) {
@@ -191,10 +211,14 @@ export class Collection extends MongooseCollection {
* @param options
*/
async findOneAndDelete(filter: Record<string, any>, options?: FindOneAndDeleteOptions) {
if (options != null) {
processSortOption(options);
let requestOptions: FindOneAndDeleteOptionsForDataAPI | undefined = undefined;
if (options != null && options.sort != null) {
requestOptions = { ...options, sort: processSortOption(options.sort) };
} else if (options != null && options.sort == null) {
requestOptions = { ...options, sort: undefined };
delete requestOptions.sort;
}
const res = await this.collection.findOneAndDelete(filter, options);
const res = await this.collection.findOneAndDelete(filter, requestOptions);
if (IS_MONGOOSE_7) {
return options?.includeResultMetadata === false ? res.value : res;
} else if (options?.includeResultMetadata !== false) {
@@ -210,10 +234,14 @@ export class Collection extends MongooseCollection {
* @param options
*/
async findOneAndReplace(filter: Record<string, any>, newDoc: Record<string, any>, options?: FindOneAndReplaceOptions) {
if (options != null) {
processSortOption(options);
let requestOptions: FindOneAndReplaceOptionsForDataAPI | undefined = undefined;
if (options != null && options.sort != null) {
requestOptions = { ...options, sort: processSortOption(options.sort) };
} else if (options != null && options.sort == null) {
requestOptions = { ...options, sort: undefined };
delete requestOptions.sort;
}
const res = await this.collection.findOneAndReplace(filter, newDoc, options);
const res = await this.collection.findOneAndReplace(filter, newDoc, requestOptions);
if (IS_MONGOOSE_7) {
return options?.includeResultMetadata === false ? res.value : res;
} else if (options?.includeResultMetadata !== false) {
@@ -236,12 +264,16 @@ export class Collection extends MongooseCollection {
* @param options
* @param callback
*/
deleteOne(filter: Record<string, any>, options?: DeleteOneOptions, callback?: NodeCallback<DataAPIDeleteResult>) {
if (options != null) {
processSortOption(options);
deleteOne(filter: Record<string, any>, options?: DeleteOneOptions, callback?: NodeCallback<DataAPIDeleteResult>) {
let requestOptions: DeleteOneOptionsForDataAPI | undefined = undefined;
if (options != null && options.sort != null) {
requestOptions = { ...options, sort: processSortOption(options.sort) };
} else if (options != null && options.sort == null) {
requestOptions = { ...options, sort: undefined };
delete requestOptions.sort;
}

const promise = this.collection.deleteOne(filter, options);
const promise = this.collection.deleteOne(filter, requestOptions);

if (callback != null) {
promise.then((res: DataAPIDeleteResult) => callback(null, res), (err: Error) => callback(err, null));
@@ -257,10 +289,14 @@ export class Collection extends MongooseCollection {
* @param options
*/
updateOne(filter: Record<string, any>, update: Record<string, any>, options?: UpdateOneOptions) {
if (options != null) {
processSortOption(options);
let requestOptions: UpdateOneOptionsForDataAPI | undefined = undefined;
if (options != null && options.sort != null) {
requestOptions = { ...options, sort: processSortOption(options.sort) };
} else if (options != null && options.sort == null) {
requestOptions = { ...options, sort: undefined };
delete requestOptions.sort;
}
return this.collection.updateOne(filter, update, options);
return this.collection.updateOne(filter, update, requestOptions);
}

/**
@@ -369,20 +405,22 @@ export class Collection extends MongooseCollection {
}
}

function processSortOption(options: { sort?: SortOption }) {
if (options.sort == null) {
return;
}
if ('$vector' in options.sort &&
typeof options.sort.$vector === 'object' &&
!Array.isArray(options.sort.$vector)) {
options.sort.$vector = options.sort.$vector.$meta;
}
if ('$vectorize' in options.sort &&
typeof options.sort.$vectorize === 'object' &&
!Array.isArray(options.sort.$vectorize)) {
options.sort.$vectorize = options.sort.$vectorize.$meta;
function processSortOption(sort: SortOption): SortOptionInternal {
const result: SortOptionInternal = {};
for (const key of Object.keys(sort)) {
const sortValue = sort[key];
if (sortValue == null || typeof sortValue !== 'object') {
result[key] = sortValue;
continue;
}

const $meta = typeof sortValue === 'object' && sortValue.$meta;
if ($meta) {
result[key] = $meta;
}
}

return result;
}

export class OperationNotSupportedError extends Error {
Loading