使用过滤器 Mongoose 搜索 Api

问题描述

我正在尝试创建一个带有过滤器的搜索 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 (将#修改为@)