首页 > 解决方案 > 猫鼬查找,填充,排序不是排序

问题描述

我只是想在模型上调用 find(),通过引用填充字段,然后对某个嵌入字段进行排序,这似乎根本没有排序。换句话说,它似乎是一个完全的空操作。下面是我对重现问题的最小案例 (foo.js) 的尝试:

const mongoose = require('mongoose');
const process = require('process');
const assert = require('assert').strict;

const Schema = mongoose.Schema;

const DogSchema = new Schema({
   name: String,
   favorite_food: { type: Schema.Types.ObjectId, ref: 'Food' }
});

const FoodSchema = new Schema({
   name: String
});

const Dog = mongoose.model('Dog', DogSchema);
const Food = mongoose.model('Food', FoodSchema);

async function setup() {
   try {
      await mongoose.connect(
         'mongodb://127.0.0.1:27017/doggy_db', { useNewUrlParser: true });
   }
   catch (err) {
      console.error(err);
      process.exit(1); 
   }

   let conn = mongoose.connection;

   try {
      await Promise.all([
         conn.dropCollection('dogs'),
         conn.dropCollection('foods')
      ]);
   }
   catch (err) {}

   let chicken = new Food({ name: 'chicken' });
   let hotdog = new Food({ name: 'hotdog' });

   await Promise.all([
      chicken.save(),
      hotdog.save()
   ]);

   await Promise.all([
      new Dog({ name: 'Snoopy', favorite_food: chicken._id }).save(),
      new Dog({ name: 'Buckie', favorite_food: hotdog._id }).save()
   ]);

   return conn;
}

async function minimal_case() {
   let conn = await setup();

   let all_dogs = await Dog.find({})
      .populate({ 
         path: 'favorite_food', 
         select: 'name',
         options: { sort: { name: 1 } } 
      });

   let favorite_food_asc = all_dogs.map(dog => dog.favorite_food.name);

   all_dogs = await Dog.find({})
      .populate({ 
         path: 'favorite_food', 
         select: 'name',
         options: { sort: { name: -1 } } 
      });

   let favorite_food_desc = all_dogs.map(dog => dog.favorite_food.name);

   assert(favorite_food_asc.join(',') === 'chicken,hotdog');
   assert(favorite_food_desc.join(',') === 'hotdog,chicken');

   conn.close();
}

minimal_case().catch(err => {
   console.error(err);
   mongoose.connection.close();
})

要运行它,请执行以下操作,

$ mkdir db
$ mongod --dbpath db

在其他一些终端,

$ node foo.js

这应该使 minimum_case() 函数中的第二个断言失败。

npm show mongoose说我正在使用猫鼬 5.2.5

这篇文章也提到了这一点:

在填充中排序不起作用(猫鼬)

在github上引用了这个问题:

https://github.com/Automattic/mongoose/issues/4481

这表示该问题已关闭,因为它不受支持...

然而,猫鼬文档似乎提到这种语义应该可以通过他们的 API

http://mongoosejs.com/docs/api.html#query_Query-populate

我的其他选择是

  1. 直接使用本机 mongo 语法来利用聚合管道(可能是查找、展开、排序),我猜这会使代码的可读性变得不稳定
  2. 更改我的模型,以便我要排序的字段直接嵌入到我正在执行查询的模型中,这样就不需要填充。这可能是很多工作......
  3. 在客户端控制器上对其进行排序,而不是让 mongo 为我做这件事。这可能真的很慢,甚至可能比对引用的(显然是非索引的)字段进行排序更慢
  4. 还有什么我想念的吗?

我知道尝试在 mongo 中对引用的字段进行连接和排序通常会影响性能,但我现在只是在做一个教程,而且这种无法尝试射击自己的能力让我感到惊讶

更新

我添加mongoose.set('debug', true)了打开查询打印。这似乎Dog.find({}).populate({...})转化为两个独立的查询(甚至在http://mongoosejs.com/docs/populate.html的文档中提到):

Mongoose: dogs.find({}, { fields: {} })
Mongoose: foods.find({ _id: { '$in': [ ObjectId("5b59e1f5abaabb5b3a6bfe17"), ObjectId("5b59e1f5abaabb5b3a6bfe18") ] } }, { sort: { name: 1 }, fields: { name: 1 } })

我开始怀疑是否有可能告诉猫鼬在填充后链式排序语义并在查询中指定一个嵌入字段。

更新

Mongoose 按填充字段排序的答案说这是不可能的。我想我会在控制器代码中对其进行排序并尝试限制查询的内容。

标签: node.jsmongodbsortingmongoose

解决方案


推荐阅读