问题描述
我正在尝试创建一个带有过滤器的搜索 api,我的架构中有多个对象 ref,我希望 api 在所有字段中搜索匹配的文本,如果选择了任何过滤器,则发送带有搜索查询和过滤的结果数据。
我的服务架构如下:
const ServiceSchema = new mongoose.Schema(
{
title: {
type: String,trim: true,required: [true,'Please add a Service title'],},slug: String,description: {
type: String,'Please add a description'],openingDate: {
type: String,'Please add opening date'],serviceCost: {
type: Number,'Please add service cost'],minimumSkill: {
type: String,'Please add a minimum skill'],enum: ['beginner','intermediate','advanced'],atHomeService: {
type: Boolean,default: false,healthscore: {
type: String,default: 'B',happyHour: {
type: Boolean,takeReservation: {
type: Boolean,offersTakeout: {
type: Boolean,photo: [
{
type: String,default: 'no-photo.jpg',],logo: {
type: String,views: {
type: Number,default: 0,address: { type: String,'Please enter address'] },location: {
type: {
type: String,// Don't do `{ location: { type: String } }`
enum: ['Point'],// 'location.type' must be 'Point'
},coordinates: {
type: [Number],index: '2dsphere',formattedAddress: String,street: String,city: String,state: String,zipcode: String,country: String,averagerating: {
type: Number,min: [1,'rating must be atleast 1'],max: [10,'rating must can not more then 10'],email: {
type: String,match: [
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,'Please enter a valid email',phone: {
type: String,maxlength: [20,'Phone number can not be longer then 20 characters'],keywords: [
{
type: String,default: 'general',isActive: {
type: Boolean,isApproved: {
type: Boolean,category: {
type: mongoose.Schema.ObjectId,ref: 'Category','Please category from the list'],business: {
type: mongoose.Schema.ObjectId,ref: 'Business',required: true,user: {
type: mongoose.Schema.ObjectId,ref: 'User',createdAt: {
type: Date,default: Date.Now,{
toJSON: { virtuals: true },toObject: { virtuals: true },}
);
ServiceSchema.index({ '$**': 'text' });
ServiceSchema.plugin(textSearch);
module.exports = mongoose.model('Service',ServiceSchema);
如您所见,我在其中包含了 Category Object 引用,并且其中可能还有 2 个引用。
我还添加了一个包 mongoose-partial-full-search 并在其中作为插件调用。它可以帮助我返回与部分或全文匹配的数据数组。我无法向其中添加更多过滤器查询。
所以我需要制作可以返回过滤数据的自定义控制器。
例如:如果用户输入 tour
字并从下拉列表中选择位置/城市/地区,或输入邮政编码(我用来在提供的半径内查找数据),或选择特定类别,它应该返回所有过滤的分页数据。
我有一个中间件,它返回简单的排序、属性过滤器、分页数据。我想在同一个中间件中添加多个过滤器,以便我可以在任何地方重复使用。
我的中间件代码:
const advancedResults = (model,populate,populate2) => async (req,res,next) => {
let query;
// copy req.query
const reqQuery = { ...req.query };
// Fields to exclude
const removeFields = ['select','sort','page','limit'];
// Loop over removeFields and delete them from reqQuery
removeFields.forEach((param) => delete reqQuery[param]);
// Create query string
let queryStr = JSON.stringify(reqQuery);
// Create operators ($gt,$gte,etc)
queryStr = queryStr.replace(/\b(gt|gte|lt|lte|in)\b/g,(match) => `$${match}`);
if (req.query.search) {
console.log(req.query.search);
if (req.query.in) {
const findIn = req.query.in;
console.log(findIn);
queryStr = `{ "${findIn}": { "$regex": "${new RegExp(req.query.search)}"}}`;
console.log(queryStr);
query = model.find(JSON.parse(queryStr));
} else {
const searchQ = req.query.search.toLowerCase();
query = model.find({
slug: {
$regex: new RegExp(searchQ),});
}
} else {
// Finding resource
query = model.find(JSON.parse(queryStr));
}
// Select Fields
if (req.query.select) {
const fields = req.query.select.split(',').join(' ');
query = query.select(fields);
}
// Sort
if (req.query.sort) {
const sortBy = req.query.sort.split(',').join(' ');
query = query.sort(sortBy);
} else {
query = query.sort('-createdAt');
}
// Pagination
const page = parseInt(req.query.page,10) || 1;
const limit = parseInt(req.query.limit,10) || 25;
const startIndex = (page - 1) * limit;
const endindex = page * limit;
const total = await model.countDocuments();
query = query.skip(startIndex).limit(limit);
if (populate) {
query = query.populate(populate);
}
if (populate2) {
query = query.populate(populate2);
}
// Executing query
const results = await query;
// Pagination result
const pagination = {};
if (endindex < total) {
pagination.next = {
page: page + 1,limit,};
}
if (startIndex > 0) {
pagination.prev = {
page: page - 1,};
}
res.advancedResults = {
success: true,count: results.length,pagination,data: results,};
next();
};
module.exports = advancedResults;
注意:上面代码中的以下部分(中间件代码)没有按预期工作,它只是像正常查找一样工作。
if (req.query.search) {
console.log(req.query.search);
if (req.query.in) {
const findIn = req.query.in;
console.log(findIn);
queryStr = `{ "${findIn}": { "$regex": "${new RegExp(req.query.search)}"}}`;
console.log(queryStr);
query = model.find(JSON.parse(queryStr));
} else {
const searchQ = req.query.search.toLowerCase();
query = model.find({
slug: {
$regex: new RegExp(searchQ),});
}
} else {
// Finding resource
query = model.find(JSON.parse(queryStr));
}
如果用户只输入文本和位置,它应该在关键字、名称/标题和类别中找到匹配的文本。例如,如果用户在搜索中输入“tour”并在 location 中输入“Texas”,它应该在名称/标题中找到文本,并且还返回具有文本匹配类别的服务,因为它们的标题与德克萨斯州可用的用户文本输入“tour”相匹配。 稍后用户可以设置“isActive”或 HomeService 等布尔值并设置在 200 半径内可用。
任何有关代码或教程的帮助都可以,并且会很有帮助,因为我对进行深度搜索和复杂的单个 api 没有想法。我可以附加到任何路由以支持所有过滤器和东西的中间件。
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)