首页 > 解决方案 > 使用 NodeJS + MongoDB(或 SQL)实现搜索算法

问题描述

有一个带有搜索输入的应用程序,它提供了通过存储在数据库中的信息来搜索联系人的机会。

例如,我可以键入0972133122 Alan,并且我的搜索引擎必须返回firstnameAlan号码与0972133122字符串匹配的所有联系人。

当然,Alan 0972例如,我可以只键入 ,并且必须返回与此模式匹配的所有可能联系人。查询顺序可能不同,因此我可以输入0972 Alan Smith,如果有 2 个联系人的Alan姓名和电话号码以 开头0972,那么额外Smith的说明应该返回唯一的 1 个联系人。

我建议内置的 Android 手机应用程序使用这种搜索算法: 这样我的目标是达到类似的结果,但我知道如何做到这一点。这是我的代码:GraphQL 查询在此处输入图像描述

query contacts {
  contacts(input: {
    contactQuery: "Alan Smith"
  }) {
    name {
      firstName
      lastName
    }
  }
}

NodeJS 查询到 MongoDB

const conditions = {};
const expr = contactQuery
  .split(' ')
  .map((contact) => new RegExp(`${contact}`, 'i'))

conditions.$or = [
  { 'firstName': { $in: expr } },
  { 'lastName': { $in: expr } },
  { 'university': { $in: expr } },
  { emails: { $elemMatch: { email: { $in: expr } } } },
  { phones: { $elemMatch: { phone: { $in: expr } } } },
  { socials: { $elemMatch: { id: { $in: expr } } } },
]

const contacts = await this.contacts
  .find(conditions, undefined)
  .exec()

这部分有效,但我从 MongoDB 收到不需要的文档:

{
  contacts: [
    {
      firstName: "Alan",
      lastName: "Smith",
      university: "KNTU",
      ...
    },
    {
      firstName: "Alan",
      lastName: "Alderson", // should not be returned
      university: "ZNU",
      ...
    },
    ...
  ]
}

但我需要得到一个严格Alan firstnameSmith lastname. 如果无法使用 MongoDB,请提供一个 SQL 查询示例。任何建议和解决方案都将被接受!

如果我的问题仍然不清楚,请告诉我。

标签: sqlnode.jsmongodbmongoose

解决方案


首先,您需要从搜索文本中分离出数字和单词,然后您可以创建一个可能的组合,例如:

  1. 名字:Alan,姓氏:Smith
  2. 名字:Smith,姓氏:Alan

使用正则表达式,您可以轻松地做到这一点,然后您可以使用logical operatorsmongodb创建这样的查询

方法一

db.collection.find({
  $or: [
    {
      $and: [
        {
          firstName: {
            $regex: "Alan",
            $options: "i"
          }
        },
        {
          lastName: {
            $regex: "Smith",
            $options: "i"
          }
        }
      ]
    },
    {
      $and: [
        {
          firstName: {
            $regex: "Smith",
            $options: "i"
          }
        },
        {
          lastName: {
            $regex: "Alan",
            $options: "i"
          }
        }
      ]
    }
  ]
})

这是 Playground 的链接,您可以在Mongo Playground中查看它

方法二

另一种方法是将concat所有可搜索的键放入一个字段中,然后使用正则表达式将其过滤掉

db.collection.aggregate([
  {
    $addFields: {
      text: {
        $concat: [
          "$firstName",
          " ",
          "$lastName",
          " ",
          "$university",
          " ",
          "$phones"
        ]
      }
    }
  },
  {
    $match: {
      text: {
        $regex: "(?=.*?(0972))(?=.*?(Alan))(?=.*?(Smith))",
        $options: "i"
      }
    }
  },
  {
    $project: {
      text: 0
    }
  }
])

构建查询的代码:

let text = "0972 Alan Smith";
let parts = text.split(" ");
let query = parts.map(part => "(?=.*?("+part+"))").join("");

console.log(query);

但是您需要检查这种方法的性能影响,或者您可以创建一个view然后查询来查看以使您的查询更清晰

这是 Playground 的链接,您可以在Mongo Playground中查看它


推荐阅读