【问题标题】:Search Algorithm Implementation using NodeJS + MongoDB(or SQL)使用 NodeJS + MongoDB(或 SQL)实现搜索算法
【发布时间】:2021-09-19 18:14:59
【问题描述】:

有一个带有搜索输入的应用程序,可以通过存储在数据库中的信息来搜索联系人。

例如,我可以输入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 查询示例。任何建议和解决方案都将被接受!

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

【问题讨论】:

  • 在 SQL 中您可以执行 WHERE firstname = 'Alan' AND lastname = 'Smith',看起来您当前的查询正在应用 OR 而不是 AND
  • 可以有多个字段。我可以用Alan Smith KNTU 澄清我的疑问,这样如果有另一个艾伦史密斯在另一所大学(不是 ZNTU)学习,我应该收到唯一一个在 KNTU 学习的联系人。 可能有很多字段,我的应用程序无法确切知道客户端传递了哪些字段。
  • 如果我输入ZNTU Smith,-- 应该有匹配此模式的联系人。但是我的应用不知道字段的确切顺序,也不知道它们有多少。

标签: sql node.js mongodb mongoose


【解决方案1】:

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

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

使用正则表达式,您可以轻松完成此操作,然后您可以使用 logical operatorsmongodb 来创建这样的查询

方法 1

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"
          }
        }
      ]
    }
  ]
})

这里是操场的链接,供您查看Mongo Playground

方法 2

另一种方法是您将 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,然后查询以查看以使您的查询更清晰

这里是操场的链接,供您查看Mongo Playground

【讨论】:

  • 感谢您的回答!拜托,你能给我一个全自动的例子吗?如果没有数据库,我只需将每个对象的所有键值加入数组中,然后使用正则表达式找到我需要的内容。我想记住,我正在尝试使用数据库(不仅仅是普通的 javascript)通过多个字段实现搜索,并且从客户端我得到 string query 只能包含 1 个或多个值(例如 Alan Smith ZNTU - 或电话号码等)
  • 欢迎您,@LeonardoDiPierro!我用另一种方法编辑了我的答案。如果您仍想使用方法 1,将尝试为您提供一个自动化示例来为该方法构建查询
  • 性能不是我优先考虑的。我认为第二种方法看起来很漂亮,但似乎(我现在无法检查第二种方法,所以请纠正我)它没有考虑到顺序可以不同的事实,这使得绘制所有可能排列的组合。因此,您可以自信地使用您认为易于实施的方法...提前致谢!
  • 有没有办法将第二个示例的结果转换回对象?
  • 在第二种方法中,您不需要创建所有可能的排列,因为regex 将处理该部分。您尝试在操场上测试您的用例,我还更新了操场和答案,它将返回原始文档
猜你喜欢
  • 2015-12-06
  • 2017-01-06
  • 2016-12-20
  • 1970-01-01
  • 1970-01-01
  • 2020-07-10
  • 2012-11-29
  • 2011-02-22
  • 1970-01-01
相关资源
最近更新 更多