diff --git a/src/index.js b/src/index.js index f537817..e3fe72c 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,5 @@ let Promise = require('bluebird') +let _ = require('lodash/fp') // Basic function to encapsulate everything needed to run a request - tiny wrapper over raw mongo syntax let mongoDSL = (client, dsl) => { @@ -6,6 +7,28 @@ let mongoDSL = (client, dsl) => { if (dsl.aggs) return Collection.aggregate(dsl.aggs).toArray() } +let createLookup = node => + node.lookup + ? [ + { + $lookup: { + from: _.getOr(node.lookup, 'lookup.from', node), + localField: _.getOr(node.lookup, 'lookup.localField', node), + foreignField: _.getOr('_id', 'lookup.foreignField', node), + as: _.getOr(node.lookup, 'lookup.as', node), + }, + }, + node.noUnwind + ? {} + : { + $unwind: { + path: `$${_.getOr(node.lookup, 'lookup.as', node)}`, + preserveNullAndEmptyArrays: true, + }, + }, + ] + : [] + let MongoProvider = config => ({ groupCombinator: (group, filters) => ({ [`$${group.join === 'not' ? 'nor' : group.join}`]: filters, @@ -22,6 +45,7 @@ let MongoProvider = config => ({ { $match: filters || {}, }, + ...createLookup(node), ...aggs, ], }, diff --git a/test/example-types/dateHistogram.js b/test/example-types/dateHistogram.js index 002427d..d1528ef 100644 --- a/test/example-types/dateHistogram.js +++ b/test/example-types/dateHistogram.js @@ -1,19 +1,12 @@ let _ = require('lodash/fp') let { expect } = require('chai') -let { MongoClient } = require('mongodb') -let { MongoMemoryServer } = require('mongodb-memory-server') +let { setUpClientConnection } = require('../utils') let dateHistogram = require('../../src/example-types/dateHistogram') let aggregate before(async () => { - let mongoServer = new MongoMemoryServer() - let mongoUri = await mongoServer.getConnectionString() - let conn = await MongoClient.connect(mongoUri, { - useNewUrlParser: true, - useUnifiedTopology: true, - }) - let db = conn.db(await mongoServer.getDbName()) + let db = await setUpClientConnection() let col = db.collection('test') // Generate sample data diff --git a/test/index.js b/test/index.js new file mode 100644 index 0000000..353d919 --- /dev/null +++ b/test/index.js @@ -0,0 +1,106 @@ +let { expect } = require('chai') +let MongoProvider = require('../src/index') +let { MongoClient } = require('mongodb') +let { MongoMemoryServer } = require('mongodb-memory-server') + +let getClient = () => new MongoMemoryServer() + +// let aggregate + +// before(async () => { +// let mongoServer = new MongoMemoryServer() +// let mongoUri = await mongoServer.getConnectionString() +// let conn = await MongoClient.connect(mongoUri, { +// useNewUrlParser: true, +// useUnifiedTopology: true, +// }) +// let db = conn.db(await mongoServer.getDbName()) +// let col = db.collection('test') + +// // Generate sample data +// let sampleData = _.times( +// i => ({ +// createdAt: new Date(`2020-02-0${(i % 5) + 1}`), +// metrics: { usersCount: i * 100 }, +// }), +// 50 +// ) +// col.insertMany(sampleData) +// aggregate = aggs => col.aggregate(aggs).toArray() +// }) + +let fullLookup = { + from: 'organization', + localField: 'organization', + foreignField: '_id', + as: 'organization', +} + +let stuff = { + options: {}, + node: { + field: 'organization', + type: 'facet', + isMongoId: true, + paused: false, + lookup: 'organization', + values: ['53b46fec938d8948dw294gl0'], + mode: 'include', + optionsFilter: '', + key: 'organization-facet', + lastUpdateTime: 1603807973631, + schema: 'user', + }, + schema: { + securityRules: 'restrictToSameOrg', + mongo: { collection: 'user' }, + fields: { + _id: { + typeDefault: 'mongoId', + typeOptions: ['mongoId'], + mongo: { dataType: 'objectId' }, + field: '_id', + label: 'Id', + defaultComponentProps: { Facet: {} }, + hide: { facetFilter: true }, + defaultNodeProps: { facet: { isMongoId: true } }, + }, + organization: { + typeDefault: 'mongoId', + typeOptions: ['mongoId'], + mongo: { dataType: 'objectId' }, + field: 'organization', + label: 'Agency', + defaultComponentProps: { Facet: {} }, + hide: { facetFilter: true }, + defaultNodeProps: { facet: { isMongoId: true } }, + }, + }, + }, + filters: { + $and: [ + { isDeleted: { $nin: [true] } }, + { status: { $in: ['open'] } }, + { dueAt: { $gte: '2020-10-27T14:23:28.397Z' } }, + { + $or: [ + { maxResponses: { $gte: 0, $lte: 0 } }, + { remainingSlots: { $gte: 1 } }, + ], + }, + { organization: { $in: ['53b46fec938d89315aae1940'] } }, + ], + }, + aggs: [ + { $unwind: '$organization' }, + { $group: { _id: '$organization' } }, + { $group: { _id: 1, count: { $sum: 1 } } }, + ], +} + +describe('MongoProvider', () => { + xit('should return a standard search', () => {}) + xit('should run a simple lookup/unwind on a node with a string "lookup" property', () => {}) + xit('should run a custom lookup on a node with a custom object "lookup" property', () => {}) + xit('should not unwind the lookup if noUnwind is true', () => {}) +}) diff --git a/test/utils.js b/test/utils.js new file mode 100644 index 0000000..7601d13 --- /dev/null +++ b/test/utils.js @@ -0,0 +1,22 @@ +let { MongoClient } = require('mongodb') +let { MongoMemoryServer } = require('mongodb-memory-server') + +let getMongoClient = async mongoUri => + MongoClient.connect(mongoUri, { + useNewUrlParser: true, + useUnifiedTopology: true, + }) + +let setUpClientConnection = async () => { + let mongoServer = new MongoMemoryServer() + let mongoUri = await mongoServer.getConnectionString() + let connection = await getMongoClient(mongoUri) + let dbName = await mongoServer.getDbName() + + return connection.db(dbName) +} + +module.exports = { + getMongoClient, + setUpClientConnection, +}