ADD: TsApiFeatures
ADD: Typesense ADD: Index all belongings router, controller & service
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user