猫鼬:find()忽略重复的值

时间:2020-05-23 08:57:38

标签: node.js mongodb mongoose nosql

我有一个“聊天”猫鼬Schema,它具有以下属性:

const schema = mongoose.Schema({
    ...
    recipient: {
        type: mongoose.Types.ObjectId,
        required: true,
        ref: 'User',
    },
    sender: {
        type: mongoose.Types.ObjectId,
        required: true,
        ref: 'User',
    },
    content: {
        type: String,
    },
    ...
}, {
    timestamps: true,
});

通常,我想获取用户每次掩护的最后一条消息。这意味着我需要提供一个用户ID(可以存储在senderrecipient字段中)并获取用户与每个用户共享的最后一条消息(由createdAt表示)其他用户。

示例: 假设我有以下document

[
  {
    recipient: "One",
    sender: "Two",
    createdAt: ISODate("2014-01-01T08:00:00Z"),

  },
  {
    recipient: "One",
    sender: "Three",
    createdAt: ISODate("2014-02-15T08:00:00Z")
  },
  {
    recipient: "Two",
    sender: "One",
    createdAt: ISODate("2014-02-16T12:05:10Z")
  }
]

以“ One”作为输入-来自Model.find(...)的期望结果是:

[
  {
    recipient: "One",
    sender: "Three",
    createdAt: ISODate("2014-02-15T08:00:00Z")
  },
  {
    recipient: "Two",
    sender: "One",
    createdAt: ISODate("2014-02-16T12:05:10Z")
  }
]

3 个答案:

答案 0 :(得分:2)

您可以通过汇总来执行此操作,如下面的查询所示

工作示例-https://mongoplayground.net/p/wEi4Y6IZJ2v

db.collection.aggregate([
  {
    $sort: {
      recipient: 1,
      createdAt: 1
    }
  },
  {
    $group: {
      _id: "$recipient",
      createdAt: {
        $last: "$createdAt"
      }
    }
  },
  {
    $project: {
      _id: 0,
      recipient: "$_id",
      createdAt: "$createdAt"
    }
  }
])

如果要匹配两个字段,则可以使用以下查询

工作示例-https://mongoplayground.net/p/Rk5MxuphLOT

db.collection.aggregate([
  {
    $match: {
      $or: [
        {
          sender: "One"
        },
        {
          recipient: "One"
        }
      ]
    }
  },
  {
    $addFields: {
      other: {
        $cond: {
          if: {
            $eq: [
              "$recipient",
              "One"
            ]
          },
          then: "$sender",
          else: "$recipient"
        }
      }
    }
  },
  {
    $sort: {
      createdAt: 1
    }
  },
  {
    $group: {
      _id: "$other",
      createdAt: {
        $last: "$createdAt"
      },
      recipient: {
        $last: "$recipient"
      },
      sender: {
        $last: "$sender"
      }
    }
  },
  {
    $project: {
      _id: 0,
      recipient: "$recipient",
      sender: "$sender",
      createdAt: "$createdAt"
    }
  }
])

答案 1 :(得分:2)

使用示例数据:

[
  {
    recipient: "One",
    sender: "Two",
    createdAt: ISODate("2014-01-01T08:00:00Z"),
    content: "Hi Mr. One! - Two"
  },
  {
    recipient: "One",
    sender: "Three",
    createdAt: ISODate("2014-02-15T08:00:00Z"),
    content: "Hello One! - Three"
  },
  {
    recipient: "Two",
    sender: "One",
    createdAt: ISODate("2014-02-16T12:05:10Z"),
    content: "Whats up, Two? - One"
  }
]

看看以下汇总:https://mongoplayground.net/p/DTSDWX3aLWe

它...

  • 使用 $ match 来按收件人或发件人筛选所有邮件。返回与当前用户(One)匹配的用户
  • 使用 $ addFields 添加一个conversationWith字段,该字段包含recipient(如果是向用户One发出的消息 或{{1} },如果它是用户One发送的消息
  • 使用 $ sort
  • 按日期对邮件进行排序
  • 在新的sender字段中使用 $ group 将所有消息分组,并以conversationWith的形式返回最新消息

完整的聚合管道:

firstMessage

使用mongoplayground,您可以一个一个地删除聚合步骤,以查看每个步骤的作用。

尝试:

  • $ match 步骤
  • $ match + $ addFields
  • $ match + $ addFields + $ sort
  • [..]

以获得最佳理解。

答案 2 :(得分:0)

如果您想依靠mongodb过滤出重复项,最好不要创建unique index,甚至永远不要重复。

因为似乎您的收件人嵌套在父方案中,所以我将在nodejs中过滤重复项,因为它很难在mongodb查询中解开此问题。 如果您需要使用mongodb,请使用distinct函数或aggregate pipeline,并受this article

的启发