Search code examples
javascriptnode.jsmongodbnext.jsmongoose-schema

How to search field's in a MongoDB Schema which has Reference to another Schema?


I want to search product with its name in below DOC Schema. But the products are stored as reference to another Product Schema with _id. Below is the code -

DOC Schema

import mongoose from "mongoose";
import Product from "./Product";

const DocSchema = new mongoose.Schema({
  name: { type: String },
  address: [
    {
      region: { type: String },
      country: { type: String },
      plantlocation: { type: String },
    },
  ],
  products: [
    {
      type: mongoose.Schema.Types.ObjectId,
      ref: "Product",
    },
  ],
});

const Doc = mongoose.models.Doc || mongoose.model("Doc", DocSchema);
export default Doc;

Product Schema


import mongoose from "mongoose";

const ProductSchema = new mongoose.Schema({
  name: { type: String, unique: true },
  address: [
    {
      region: { type: String },
      country: { type: String },
      plantlocation: { type: String },
    },
  ],
  installCapacity: [
    {
      year: { type: Date },
      quantity: { type: Number },
    },
  ],
});

const Product =
  mongoose.models.Product || mongoose.model("Product", ProductSchema);
export default Product;


My API


import Doc from "@/models/Doc";
import connectToMongo from "@/middleware/db";
import catchAsyncErrors from "@/middleware/catchAsyncErrors";
import ErrorHandler from "@/utils/errorhandler";
import { NextApiRequest, NextApiResponse } from "next";
import ApiFeatures from "@/utils/apiFeatures";
import Product from "@/models/Product";

type DataType = {
  docs?: object;
  success?: boolean;
  message?: string;
  error?: {};
};

const handler = catchAsyncErrors(
  async (req: NextApiRequest, res: NextApiResponse<DataType>) => {
    if (req.method != "POST") {
      return res.status(405).json({ success: false, message: "Bad Request" });
    } else {
      try {
        const apiFeatures = new ApiFeatures(
          Doc.find(),
          req.body.query
        ).search(); // Search Class

        const docs = await apiFeatures.query;
        if (!docs || docs.length <= 0) {
          return res
            .status(404)
            .json({ success: false, message: "Docs not found" });
        }
        res.status(200).json({
          success: true,
          message: "Doc data fetched successfully",
          docs,
        });
      } catch (error) {
        console.log(error);
        return new ErrorHandler("Internal Server Error", 500);
      }
    }
  }
);

export default connectToMongo(handler);


MY APIFeature Class


class ApiFeatures {
  query: any;
  queryStr: any;
  constructor(query: any, queryStr: any = "") {
    this.query = query;
    this.queryStr = queryStr;
  }
  /**
   * Builds a search query based on provided criteria
   * @returns this (for method chaining)
   */

  search() {
    let keyword: any =
      this.queryStr.product || this.queryStr.company || this.queryStr.region
        ? {
            $or: [],
          }
        : {};

    if (this.queryStr.product) {
      keyword.$or.push({
        "products.name": {
          $regex: this.queryStr.product,
          $options: "i",
        },
      });
    }

    if (this.queryStr.company) {
      keyword.$or.push({
        name: { $regex: this.queryStr.company, $options: "i" },
      });
    }

    if (this.queryStr.region) {
      keyword.$or.push({
        "address.region": {
          $regex: this.queryStr.region,
          $options: "i",
        },
      });
    }
    this.query = this.query.find(keyword);
    return this;
  }
}

export default ApiFeatures;

I want to search inside DOC Schema with product name I am making a search API using Model.find() method but it fails to find the 'name' field with product ID in DOC schema.

Can some one help me, I want to search Products based on product name in DOC Schema.

Thank You.

I tried Doc.find({// My query }).populate('products') but it simply fetch all products or returns 404 not found.


Solution

  • To populate you can use like this

    await DOC.find().populate("products", "")
    

    either this,

    await DOC.find().populate({path: "products", select: ""})