You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Node+MongoDB中实现带分页的多条件搜索API端点

Great start on your search endpoint! Let's fix the bottlenecks and add the pagination/limit features you need. Here's a step-by-step breakdown and improved implementation:

First, Let's Address the Issues in Your Current Code

  1. When no search parameters (fn/ln/cp) are provided, your empty $or array will return all Profile documents (probably not what you want).
  2. You're using Profile.find() with a callback incorrectly—Mongoose returns a Query object here, not the actual results, and your await usage is causing redundant database queries.
  3. Error handling doesn't respect custom status codes (you're defaulting all errors to 500, even 404 cases).

Improved Search Controller Implementation

// Models
const { Profile } = require("../models");
// Error handling
const { ErrorHandlers } = require("../utilities");

const SearchController = {
  async getQuery(req, res) {
    try {
      // Destructure query params with default values for pagination
      const { fn, ln, cp, page = 1, limit = 10 } = req.query;
      
      // 1. Block empty search requests
      if (!fn && !ln && !cp) {
        throw new ErrorHandlers.ErrorHandler(
          400,
          "Please provide at least one search parameter: fn (firstname), ln (surname), or cp (company)"
        );
      }

      // 2. Build dynamic query conditions
      const queryConditions = [];
      if (fn) {
        // Use exact match as requested; swap to regex below for partial/caseless search
        queryConditions.push({ firstname: fn });
      }
      if (ln) {
        queryConditions.push({ surname: ln });
      }
      if (cp) {
        // Match profiles where ANY experience entry has this company
        queryConditions.push({ "experience.company": cp });
      }

      const query = { $or: queryConditions };

      // 3. Parse pagination values (query params are strings by default)
      const pageNum = parseInt(page);
      const limitNum = parseInt(limit);
      const skip = (pageNum - 1) * limitNum;

      // 4. Execute query with pagination
      const searchResult = await Profile.find(query)
        .skip(skip)
        .limit(limitNum);

      // 5. Handle no matching results
      if (searchResult.length === 0) {
        throw new ErrorHandlers.ErrorHandler(
          404,
          "No profiles match your search criteria"
        );
      }

      // 6. Return results with helpful pagination metadata
      res.status(200).json({
        page: pageNum,
        limit: limitNum,
        totalResults: searchResult.length,
        data: searchResult
      });

    } catch (err) {
      // Use custom error status code if available, else default to 500
      res.status(err.statusCode || 500).json({ message: err.message });
    }
  }
};

module.exports = SearchController;

Key Enhancements Explained

1. Empty Query Protection

We immediately return a 400 error if no search parameters are provided, preventing accidental full-database scans.

2. Flexible Pagination

  • page and limit are optional query params with sensible defaults (page 1, 10 results per page)
  • skip calculates how many documents to skip to reach the requested page
  • Chain .skip() and .limit() to the Mongoose query to apply pagination efficiently

3. Optional: Partial/Case-Insensitive Search

If you want to allow partial matches (e.g., fn=jo finds "Joe" and "Joseph") or case-insensitive searches, replace the exact match lines with regex:

// For case-insensitive partial match on firstname
queryConditions.push({ firstname: { $regex: fn, $options: 'i' } });

4. Better Error Handling

We now respect the status code from your custom ErrorHandler, so 400/404 errors return the correct HTTP status instead of defaulting to 500.

5. Metadata for Frontend

Including page, limit, and totalResults in the response makes it easier for frontend developers to build pagination controls. If you want the total number of matching documents (not just the current page count), add a separate count query:

const totalMatching = await Profile.countDocuments(query);
// Then add totalMatching to your response JSON

内容的提问来源于stack exchange,提问作者Jakub

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 15:47:45