基于Mongodb索引的文本搜索以匹配完整字符串

66bbxpm5  于 2022-11-03  发布在  Go
关注(0)|答案(2)|浏览(167)

当使用mongodb的文本索引功能在mongodb示例中搜索条目时,我似乎收到了包含输入字符串中任何单词的结果。因此,例如,如果我搜索“google seo”,它会返回google seo,google,和seo。我只需要它返回的结果有整个字符串或至少他们两个在句子中。所以结果像'为什么我应该谷歌搜索引擎优化','什么是谷歌搜索引擎优化','谷歌与搜索引擎优化有什么关系'等应该返回。以下任何组合将是完美的。
我现在可以通过使用mongodb正则表达式来缓解整个问题,但是这比索引搜索要慢得多,因为我有超过2.5亿个条目。作为测试,索引搜索平均花费了1.72秒,而正则表达式搜索花费了27.23秒。我希望索引搜索的速度甚至只有正则表达式搜索准确性的一半,就好像用户可以更快地搜索一样。如果结果不是最准确的,也没有关系。另外,如果一个字符串中的所有单词只是位于输入字符串的任何地方,那么通过编程创建正则表达式搜索来匹配它们。例如,对于我来说,返回的结果在同一个句子中包含单词“google”和“seo”,这是很多不必要的代码,也不是100%准确。
当前的数据库模式如下

{
    _id: 0000000000,
    search_string: string,
    difficulty: number,
    clicks: number,
    volume: number,
    keyword: string
 }

后端是一个NodeJS服务器。
任何帮助都是感激不尽的。
谢谢你!

9lowa7mx

9lowa7mx1#

将这两种方法(文本搜索和正则表达式)结合起来是否有效?
没有playroom链接,因为这需要一个文本索引来演示,但请考虑以下示例文档:

test> db.foo.find()
[  
  { _id: 1, val: 'google seo' },   
  { _id: 2, val: 'google ' },     
  { _id: 3, val: 'seo random ' },  
  { _id: 4, val: 'none' }       
]

如问题中所述和文档中所述,对'google seo'的搜索将返回至少与这些术语中的一个匹配的所有文档(在此示例数据中为4个中的3个):

test> db.foo.find({$text:{$search:'google seo'}})    
[                                                                                                                         
  { _id: 2, val: 'google ' },                                                                                             
  { _id: 1, val: 'google seo' },                                                                                          
  { _id: 3, val: 'seo random ' }
]

如果我们通过$all操作符扩展查询 predicate ,使其同时包含两个搜索项的正则表达式,结果将缩小到只有一个文档:

test> db.foo.find({$text:{$search:'google seo'}, val:{$all:[/google/i, /seo/i]}})                                       
[ 
  { _id: 1, val: 'google seo' } 
]

如果单词的顺序不符合我们的预期,它也会起作用:

test> db.foo.insert({_id:5, val:'seo out of order google string'})
{ acknowledged: true, insertedIds: { '0': 5 } }
test> db.foo.find({$text:{$search:'google seo'}, val:{$all:[/google/i, /seo/i]}})
[
  { _id: 1, val: 'google seo' },
  { _id: 5, val: 'seo out of order google string' }
]

数据库首先使用文本索引选择候选文档,然后在将它们返回给客户端之前通过正则表达式执行最终过滤。
或者,如果您正在使用Atlas,您可以查看Atlas搜索功能。似乎mustfilter也可以满足此用例(参考)。

kninwzqo

kninwzqo2#

∮我的工作∮
在成千上万的数据点上运行任何类型的正则表达式总是非常耗费时间和资源的,而且在mongodb上原生地运行意味着数据不是以块/异步的方式发送的(至少就我的知识范围而言)。
相反,有两种方法可以减少时间、服务器资源或带宽使用。

  • 在发送数据之前使用服务器来处理数据。这看起来似乎很明显,但是如果你有硬件开销在服务器端执行这样的操作,那么在服务器端运行字符串比较并将数据以块的形式发送回以延迟加载到你的应用程序中会更好、更快。对我来说,这将平均搜索时间从29.3秒减少到2秒以下。平均23 s,数据库的样本大小为250 m个条目,每次搜索80 k个结果,过滤结果约为10 k-15 k。
  • 如果您没有处理开销并且愿意牺牲带宽和用户体验,那么在客户端上这样做也是可行的,特别是考虑到现代硬件的能力。这提供了更大的灵活性,使得所有数据都可以显示,相关数据首先显示,其他“无关数据”最后显示。这确实需要很好地优化,并在假设您的应用程序将主要运行在现代硬件上或最好不运行在移动的设备上的情况下实现。

这些都是我最好的解决方案。也许有更好的本地技术,但在一周的时间里,我无法找到任何更好(更快)的解决方案。

编辑

我觉得有必要详细说明数据在发送之前要经过什么样的处理,以及我是如何处理的。
目前我有一个大约2.5亿个条目的数据库。每个条目都有问题中描述的模式。一般的查询通常是“谁是普京”、“安卓”、“iphone13”等。每个“主要”关键词的数据库由12个集合组成(what、why、should、how、when、which、who等),因此查询首先被去除这些。因此,如果查询是“who is putin”,则其被转换为仅“is putin”。对于没有关键字的情况,所有的收藏品都被检查过了。如果有更好的方法,请告诉我
之后,我们将查询发送到数据库并检索结果。该查询经过另一个函数处理,该函数去除了“不必要”的单词,因此is、if、a、be等单词也被删除,并返回一个主要单词的数组,因此“What is going on between Russia and Ukraine”(俄罗斯和乌克兰之间发生了什么)这样的查询被转换为['going', 'between', 'Russia', 'Ukraine']。当收到结果时,我们检查每个数组是否包含了数组中的所有单词,如果包含,则返回给客户端。这里的操作非常简单,因为我们不关心大小写、空格等。只需使用js contains()方法。
在检索一个包含2,344,123个结果的查询时,返回第一个结果所需的平均时间约为2.12秒,返回结束所需的平均时间约为8.32秒。再次运行同一个查询所需的平均时间约为0.84秒,返回结束所需的平均时间约为1.98秒(第一次返回结果所需的平均时间为冷,后续请求所需的平均时间为热)。

相关问题