feat: add MongoDB search method, endpoints, and settings module
deploy-chabokan / deploy (push) Canceled after 0s
deploy-chabokan / deploy (push) Canceled after 0s
This commit is contained in:
@@ -90,15 +90,136 @@ const ensureCollectionExists = async (forceRecreate = false) => {
|
||||
}
|
||||
}
|
||||
|
||||
const settingService = require('../setting/settingService')
|
||||
const mongoose = require('mongoose')
|
||||
|
||||
const searchPropertiesMongo = async (inputQuery = {}) => {
|
||||
const page = parseInt(inputQuery.page) || 1
|
||||
const per_page = parseInt(inputQuery.per_page) || parseInt(inputQuery.limit) || 20
|
||||
const mongoQuery = { isActive: { $ne: false } }
|
||||
|
||||
const q = (inputQuery.query || inputQuery.search || inputQuery.q || '').trim()
|
||||
if (q && q !== '*') {
|
||||
const searchRegex = { $regex: q, $options: 'i' }
|
||||
const orConditions = [
|
||||
{ name: searchRegex },
|
||||
{ nationalGeoCode: searchRegex },
|
||||
{ uniqueId: searchRegex },
|
||||
{ serialNumber: searchRegex },
|
||||
{ ownerName: searchRegex },
|
||||
{ registrationPlate: searchRegex },
|
||||
{ description: searchRegex },
|
||||
{ address: searchRegex },
|
||||
{ tags: searchRegex },
|
||||
{ relatedProjects: searchRegex },
|
||||
]
|
||||
|
||||
if (!isNaN(q) && q !== '') {
|
||||
orConditions.push({ propertyCode: Number(q) })
|
||||
}
|
||||
|
||||
if (mongoose.Types.ObjectId.isValid(q)) {
|
||||
orConditions.push({ _id: q })
|
||||
}
|
||||
|
||||
mongoQuery.$or = orConditions
|
||||
}
|
||||
|
||||
if (inputQuery.category && mongoose.Types.ObjectId.isValid(inputQuery.category)) {
|
||||
mongoQuery.category = inputQuery.category
|
||||
}
|
||||
|
||||
if (inputQuery.status && mongoose.Types.ObjectId.isValid(inputQuery.status)) {
|
||||
mongoQuery.status = inputQuery.status
|
||||
}
|
||||
|
||||
let sortObj = { createdAt: -1 }
|
||||
if (inputQuery.sort) {
|
||||
if (typeof inputQuery.sort === 'string') {
|
||||
const parts = inputQuery.sort.split(':')
|
||||
const field = parts[0]
|
||||
const order = parts[1] === 'asc' ? 1 : -1
|
||||
sortObj = { [field]: order }
|
||||
} else if (typeof inputQuery.sort === 'object') {
|
||||
sortObj = inputQuery.sort
|
||||
}
|
||||
}
|
||||
|
||||
const totalCount = await Property.countDocuments(mongoQuery)
|
||||
const properties = await Property.find(mongoQuery)
|
||||
.populate('category')
|
||||
.populate('status')
|
||||
.sort(sortObj)
|
||||
.skip((page - 1) * per_page)
|
||||
.limit(per_page)
|
||||
|
||||
const items = properties.map((prop) => {
|
||||
const combinedTags = Array.isArray(prop.tags) ? prop.tags.join(' ') : (prop.tags || '')
|
||||
return {
|
||||
_id: prop._id.toString(),
|
||||
id: prop.propertyCode ? prop.propertyCode.toString() : prop._id.toString(),
|
||||
propertyCode: prop.propertyCode,
|
||||
name: prop.name || '',
|
||||
category: prop.category?.englishName || (typeof prop.category === 'string' ? prop.category : ''),
|
||||
nativeCategory: prop.category?.nativeName || '',
|
||||
categoryObj: prop.category,
|
||||
status: prop.status,
|
||||
imageUrl: prop.mainImageUrls?.[0] || 'https://cloud.mbmanager.ir/images/item-no-image.png',
|
||||
mainImageUrls: prop.mainImageUrls || [],
|
||||
documents: prop.documentUrls || [],
|
||||
documentUrls: prop.documentUrls || [],
|
||||
description: prop.description || '',
|
||||
nationalGeoCode: prop.nationalGeoCode || '',
|
||||
tags: combinedTags,
|
||||
dong: prop.dong,
|
||||
ownerName: prop.ownerName || '',
|
||||
registrationPlate: prop.registrationPlate || '',
|
||||
uniqueId: prop.uniqueId || '',
|
||||
serialNumber: prop.serialNumber || '',
|
||||
coordinates: prop.location?.coordinates || [0, 0],
|
||||
location: prop.location,
|
||||
createdAt: Math.floor(new Date(prop.createdAt || Date.now()).getTime() / 1000),
|
||||
updatedAt: prop.updatedAt,
|
||||
isActive: prop.isActive !== false,
|
||||
features: prop.features || [],
|
||||
}
|
||||
})
|
||||
|
||||
const pagesCount = Math.ceil(totalCount / per_page) || 1
|
||||
|
||||
return {
|
||||
facets: [],
|
||||
belongings: items,
|
||||
properties: items,
|
||||
totalCount,
|
||||
pagesCount,
|
||||
searchEngine: 'mongo',
|
||||
}
|
||||
}
|
||||
|
||||
const searchProperties = async (inputQuery) => {
|
||||
await ensureCollectionExists(false)
|
||||
inputQuery = updateInputQuery(inputQuery, {})
|
||||
try {
|
||||
const setting = await settingService.getSearchSettingByPage('properties')
|
||||
if (setting && setting.searchMethod === 'mongo') {
|
||||
return searchPropertiesMongo(inputQuery)
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Failed to fetch search setting for properties, attempting typesense:', e.message)
|
||||
}
|
||||
|
||||
const tsFeatures = new TsApiFeatures(client, 'properties', inputQuery).filter().facet().sort().limitFields().paginate().search()
|
||||
try {
|
||||
await ensureCollectionExists(false)
|
||||
const updatedQuery = updateInputQuery({ ...inputQuery }, {})
|
||||
|
||||
let { facets, items, totalCount, pagesCount } = await tsFeatures.execute()
|
||||
const tsFeatures = new TsApiFeatures(client, 'properties', updatedQuery).filter().facet().sort().limitFields().paginate().search()
|
||||
|
||||
return { facets, belongings: items, totalCount, pagesCount }
|
||||
let { facets, items, totalCount, pagesCount } = await tsFeatures.execute()
|
||||
|
||||
return { facets, belongings: items, properties: items, totalCount, pagesCount, searchEngine: 'typesense' }
|
||||
} catch (err) {
|
||||
console.warn('Typesense search failed for properties, falling back to MongoDB:', err.message)
|
||||
return searchPropertiesMongo(inputQuery)
|
||||
}
|
||||
}
|
||||
|
||||
const updateInputQuery = (inputQuery, extraQueries) => {
|
||||
@@ -111,6 +232,7 @@ const updateInputQuery = (inputQuery, extraQueries) => {
|
||||
return inputQuery
|
||||
}
|
||||
|
||||
|
||||
const indexAllProperties = async () => {
|
||||
const batchSize = 20
|
||||
const count = await Property.countDocuments({ isActive: true }, null)
|
||||
@@ -213,5 +335,7 @@ module.exports = {
|
||||
createProperty,
|
||||
deleteProperty,
|
||||
searchProperties,
|
||||
searchPropertiesMongo,
|
||||
indexOneProperty,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user