【问题标题】:req.query.countries Returns Empty Array on Filterreq.query.countries 在过滤器上返回空数组
【发布时间】:2022-10-14 13:17:22
【问题描述】:

请帮忙。 Nodejs 和 Angular 仍然相对较新。我在 Nodejs 上有以下后端代码,我无法通过 req.query.countries 按国家/地区过滤来自 MongoDB 的数据,我终生无法理解这里出了什么问题。在此先感谢您提供任何线索、指示或解决方案。

我的后端代码如下 tourController.js 文件

exports.getToursByCountry = catchAsync(async (req, res, next) => {
let filter = {};
  if (req.query.countries) {
    console.log(req.query.countries);
    filter = { country: req.query.countries.split(',') };
  }

  console.log(filter);

  const tourList = await Tour.find(filter).populate('country');
  console.log(tourList);

  if (!tourList) {
    next(new AppError('Sorry could not retrieve tours by country', 400));
  }

  res.status(200).json({
    status: 'success',
    results: tourList.length,
    data: {
      data: tourList
    }
  });
});

tourModel.js 文件

const mongoose = require('mongoose');
const slugify = require('slugify');
// const User = require('./userModel');

const tourSchema = new mongoose.Schema(
  {
    name: {
      type: String,
      required: [true, 'A tour must have a name'],
      unique: true,
      trim: true,
      maxlength: [
        120,
        'The tour name must have less or equal than 120 characters'
      ],
      minlength: [
        10,
        'The tour name must have more or equal than 10 characters'
      ]
    },
    slug: String,
    country: {
      type: mongoose.Schema.Types.ObjectId,
      ref: 'Country'
    },
    duration: {
      type: Number,
      required: [true, 'A tour must have a duration'],
      validate: {
        validator: val => {
          //'this' only works for the new posting of tour and not on update
          return val < 22;
        },
        message: 'The duration {VALUE} days should be below the 22 days'
      }
    },
    maxGroupPair: {
      type: Number,
      required: [true, 'A tour must have a maximum group pair size']
    },
    singleSupplement: {
      type: Number,
      required: [true, 'A tour must have a single supplment specified']
    },
    difficulty: {
      type: String,
      required: [true, 'A tour must have a difficulty'],
      enum: {
        values: ['easy', 'medium', 'difficult'],
        message: 'Difficulty is either: easy, medium nor difficult'
      },
      match: [
        new RegExp(/^[a-zA-Z\s]+$/),
        '{VALUE} is not valid. Please use only letters'
      ]
    },
    ratingsAverage: {
      type: Number,
      min: [1, 'Rating must be above 1.0'],
      max: [5, 'Rating must be below 5.0'],
      set: val => Math.round(val * 10) / 10
    },
    ratingsQuantity: {
      type: Number,
      default: 0
    },
    price: {
      type: Number,
      required: [true, 'A tour must have a price']
    },
    priceDiscount: {
      type: Number,
      validate: {
        validator: function(val) {
          //'this' only works for the new posting of tour and not on update
          return val < this.price;
        },
        message: 'Discount price {VALUE} should be below the regular price'
      }
    },
    overview: {
      type: String,
      trim: true,
      required: [true, 'A tour must have an overview']
    },
    imageCover: {
      type: String,
      required: [true, 'A tour must have a cover image']
    },
    images: [String],
    minimumAge: {
      type: String,
      required: [true, 'A tour must have a minimum age'],
      maxlength: [
        10,
        'The minimum age must have less or equal than 10 characters'
      ]
    },
    dateCreated: {
      type: Date,
      default: Date.now
    },
    availabilityDates: [String],
    isFeatured: {
      type: Boolean,
      default: false
    },
    secretTour: {
      type: Boolean,
      default: false
    },
    departureLocation: {
      // GeoJSON
      type: {
        type: String,
        default: 'Point',
        enum: ['Point']
      },
      coordinates: [Number],
      town: String,
      decription1: String,
      decription2: String
    },
    locations: [
      {
        type: {
          type: String,
          default: 'Point',
          enum: ['Point']
        },
        Area: String,
        coordinates: [Number],
        image: String,
        overnight: String,
        Meals: String,
        description: String,
        day: [Number]
      }
    ],
    guides: [
      {
        type: mongoose.Schema.ObjectId,
        ref: 'User'
      }
    ]
  },
  {
    toJSON: { virtuals: true },
    toObject: { virtuals: true }
  }
);

// tourSchema.index({ price: 1 });
tourSchema.index({ price: 1, ratingsAverage: -1 });
tourSchema.index({ slug: 1 });
tourSchema.index({ departureLocation: '2dsphere' });

tourSchema.virtual('id').get(function() {
  return this._id.toHexString();
});

tourSchema.set('toJSON', {
  virtuals: true
});

tourSchema.virtual('durationWeek').get(function() {
  return this.duration / 7;
});

// Virtual populate - this approach does not persist the infomation to the database
tourSchema.virtual('reviews', {
  ref: 'Review',
  foreignField: 'tour',
  localField: '_id'
});

//DOCUMENT MIDDLEWARE: runs before .save() and .create() & not on .insertMany
tourSchema.pre('save', function(next) {
  this.slug = slugify(this.name, { lower: true });
  next();
});


//QUERY MIDDLEWARE
tourSchema.pre(/^find/, function(next) {
  this.find({ secretTour: { $ne: true } });

  this.start = Date.now();
  next();
});

tourSchema.pre(/^find/, function(next) {
  //This will work on find method available in mongoose. 'this' calls populate on the current query
  this.populate({
    path: 'guides',
    select: '-__v -email -phone -street -apartment -suburb -zipCode -code -city'
  });

  this.populate({
    path: 'country',
    select: '-__v'
  });

  next();
});

tourSchema.post(/^find/, function(docs, next) {
  console.log(`Query took ${Date.now() - this.start} milliseconds`);
  // console.log(docs);
  next();
});

//AGGREGATION MIDDLEWARE
tourSchema.pre('aggregate', function(next) {
  if (!(this.pipeline().length > 0 && '$geoNear' in this.pipeline()[0])) {
    this.pipeline().unshift({
      $match: { secretTour: { $ne: true } }
    });
  }

  console.log(this.pipeline());
  next();
});

const Tour = mongoose.model('Tour', tourSchema);

module.exports = Tour;

tourRoutes.js 路线文件

router.route('/').get(tourController.getToursByCountry);

【问题讨论】:

  • req.query.countries 是一个数组吗?
  • 是的,它是数组中的一个对象。
  • req.query.countries.split(',') 代码运行良好吗?它是一个数组,因此不能拆分。如果其中有一个数组和一个对象,则可以使用 req.query.countries[0].split(',')[0] 。因为在拆分数组之后,您将得到一个数组作为结果。如果不是解决方案,您可以在 req.query 中共享国家/地区吗?因为问题看起来像是过滤器对象。
  • 谢谢,但建议的方法不正确。在 console.log(req.query.countries);我得到 61ed829364aa758278cde7d5 On console.log(filter);我得到 { country: [ '61ed829364aa758278cde7d5' ] } 但是在 const tourList = await Tour.find(filter).populate('country'); console.log(tourList);我得到 [] Empty Object 在这种情况下,我在 Postman 上过滤了一个国家,61ed829364aa758278cde7d5
  • 你可以使用 {country: {$in: req.query.countries.split(',') }}。我想,这对你有用。因为我们需要找到数组内的国家。如果过滤后的 req.query.countries 始终是数组中的对象,则可以将其写为 req.query.countries.split(',')[0]。它也有效。但是如果它将作为数组中的对象,那么 $in 对你来说更好。我希望,它有效。如果没有,请写在这里。

标签: node.js mongodb express mongoose


【解决方案1】:

我已经解决了这个问题。我在国家字段下导入 MongoDB 的种子 json 数据是我数据库中的字符串,而不是 ObjectID。

【讨论】:

    猜你喜欢
    • 2023-01-25
    • 2019-11-20
    • 2021-07-13
    • 1970-01-01
    • 2017-07-02
    • 1970-01-01
    • 2021-05-06
    • 1970-01-01
    相关资源
    最近更新 更多