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).populate('category') } const createBelonging = async (data) => { return Belonging.create(data, null) } const getBelonging = async (belongingId) => { let belonging = await Belonging.findById(belongingId, null, null) if (!belonging) { throw new AppError('NoBelongingFound', 404, {}) } const documentUrls = await belonging.getDocumentsSignedUrls() const mainImageUrls = await belonging.getMainImagedSignedUrl() belonging = JSON.parse(JSON.stringify(belonging)) belonging.signedDocumentUrls = documentUrls belonging.signedMainImageUrls = mainImageUrls return belonging } const updateBelonging = async (belongingId, data) => { const belonging = await Belonging.findOneAndUpdate({ _id: belongingId }, data, { new: true }) if (!belonging) { throw new AppError('NoBelongingFound', 404, {}) } return belonging } const deleteBelonging = async (belongingId) => { const belonging = await Belonging.findByIdAndUpdate(belongingId, { isActive: false }, { new: true }) if (!belonging) { throw new AppError('NoBelongingFound', 404, {}) } 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 searchBelongings = async (inputQuery) => { inputQuery = updateInputQuery(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 updateInputQuery = (inputQuery, extraQueries, features) => { for (const [key, value] of Object.entries(extraQueries)) { inputQuery[key] = value } inputQuery.query_by = 'name,unit,tags,description,_id' return inputQuery } const indexAllBelongings = async () => { const batchSize = 20 const count = await Belonging.countDocuments({isActive: true}, null) const rounds = Math.ceil(count / batchSize) await ensureCollectionExists() for (let i = 1; i <= rounds; i++) { console.log(`Indexing ${batchSize} belongings, Round ${i}`) const belongings = await Belonging.find({isActive: true}, null, null) .populate('category') .skip((i - 1) * batchSize) .limit(batchSize) let documents = [] for (const belonging of belongings) { const combinedTags = belonging.tags.join(' '); const data = { _id: belonging._id, isActive: belonging.isActive, // belongingCode: belonging.belongingCode, name: belonging.name, category: belonging.category?.englishName || '', nativeCategory: belonging.category?.nativeName || '', imageUrl: belonging.mainImageUrls[0] || '', documents: belonging.documentUrls || [] , description: belonging.description || '', unit: belonging.unit || '', tags: combinedTags || '', coordinates: belonging.location.coordinates || [0, 0], createdAt: Math.floor(new Date(belonging.createdAt).getTime() / 1000), // Store as timestamp in seconds id: belonging.belongingCode.toString(), } documents.push(data) } await client.collections('belongings').documents().import(documents, { action: 'emplace' }) } return { totalCount: count } } const indexOneBelonging = async (belongingId) => { const belonging = await getBelonging(belongingId) console.log(`Belonging ${belonging._id} indexing`) const combinedTags = belonging.tags.join(' '); const data = { _id: belonging._id, isActive: belonging.isActive, // belongingCode: belonging.belongingCode, name: belonging.name, category: belonging.category?.englishName || '', nativeCategory: belonging.category?.nativeName || '', imageUrl: belonging.mainImageUrls[0] || '', documents: belonging.documentUrls || [] , description: belonging.description || '', unit: belonging.unit || '', tags: combinedTags || '', coordinates: belonging.location.coordinates || [0, 0], createdAt: Math.floor(new Date(belonging.createdAt).getTime() / 1000), // Store as timestamp in seconds id: belonging.belongingCode.toString(), } await client.collections('belongings').documents().upsert(data) } const getTypeSenseSchema = () => { return { name: 'belongings', enable_nested_fields: true, fields: [ { name: '_id', type: 'string', facet: false, infix: false }, { name: 'name', type: 'string', facet: false, infix: true }, // { name: 'belongingCode', type: 'int64', facet: false }, { name: 'category', type: 'string', facet: true, infix: true }, { name: 'nativeCategory', type: 'string', facet: false, infix: true }, { name: 'description', type: 'string', facet: false }, { name: 'unit', type: 'string', facet: false, infix: true }, { name: 'tags', type: 'string', facet: false, infix: true }, { name: 'coordinates', type: 'geopoint' }, { name: 'createdAt', type: 'int64', facet: false }, { name: 'imageUrl', type: 'string', facet: false, infix: true }, ], default_sorting_field: 'createdAt', } } module.exports = { updateBelonging, indexAllBelongings, getBelonging, getAllBelongings, createBelonging, deleteBelonging, searchBelongings, indexOneBelonging }