From ef504f3775482deb90a363787eba995bff38bcb5 Mon Sep 17 00:00:00 2001 From: kaveh Date: Tue, 31 Dec 2024 22:49:43 +0330 Subject: [PATCH 1/4] ADD: TsApiFeatures ADD: Typesense ADD: Index all belongings router, controller & service --- components/belonging/belongingController.js | 10 + components/belonging/belongingRoutes.js | 2 + components/belonging/belongingService.js | 70 +++++++ setup/typesense.js | 15 ++ utils/TsApiFeatures.js | 194 ++++++++++++++++++++ 5 files changed, 291 insertions(+) create mode 100644 setup/typesense.js create mode 100644 utils/TsApiFeatures.js diff --git a/components/belonging/belongingController.js b/components/belonging/belongingController.js index aabe7f0..0e86a51 100644 --- a/components/belonging/belongingController.js +++ b/components/belonging/belongingController.js @@ -1,6 +1,16 @@ const catchAsync = require('../../globalErrorHandler/catchAsync') const belongingService = require('./belongingService') +exports.indexAllBelongings = catchAsync(async (req, res, next) => { + const { indexedCount } = await belongingService.indexAllBelongings() + + res.status(200).json({ + status: 'success', + indexedCount + }) +}) + + exports.getAllBelongings = catchAsync(async (req, res, next) => { const belongings = await belongingService.getAllBelongings() diff --git a/components/belonging/belongingRoutes.js b/components/belonging/belongingRoutes.js index 0e8fc7e..584f9c9 100644 --- a/components/belonging/belongingRoutes.js +++ b/components/belonging/belongingRoutes.js @@ -11,6 +11,8 @@ router.get('/admin/get-all', perm.requires('getallbelongings'), belongingControl router.post('/admin/create', perm.requires('createabelonging'), belongingController.createBelonging) +router.post('/admin/index-all', perm.requires('indexbelongings', belongingController.indexAllBelongings)) + router.get('/admin/get-one/:belongingId', perm.requires('getabelonging'), belongingController.getBelonging) router.put('/admin/update/:belongingId', perm.requires('updateabelonging'), belongingController.updateBelonging) diff --git a/components/belonging/belongingService.js b/components/belonging/belongingService.js index a2643bb..c5311fb 100644 --- a/components/belonging/belongingService.js +++ b/components/belonging/belongingService.js @@ -1,5 +1,6 @@ const Belonging = require('./belongingModel') const AppError = require('../../globalErrorHandler/AppError') +const { client } = require('../../setup/typesense') module.exports.getAllBelongings = async () => { return Belonging.find({}, null, null) @@ -38,3 +39,72 @@ module.exports.deleteBelonging = async (belongingId) => { return null } + +const ensureCollectionExists = async () => { + const collections = await client.collections().retrieve() + const collectionExists = collections.some((collection) => collection.name === 'belongings') + + if (collectionExists) { + await client.collections('belongings').delete() + } + + const schema = getTypeSenseSchema() + + await client.collections().create(schema) + console.log('Collection "belongings" created successfully') +} +const indexAllBelongings = async () => { + const batchSize = 20 + const count = await Product.countDocuments({isTrash: false, isActive: true, isHidden: false}, null) + const rounds = Math.ceil(count / batchSize) + + await ensureCollectionExists() + + for (let i = 1; i <= rounds; i++) { + console.log(`Indexing ${batchSize} products, Round ${i}`) + + const belongings = await Belonging.find({}, null, null) + .populate('category') + .skip((i - 1) * batchSize) + .limit(batchSize) + + let documents = [] + + for (const belonging of belongings) { + + const data = { + } + + for (let feature of product.features) { + if (feature.definition && feature.value) { + data[feature.definition.englishName] = feature.value + } + } + + documents.push(data) + } + + await client.collections('belonging').documents().import(documents, {action: 'emplace'}) + } + + return {totalCount: count} +} + + +const getTypeSenseSchema = () => { + return { + name: 'belongings', + enable_nested_fields: true, + fields: [ + { name: '_id', type: 'string', facet: false, infix: false }, + { name: 'projectCode', type: 'string', facet: false, infix: true }, + { name: 'belongingCode', type: 'string', facet: true }, + { name: 'category', type: 'string[]', facet: true }, + { name: 'nativeCategory', type: 'string[]', facet: false }, + { name: 'documents', type: 'string[]', facet: false }, + { name: 'description', type: 'string', facet: false }, + { name: 'coordinates', type: 'geopoint' }, + ], + default_sorting_field: 'createdAt', + } +} \ No newline at end of file diff --git a/setup/typesense.js b/setup/typesense.js new file mode 100644 index 0000000..ebe7e24 --- /dev/null +++ b/setup/typesense.js @@ -0,0 +1,15 @@ +const TypeSense = require('typesense') + +let client = new TypeSense.Client({ + nodes: [ + { + host: process.env.TYPE_SENSE_URL, + port: '443', + protocol: 'httpS', + }, + ], + apiKey: process.env.TYPE_SENSE_API_KEY, + connectionTimeoutSeconds: 2, +}) + +module.exports = { client } diff --git a/utils/TsApiFeatures.js b/utils/TsApiFeatures.js new file mode 100644 index 0000000..6f81157 --- /dev/null +++ b/utils/TsApiFeatures.js @@ -0,0 +1,194 @@ +const axios = require('axios') +const { fi } = require('@faker-js/faker') +class TsApiFeatures { + constructor(client, collectionName, queryString) { + this.client = client + this.collectionName = collectionName + this.queryString = queryString + this.queryOptions = {} + } + + search() { + // Set `q` to provided search term or default to `*` for all documents + this.queryOptions.q = this.queryString.query || '*' + + // Set `query_by` to specified fields or default to `*` + this.queryOptions.query_by = this.queryString.query_by || 'name' + + return this + } + + filter() { + let filters = [] + let rawKeys = [] + + // Add any additional filter conditions from queryString, if needed + Object.keys(this.queryString).forEach((key) => { + let operatorConditions = [] + let normalValues = [] + if ( + [ + 'page', + 'sort', + 'limit', + 'fields', + 'per_page', + 'filter', + 'query', + 'facet_by', + 'query_by', + 'highlight_full_fields', + ].includes(key) + ) + return + + let value = this.queryString[key] + rawKeys.push(key) + if (!Array.isArray(value)) { + value = [value] + } + if (/\b(gte|gt|lte|lt|ne|eq)\b/.test(value)) { + const operatorMap = { gte: '>=', gt: '>', lte: '<=', lt: '<', ne: '!=', eq: '=' } + const match = value.match(/\b(gte|gt|lte|lt|ne|eq)\b/) + if (match) { + const operator = operatorMap[match[0]] + const cleanValue = value.replace(match[0], '').trim() + operatorConditions.push(`${key}${operator}${cleanValue}`) + } + } else { + normalValues.push(value) + } + + if (operatorConditions.length > 0 && normalValues.length > 0) { + // Add all operator conditions + operatorConditions.forEach((cond) => filters.push(cond)) + + // Add the normal values condition + if (normalValues.length === 1) { + filters.push(`${key}:=${normalValues[0]}`) + } else { + filters.push(`${key}:=[${normalValues.join(',')}]`) + } + } else if (operatorConditions.length > 0) { + // Only operators + operatorConditions.forEach((cond) => filters.push(cond)) + } else { + // Only normal values + if (normalValues.length === 1) { + filters.push(`${key}:=${normalValues[0]}`) + } else if (normalValues.length > 1) { + filters.push(`${key}:=[${normalValues.join(',')}]`) + } + } + }) + + // Join all conditions for TypeSense filter + if (filters.length) { + this.queryOptions.filters = filters.join(' && ') + } + const searchFilters = [] + rawKeys.forEach((value, index) => { + const tempArray = JSON.parse(JSON.stringify(filters)) + tempArray.splice(index, 1) + searchFilters.push(tempArray.join(' && ')) + }) + this.filterKeys = rawKeys + this.searchFilters = searchFilters + return this + } + + sort() { + if (this.queryString.sort) { + this.queryOptions.sort_by = this.queryString.sort.split(',').join(',') + } else { + this.queryOptions.sort_by = 'createdAt:desc' + } + + return this + } + + limitFields() { + if (this.queryString.fields) { + this.queryOptions.include_fields = this.queryString.fields.split(',').join(',') + } else { + this.queryOptions.exclude_fields = '__v' + } + + return this + } + + facet() { + // Set `facet_by` based on the queryString, if available + if (this.queryString.facet_by) { + this.queryOptions.facet_by = this.queryString.facet_by + this.queryOptions.highlight_full_fields = this.queryString.highlight_full_fields + this.queryOptions.exhaustive_search = true + } + + return this + } + + paginate() { + const page = parseInt(this.queryString.page) || 1 + const per_page = parseInt(this.queryString.per_page) || 20 + + this.queryOptions.page = page + this.queryOptions.per_page = per_page + + return this + } + + async execute() { + const searches = [ + { + collection: this.collectionName, + exhaustive_search: this.queryOptions.exhaustive_search, + query_by: this.queryOptions.query_by, + filter_by: this.queryOptions.filters, + highlight_full_fields: this.queryOptions.highlight_full_fields, + q: this.queryOptions.q, + facet_by: this.queryOptions.facet_by, + max_facet_values: 100, + page: this.queryOptions.page, + per_page: this.queryOptions.per_page, + }, + ] + this.filterKeys.forEach((value, index) => { + searches.push({ + collection: this.collectionName, + exhaustive_search: this.queryOptions.exhaustive_search, + query_by: this.queryOptions.query_by, + filter_by: this.searchFilters[index], + highlight_full_fields: this.queryOptions.highlight_full_fields, + q: this.queryOptions.q, + facet_by: value, + max_facet_values: 100, + page: this.queryOptions.page, + per_page: this.queryOptions.per_page, + }) + }) + const config = { + url: 'https://' + process.env.TYPE_SENSE_URL + '/multi_search?x-typesense-api-key=' + process.env.TYPE_SENSE_API_KEY, + method: 'POST', + data: { + searches, + }, + } + + const request = await axios(config) + const results = request.data.results[0] + let facet_counts = results.facet_counts + this.filterKeys.forEach((value, index) => { + const objIndex = facet_counts.findIndex((item) => item.field_name === value) + facet_counts[objIndex] = request.data.results[index + 1].facet_counts[0] + }) + return { + facets: facet_counts, + items: results.hits.map((hit) => hit.document), + totalCount: results.found, + pagesCount: Math.ceil(results.found / this.queryOptions.per_page), + } + } +} + +module.exports = TsApiFeatures From f12320bc82ff23326c7f4a7da36bb5d7d52df03a Mon Sep 17 00:00:00 2001 From: kaveh Date: Tue, 31 Dec 2024 22:50:37 +0330 Subject: [PATCH 2/4] ADD: Typesense to docker compose --- docker-compose.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 2d0aab4..34e0f59 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -37,7 +37,20 @@ services: ports: - 6379:6379 + typesense: + container_name: typesense + image: typesense/typesense:27.1 + command: ./typesense-server + ports: + - "8108:8108" + environment: + TYPESENSE_API_KEY: vYQq7pTgl6ACoRy + TYPESENSE_DATA_DIR: /data + volumes: + - bm_typesense:/data + volumes: bm_mongo: bm_redis: bm_minio: + bm_typesense: From 25a69783464dd14f39e363a04c501062235c7e76 Mon Sep 17 00:00:00 2001 From: kaveh Date: Thu, 2 Jan 2025 16:00:38 +0330 Subject: [PATCH 3/4] ADD: Description to belongingModel ADD: Typesense Index All function --- components/belonging/belongingModel.js | 8 ++----- components/belonging/belongingService.js | 30 ++++++++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/components/belonging/belongingModel.js b/components/belonging/belongingModel.js index b8d9a8c..9214a71 100644 --- a/components/belonging/belongingModel.js +++ b/components/belonging/belongingModel.js @@ -9,15 +9,11 @@ const belongingSchema = new mongoose.Schema( required: true, unique: true, }, - belongingCategory: { + category: { type: ObjectID, ref: 'Category', }, - projectCode: { - type: String, - required: true, - default: false, - }, + description: String, relatedProjects: [String], location: { type: { diff --git a/components/belonging/belongingService.js b/components/belonging/belongingService.js index c5311fb..fa2f162 100644 --- a/components/belonging/belongingService.js +++ b/components/belonging/belongingService.js @@ -2,15 +2,15 @@ const Belonging = require('./belongingModel') const AppError = require('../../globalErrorHandler/AppError') const { client } = require('../../setup/typesense') -module.exports.getAllBelongings = async () => { +const getAllBelongings = async () => { return Belonging.find({}, null, null) } -module.exports.createBelonging = async (data) => { +const createBelonging = async (data) => { return Belonging.create(data, null) } -module.exports.getBelonging = async (belongingId) => { +const getBelonging = async (belongingId) => { const belonging = await Belonging.findById(belongingId, null, null) if (!belonging) { @@ -20,7 +20,7 @@ module.exports.getBelonging = async (belongingId) => { return belonging } -module.exports.updateBelonging = async (belongingId, data) => { +const updateBelonging = async (belongingId, data) => { const belonging = await Belonging.findOneAndUpdate({ _id: belongingId }, data, { new: true }) if (!belonging) { @@ -30,7 +30,7 @@ module.exports.updateBelonging = async (belongingId, data) => { return belonging } -module.exports.deleteBelonging = async (belongingId) => { +const deleteBelonging = async (belongingId) => { const belonging = await Belonging.findByIdAndUpdate(belongingId, { isActive: false }, { new: true }) if (!belonging) { @@ -73,12 +73,14 @@ const indexAllBelongings = async () => { for (const belonging of belongings) { const data = { - } - - for (let feature of product.features) { - if (feature.definition && feature.value) { - data[feature.definition.englishName] = feature.value - } + _id: belonging._id, + isActive: belonging.isActive, + belongingCode: belonging.belongingCode, + category: belonging.category.map( x=> x.englishName), + nativeCategory: belonging.category.map( x=> x.nativeName), + documents: belonging.documents, + description: belonging.description, + coordinates: belonging.location.coordinates } documents.push(data) @@ -97,7 +99,7 @@ const getTypeSenseSchema = () => { enable_nested_fields: true, fields: [ { name: '_id', type: 'string', facet: false, infix: false }, - { name: 'projectCode', type: 'string', facet: false, infix: true }, + { name: 'isActive', type: 'bool', facet: false, infix: true }, { name: 'belongingCode', type: 'string', facet: true }, { name: 'category', type: 'string[]', facet: true }, { name: 'nativeCategory', type: 'string[]', facet: false }, @@ -107,4 +109,6 @@ const getTypeSenseSchema = () => { ], default_sorting_field: 'createdAt', } -} \ No newline at end of file +} + +module.exports = {indexAllBelongings, getBelonging, getAllBelongings, createBelonging, deleteBelonging} \ No newline at end of file From 1021774c893274f10edba564cd8ed63172311aef Mon Sep 17 00:00:00 2001 From: kaveh Date: Thu, 2 Jan 2025 16:45:16 +0330 Subject: [PATCH 4/4] ADD: Server urls ADD: SearchBelongings function REMOVE: API from docker compose (run it manually) ADD: Required packages REMOVE: Redundant import from TsApiFeatures --- components/belonging/belongingController.js | 9 ++ components/belonging/belongingService.js | 15 ++- config/.env | 8 +- docker-compose.yml | 27 +++-- package-lock.json | 121 ++++++++++++++++++++ package.json | 2 + utils/TsApiFeatures.js | 1 - 7 files changed, 162 insertions(+), 21 deletions(-) diff --git a/components/belonging/belongingController.js b/components/belonging/belongingController.js index 0e86a51..5c57c31 100644 --- a/components/belonging/belongingController.js +++ b/components/belonging/belongingController.js @@ -1,6 +1,15 @@ const catchAsync = require('../../globalErrorHandler/catchAsync') const belongingService = require('./belongingService') +exports.searchBelongings = catchAsync(async (req, res, next) => { + const {facets, belongings, totalCount, pagesCount} = await belongingService.searchBelongings() + + res.status(200).json({ + status: 'success', + facets, belongings, totalCount, pagesCount + }) +}) + exports.indexAllBelongings = catchAsync(async (req, res, next) => { const { indexedCount } = await belongingService.indexAllBelongings() diff --git a/components/belonging/belongingService.js b/components/belonging/belongingService.js index fa2f162..6d576f1 100644 --- a/components/belonging/belongingService.js +++ b/components/belonging/belongingService.js @@ -1,6 +1,7 @@ const Belonging = require('./belongingModel') const AppError = require('../../globalErrorHandler/AppError') const { client } = require('../../setup/typesense') +const TsApiFeatures = require("../../utils/TsApiFeatures"); const getAllBelongings = async () => { return Belonging.find({}, null, null) @@ -53,15 +54,23 @@ const ensureCollectionExists = async () => { await client.collections().create(schema) console.log('Collection "belongings" created successfully') } + +const searchBelongings = async (inputQuery) => { + const tsFeatures = new TsApiFeatures(client, 'belongings', inputQuery).filter().facet().sort().limitFields().paginate().search() + + let {facets, items, totalCount, pagesCount} = await tsFeatures.execute() + + return {facets, belongings: items, totalCount, pagesCount} +} const indexAllBelongings = async () => { const batchSize = 20 - const count = await Product.countDocuments({isTrash: false, isActive: true, isHidden: false}, null) + const count = await Belonging.countDocuments({}, null) const rounds = Math.ceil(count / batchSize) await ensureCollectionExists() for (let i = 1; i <= rounds; i++) { - console.log(`Indexing ${batchSize} products, Round ${i}`) + console.log(`Indexing ${batchSize} belongings, Round ${i}`) const belongings = await Belonging.find({}, null, null) .populate('category') @@ -111,4 +120,4 @@ const getTypeSenseSchema = () => { } } -module.exports = {indexAllBelongings, getBelonging, getAllBelongings, createBelonging, deleteBelonging} \ No newline at end of file +module.exports = {indexAllBelongings, getBelonging, getAllBelongings, createBelonging, deleteBelonging, searchBelongings} \ No newline at end of file diff --git a/config/.env b/config/.env index 4642e61..e76abc7 100644 --- a/config/.env +++ b/config/.env @@ -1,7 +1,9 @@ -MONGODB_URL=mongodb://admin:password@mongodb:27018/marbm?authSource=admin +MONGODB_URL=mongodb://admin:*135792468*@localhost:27018/marbm?authSource=admin NODE_ENV=development PORT=3000 JWT_SECRET=Y@RJ$WT!@K0NS@MPL! -REDIS_URL= redis://:135792468@redis:6379/0 +REDIS_URL= redis://:135792468@localhost:6379/0 LOGIN_ATTEMPT_LIMIT=5 -SUPER_ADMIN_ROLE_ID=676afd6d076b286f9395b05f \ No newline at end of file +SUPER_ADMIN_ROLE_ID=676afd6d076b286f9395b05f +TYPE_SENSE_URL=localhost:8108 +TYPE_SENSE_API_KEY=vYQq7pTgl6ACoRy \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 834e5a7..3c87d83 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,14 +1,14 @@ services: - api-server: - build: ./ - ports: - - 3000:3000 - environment: - - MONGODB_URL=mongodb://admin:*135792468*@mongo:27017/marbm?authSource=admin - - NODE_ENV=development - - PORT=3000 - - REDIS_URL=redis://:135792468@redis:6379/0 - minio-server: +# api-server: +# build: ./ +# ports: +# - 3000:3000 +# environment: +# - MONGODB_URL=mongodb://admin:*135792468*@mongo:27017/marbm?authSource=admin +# - NODE_ENV=development +# - PORT=3000 +# - REDIS_URL=redis://:135792468@redis:6379/0 + bm_minio: image: minio/minio ports: - 9000:9000 @@ -19,7 +19,7 @@ services: volumes: - bm_minio:/upload/ command: server /data --console-address ":9001" - mongo: + bm_mongo: image: mongo:8.0 ports: - 27018:27017 @@ -30,7 +30,7 @@ services: volumes: - bm_mongo:/data/db - redis: + bm_redis: image: redis command: redis-server --save 20 1 --loglevel warning --requirepass 135792468 volumes: @@ -38,8 +38,7 @@ services: ports: - 6379:6379 - typesense: - container_name: typesense + bm_typesense: image: typesense/typesense:27.1 command: ./typesense-server ports: diff --git a/package-lock.json b/package-lock.json index 7d63afc..6469134 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "axios": "^1.7.9", "bcryptjs": "^2.4.3", "body-parser": "^1.20.3", "chalk": "^5.3.0", @@ -21,12 +22,25 @@ "mongoose": "^8.9.0", "mongoose-sequence": "^6.0.1", "redis": "^4.7.0", + "typesense": "^1.8.2", "validator": "^13.12.0" }, "devDependencies": { "nodemon": "^3.1.9" } }, + "node_modules/@babel/runtime": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "peer": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@mongodb-js/saslprep": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz", @@ -144,6 +158,21 @@ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -303,6 +332,17 @@ "node": ">=0.10.0" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -401,6 +441,14 @@ "ms": "2.0.0" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -585,6 +633,38 @@ "node": ">= 0.8" } }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -888,6 +968,18 @@ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" }, + "node_modules/loglevel": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", + "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, "node_modules/math-intrinsics": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.0.0.tgz", @@ -1255,6 +1347,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -1321,6 +1418,9 @@ "version": "4.7.0", "resolved": "https://registry.npmjs.org/redis/-/redis-4.7.0.tgz", "integrity": "sha512-zvmkHEAdGMn+hMRXuMBtu4Vo5P6rHQjLoHftu+lBqq8ZTA3RCVC/WzD790bkKKiNFp7d5/9PcSD19fJyyRvOdQ==", + "workspaces": [ + "./packages/*" + ], "dependencies": { "@redis/bloom": "1.2.0", "@redis/client": "1.6.0", @@ -1330,6 +1430,12 @@ "@redis/time-series": "1.1.0" } }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "peer": true + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -1585,6 +1691,21 @@ "node": ">= 0.6" } }, + "node_modules/typesense": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/typesense/-/typesense-1.8.2.tgz", + "integrity": "sha512-aBpePjA99Qvo+OP2pJwMpvga4Jrm1Y2oV5NsrWXBxlqUDNEUCPZBIksPv2Hq0jxQxHhLLyJVbjXjByXsvpCDVA==", + "dependencies": { + "axios": "^1.6.0", + "loglevel": "^1.8.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@babel/runtime": "^7.23.2" + } + }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", diff --git a/package.json b/package.json index 36fa04b..7e1e92e 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "license": "ISC", "description": "", "dependencies": { + "axios": "^1.7.9", "bcryptjs": "^2.4.3", "body-parser": "^1.20.3", "chalk": "^5.3.0", @@ -23,6 +24,7 @@ "mongoose": "^8.9.0", "mongoose-sequence": "^6.0.1", "redis": "^4.7.0", + "typesense": "^1.8.2", "validator": "^13.12.0" }, "devDependencies": { diff --git a/utils/TsApiFeatures.js b/utils/TsApiFeatures.js index 6f81157..222f499 100644 --- a/utils/TsApiFeatures.js +++ b/utils/TsApiFeatures.js @@ -1,5 +1,4 @@ const axios = require('axios') -const { fi } = require('@faker-js/faker') class TsApiFeatures { constructor(client, collectionName, queryString) { this.client = client