relay-mongoose
Advanced tools
Comparing version 0.3.0 to 0.4.0
@@ -1,2 +0,2 @@ | ||
import { Document, FilterQuery } from 'mongoose'; | ||
import { Document, DocumentQuery, FilterQuery } from 'mongoose'; | ||
import * as mongoose from 'mongoose'; | ||
@@ -14,10 +14,11 @@ export declare type Cursor = string; | ||
} | ||
export declare type PageInfo = { | ||
startCursor?: Cursor; | ||
endCursor?: Cursor; | ||
hasNextPage: boolean; | ||
hasPreviousPage: boolean; | ||
}; | ||
export interface ConnectionDocuments<T> { | ||
edges: Edge<T>[]; | ||
pageInfo: { | ||
startCursor?: Cursor; | ||
endCursor?: Cursor; | ||
hasNextPage: boolean; | ||
hasPreviousPage: boolean; | ||
}; | ||
pageInfo: PageInfo; | ||
} | ||
@@ -30,9 +31,11 @@ export interface EnhancedDocument extends Document { | ||
} | ||
export declare const fromRelayId: (id: string) => { | ||
export declare type DecodedRelayId = { | ||
modelName: string | null; | ||
objectId: string | null; | ||
}; | ||
export declare const fromRelayId: (id: string | null | undefined) => DecodedRelayId; | ||
export declare class EnhancedModel<T extends EnhancedDocument, QueryHelpers = {}> { | ||
get relayId(): string; | ||
static findConnections<T extends Document>(conditions: FilterQuery<T>, paginationArgs: PaginationArgs, projection?: any | null): Promise<ConnectionDocuments<T>>; | ||
static findConnections<T extends Document>(conditions: FilterQuery<T>, { before, after, first, last }: PaginationArgs, projection?: any | null): Promise<ConnectionDocuments<T>>; | ||
} | ||
export declare const findConnections: <DocType extends EnhancedDocument, QueryHelpers = {}>(documentQuery: DocumentQuery<DocType | DocType[], any, QueryHelpers> & QueryHelpers, { before, after, first, last }: PaginationArgs, projection?: any | null) => Promise<ConnectionDocuments<DocType>>; |
@@ -50,3 +50,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.EnhancedModel = exports.fromRelayId = void 0; | ||
exports.findConnections = exports.EnhancedModel = exports.fromRelayId = void 0; | ||
var mongoose = require("mongoose"); | ||
@@ -64,2 +64,59 @@ exports.fromRelayId = function (id) { | ||
}; | ||
var paginate = function (dataQuery, _a) { | ||
var first = _a.first, last = _a.last; | ||
return __awaiter(void 0, void 0, void 0, function () { | ||
var pageInfo, data; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
pageInfo = { | ||
hasNextPage: false, | ||
hasPreviousPage: false | ||
}; | ||
if (!(first !== undefined)) return [3 /*break*/, 2]; | ||
return [4 /*yield*/, dataQuery | ||
.sort({ _id: 1 }) | ||
.limit(first + 1) | ||
.exec()]; | ||
case 1: | ||
data = _b.sent(); | ||
if (data.length > first) { | ||
pageInfo.hasNextPage = true; | ||
data.pop(); | ||
} | ||
return [3 /*break*/, 6]; | ||
case 2: | ||
if (!(last !== undefined)) return [3 /*break*/, 4]; | ||
return [4 /*yield*/, dataQuery | ||
.sort({ _id: -1 }) | ||
.limit(last + 1) | ||
.exec()]; | ||
case 3: | ||
data = _b.sent(); | ||
if (data.length > last) { | ||
pageInfo.hasPreviousPage = true; | ||
data.pop(); | ||
} | ||
data.reverse(); | ||
return [3 /*break*/, 6]; | ||
case 4: return [4 /*yield*/, dataQuery.sort({ _id: 1 }).exec()]; | ||
case 5: | ||
data = _b.sent(); | ||
_b.label = 6; | ||
case 6: | ||
if (data.length > 0) { | ||
pageInfo.startCursor = data[0].relayId; | ||
pageInfo.endCursor = data[data.length - 1].relayId; | ||
} | ||
return [2 /*return*/, { | ||
edges: data.map(function (edge) { return ({ | ||
cursor: edge.relayId, | ||
node: edge | ||
}); }), | ||
pageInfo: pageInfo | ||
}]; | ||
} | ||
}); | ||
}); | ||
}; | ||
var EnhancedModel = /** @class */ (function () { | ||
@@ -79,48 +136,13 @@ function EnhancedModel() { | ||
}); | ||
EnhancedModel.findConnections = function (conditions, paginationArgs, projection) { | ||
EnhancedModel.findConnections = function (conditions, _a, projection) { | ||
var before = _a.before, after = _a.after, first = _a.first, last = _a.last; | ||
return __awaiter(this, void 0, void 0, function () { | ||
var before, after, first, last, beforeObjectId, afterObjectId, idQuery, query, count, dataQuery, hasNextPage, hasPreviousPage, data, pageInfo; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
before = paginationArgs.before, after = paginationArgs.after, first = paginationArgs.first, last = paginationArgs.last; | ||
beforeObjectId = exports.fromRelayId(before).objectId; | ||
afterObjectId = exports.fromRelayId(after).objectId; | ||
idQuery = __assign(__assign({}, (beforeObjectId !== null ? { $lt: mongoose.Types.ObjectId(beforeObjectId) } : {})), (afterObjectId !== null ? { $gt: mongoose.Types.ObjectId(afterObjectId) } : {})); | ||
query = __assign(__assign({}, conditions), (Object.keys(idQuery).length === 0 ? {} : { _id: idQuery })); | ||
return [4 /*yield*/, this.find(query).countDocuments()]; | ||
case 1: | ||
count = _a.sent(); | ||
dataQuery = this.find(query, projection); | ||
hasNextPage = false; | ||
hasPreviousPage = false; | ||
if (first !== undefined && first < count) { | ||
dataQuery.limit(first); | ||
hasNextPage = true; | ||
} | ||
if (last !== undefined && last < count) { | ||
dataQuery.skip(count - last); | ||
hasPreviousPage = true; | ||
if (hasNextPage) { | ||
hasNextPage = false; | ||
} | ||
} | ||
return [4 /*yield*/, dataQuery]; | ||
case 2: | ||
data = _a.sent(); | ||
pageInfo = __assign({ hasNextPage: hasNextPage, | ||
hasPreviousPage: hasPreviousPage }, (data.length > 0 | ||
? { | ||
startCursor: data[0].relayId, | ||
endCursor: data[data.length - 1].relayId | ||
} | ||
: {})); | ||
return [2 /*return*/, { | ||
edges: data.map(function (edge) { return ({ | ||
cursor: edge.relayId, | ||
node: edge | ||
}); }), | ||
pageInfo: pageInfo | ||
}]; | ||
} | ||
var beforeObjectId, afterObjectId, idQuery, query, dataQuery; | ||
return __generator(this, function (_b) { | ||
beforeObjectId = exports.fromRelayId(before).objectId; | ||
afterObjectId = exports.fromRelayId(after).objectId; | ||
idQuery = __assign(__assign({}, (beforeObjectId !== null ? { $lt: mongoose.Types.ObjectId(beforeObjectId) } : {})), (afterObjectId !== null ? { $gt: mongoose.Types.ObjectId(afterObjectId) } : {})); | ||
query = __assign(__assign({}, conditions), (Object.keys(idQuery).length === 0 ? {} : { _id: idQuery })); | ||
dataQuery = this.find(query, projection); | ||
return [2 /*return*/, paginate(dataQuery, { first: first, last: last })]; | ||
}); | ||
@@ -132,1 +154,14 @@ }); | ||
exports.EnhancedModel = EnhancedModel; | ||
exports.findConnections = function (documentQuery, _a, projection) { | ||
var before = _a.before, after = _a.after, first = _a.first, last = _a.last; | ||
return __awaiter(void 0, void 0, void 0, function () { | ||
var beforeObjectId, afterObjectId, idQuery, query; | ||
return __generator(this, function (_b) { | ||
beforeObjectId = exports.fromRelayId(before).objectId; | ||
afterObjectId = exports.fromRelayId(after).objectId; | ||
idQuery = __assign(__assign({}, (beforeObjectId !== null ? { $lt: mongoose.Types.ObjectId(beforeObjectId) } : {})), (afterObjectId !== null ? { $gt: mongoose.Types.ObjectId(afterObjectId) } : {})); | ||
query = __assign({}, (Object.keys(idQuery).length === 0 ? {} : { _id: idQuery })); | ||
return [2 /*return*/, paginate(documentQuery.find(query, projection), { first: first, last: last })]; | ||
}); | ||
}); | ||
}; |
{ | ||
"name": "relay-mongoose", | ||
"version": "v0.3.0", | ||
"version": "0.4.0", | ||
"repository": "https://github.com/wadamek65/relay-mongoose.git", | ||
@@ -5,0 +5,0 @@ "author": "wadamek65 <wadamek65@gmail.com>", |
@@ -7,3 +7,3 @@ # relay-mongoose | ||
## Usage | ||
# `EnhancedModel` | ||
### `EnhancedModel` | ||
Use mongoose `loadClass()` method as [described here](https://mongoosejs.com/docs/guide.html#es6-classes). | ||
@@ -58,3 +58,3 @@ ```typescript | ||
# `relayId` | ||
### `relayId` | ||
As shown in the previous example, all documents returned by `findConnections` have a modified id which is | ||
@@ -83,2 +83,19 @@ the base64 encoded concatenation of model name and object ID by a dot. | ||
### `findConnections` standalone function | ||
There is also a standalone `findConnections` function to which as the first argument you can pass a mongoose | ||
query for more flexibility. For example: | ||
```typescript | ||
import * as mongoose from 'mongoose'; | ||
import { EnhancedModel, findConnections } from 'relay-mongoose'; | ||
const TestModel: EnhancedModel<any> = mongoose.model('test', testSchema) as any; | ||
const firstQuery = TestModel.find({ condition: 2 }).populate(...); | ||
const paginatedData = await findConnections(firstQuery, { first: 10 }); | ||
``` | ||
Please note that the query passed as the first argument has to have extended the `EnhancedModel` class | ||
as `relayId` property is needed for correct mapping. | ||
## API | ||
@@ -135,3 +152,11 @@ | ||
### `findConnections` | ||
```typescript | ||
export const findConnections = async <DocType extends EnhancedDocument, QueryHelpers = {}>( | ||
documentQuery: DocumentQuery<DocType | DocType[] | null, any, QueryHelpers> & QueryHelpers, | ||
{ before, after, first, last }: PaginationArgs, | ||
projection?: any | null | ||
): Promise<ConnectionDocuments<DocType>> | ||
``` | ||
## License | ||
MIT |
130
src/index.ts
@@ -1,2 +0,2 @@ | ||
import { Document, FilterQuery } from 'mongoose'; | ||
import { Document, DocumentQuery, FilterQuery } from 'mongoose'; | ||
import * as mongoose from 'mongoose'; | ||
@@ -18,10 +18,12 @@ | ||
export type PageInfo = { | ||
startCursor?: Cursor; | ||
endCursor?: Cursor; | ||
hasNextPage: boolean; | ||
hasPreviousPage: boolean; | ||
}; | ||
export interface ConnectionDocuments<T> { | ||
edges: Edge<T>[]; | ||
pageInfo: { | ||
startCursor?: Cursor; | ||
endCursor?: Cursor; | ||
hasNextPage: boolean; | ||
hasPreviousPage: boolean; | ||
}; | ||
pageInfo: PageInfo; | ||
} | ||
@@ -41,3 +43,4 @@ | ||
export const fromRelayId = (id: string): { modelName: string | null; objectId: string | null } => { | ||
export type DecodedRelayId = { modelName: string | null; objectId: string | null }; | ||
export const fromRelayId = (id: string | null | undefined): DecodedRelayId => { | ||
if (!id) { | ||
@@ -56,2 +59,49 @@ return { modelName: null, objectId: null }; | ||
const paginate = async <DocType extends EnhancedDocument, QueryHelpers = {}>( | ||
dataQuery: DocumentQuery<DocType | DocType[] | null, any, QueryHelpers> & QueryHelpers, | ||
{ first, last }: PaginationArgs | ||
): Promise<ConnectionDocuments<DocType>> => { | ||
const pageInfo: PageInfo = { | ||
hasNextPage: false, | ||
hasPreviousPage: false | ||
}; | ||
let data; | ||
if (first !== undefined) { | ||
data = await dataQuery | ||
.sort({ _id: 1 }) | ||
.limit(first + 1) | ||
.exec(); | ||
if (data.length > first) { | ||
pageInfo.hasNextPage = true; | ||
data.pop(); | ||
} | ||
} else if (last !== undefined) { | ||
data = await dataQuery | ||
.sort({ _id: -1 }) | ||
.limit(last + 1) | ||
.exec(); | ||
if (data.length > last) { | ||
pageInfo.hasPreviousPage = true; | ||
data.pop(); | ||
} | ||
data.reverse(); | ||
} else { | ||
data = await dataQuery.sort({ _id: 1 }).exec(); | ||
} | ||
if (data.length > 0) { | ||
pageInfo.startCursor = data[0].relayId; | ||
pageInfo.endCursor = data[data.length - 1].relayId; | ||
} | ||
return { | ||
edges: data.map(edge => ({ | ||
cursor: edge.relayId, | ||
node: edge | ||
})), | ||
pageInfo | ||
}; | ||
}; | ||
export class EnhancedModel<T extends EnhancedDocument, QueryHelpers = {}> { | ||
@@ -62,3 +112,3 @@ get relayId() { | ||
// eslint-disable-next-line | ||
// @ts-ignore | ||
// @ts-ignore | ||
return Buffer.from(`${this.constructor.modelName}.${this._id.toString()}`).toString('base64'); | ||
@@ -69,6 +119,5 @@ } | ||
conditions: FilterQuery<T>, | ||
paginationArgs: PaginationArgs, | ||
{ before, after, first, last }: PaginationArgs, | ||
projection?: any | null | ||
): Promise<ConnectionDocuments<T>> { | ||
const { before, after, first, last } = paginationArgs; | ||
const { objectId: beforeObjectId } = fromRelayId(before); | ||
@@ -87,44 +136,25 @@ const { objectId: afterObjectId } = fromRelayId(after); | ||
// eslint-disable-next-line | ||
// @ts-ignore | ||
const count = await this.find(query).countDocuments(); | ||
// eslint-disable-next-line | ||
// @ts-ignore | ||
// @ts-ignore | ||
const dataQuery = this.find(query, projection); | ||
return paginate<any>(dataQuery, { first, last }); | ||
} | ||
} | ||
let hasNextPage = false; | ||
let hasPreviousPage = false; | ||
export const findConnections = async <DocType extends EnhancedDocument, QueryHelpers = {}>( | ||
documentQuery: DocumentQuery<DocType | DocType[] | null, any, QueryHelpers> & QueryHelpers, | ||
{ before, after, first, last }: PaginationArgs, | ||
projection?: any | null | ||
): Promise<ConnectionDocuments<DocType>> => { | ||
const { objectId: beforeObjectId } = fromRelayId(before); | ||
const { objectId: afterObjectId } = fromRelayId(after); | ||
const idQuery = { | ||
...(beforeObjectId !== null ? { $lt: mongoose.Types.ObjectId(beforeObjectId) } : {}), | ||
...(afterObjectId !== null ? { $gt: mongoose.Types.ObjectId(afterObjectId) } : {}) | ||
}; | ||
if (first !== undefined && first < count) { | ||
dataQuery.limit(first); | ||
hasNextPage = true; | ||
} | ||
const query = { | ||
...(Object.keys(idQuery).length === 0 ? {} : { _id: idQuery }) | ||
}; | ||
if (last !== undefined && last < count) { | ||
dataQuery.skip(count - last); | ||
hasPreviousPage = true; | ||
if (hasNextPage) { | ||
hasNextPage = false; | ||
} | ||
} | ||
const data = await dataQuery; | ||
const pageInfo = { | ||
hasNextPage, | ||
hasPreviousPage, | ||
...(data.length > 0 | ||
? { | ||
startCursor: data[0].relayId, | ||
endCursor: data[data.length - 1].relayId | ||
} | ||
: {}) | ||
}; | ||
return { | ||
edges: data.map(edge => ({ | ||
cursor: edge.relayId, | ||
node: edge | ||
})), | ||
pageInfo | ||
}; | ||
} | ||
} | ||
return paginate(documentQuery.find(query, projection), { first, last }); | ||
}; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
21677
336
159
0