前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Redis统计网站搜索的热搜词

Redis统计网站搜索的热搜词

作者头像
用户4283147
发布2022-10-27 11:02:51
1.3K0
发布2022-10-27 11:02:51
举报
文章被收录于专栏:对线JAVA面试

对于一个网站来说,无论是商城网站还是门户网站,搜索框都是有一个比较重要的地位,它的存在可以说是为了让用户更快、更方便的去找到自己想要的东西。对于经常逛这个网站的用户,当然也会想知道在这里比较“火”的东西是什么,这个时候我们搜索框上的热词就起作用了。其实我觉得这一块的完善会对这个网站带来许多益处。

可能现在比较普遍的做法是把这些相应的信息存到我们的关系型数据库中,如sql server 和 oracle。方便起见的话,可能每搜索一次就往表里插一次数据,用的时候要先统计数据,统计完后再排序,最后才展示。这种情况下,如果搜索量很大的话,表的膨胀速度就会非常快,如果sql没写好,查询的时候估计会。。相比Redis,同等条件下,Redis的速率肯定是会较优,毕竟是从内存中拿出来的。

下面我们就用.NET Core和StackExchange.Redis来做一下这个简单的案例。

案例用到的一些相关技术和说明:

技术

说明

.NET Core

网站嘛,你懂的。有事没事用Core写写Demo,免得跟不上发展的脚步。

Redis

存储搜索词,用了主从的模式,主写从读

Jquery-ui

主要是用了里面的autocomplete

开始正题之前,我们要确定用Redis中的那种数据结构,五种之中比较合适的应该是SortedSet,我们可以用成员来作为搜索词,成员分数来作为搜索词的搜索次数,这样就可以很方便的来操作相关的数据了。

下面开始正题:

我们在开始的时候需要初始化一下数据。这里就直接在第一次运行的时候初始化。用上流水线的技术,速度还是很可观的。初始化了70个搜索关键词(NBA球星),然后用随机数作为关键字的下标,去随机给这个关键字加1分。这个分数就是这个关键字被搜索的次数。下面来看看初始化的相关代码:

代码语言:javascript
复制
public IActionResult Index()
        {
            //keys
            IList<string> keys = new List<string>()
            {
                "kobe","johnson","jabbar","west","o'neal","baylor","mccann","worthy","gasol","chamberlain",
                "fisher","odom","bynum","horry","rambis","riley","clarkson","Williams","young","Russell",
                "ingram","randle","nance","brown","deng","yi","ariza","artest","walton","vujacic",
                "james","paul","curry","park","yao","kevin","wade","rose","popovich","leonard",
                "aldridge","ginobili","duncan","lavine","rubio","garnett","wiggins","westbrook","durant","ibaka",
                "nowitzki","pierce","crawford","love","smith","iguodala","barnes","green","thompson","harden",
                "lillard","mccollum","lin","jackson","nash","stoudemire","whiteside","dragic","Howard","batum"
            };

            //init
            Random random = new Random();
            var tran = _redis.GetTransaction();
            for (int i = ; i < ; i++)
            {
                tran.SortedSetIncrementAsync(_searchKey, keys[random.Next(, )], );
            }
            tran.ExecuteAsync();

            return View();
        }

这里是在加载这个页面的时候就把这些热搜词存进Redis中,这样我们才能有数据来演示啊。这里还用到了一个非事务型的流水线。就是把要操作的指令存放到一个队列中,最后把这个队列扔到服务端去执行,这样就有效的减少了不必要的网络传输,同时也提高了执行速度。

好了,初始数据有了,下面要做的就是用户在搜索的时候,根据用户的输入去匹配搜索次数多的关键字,展示最Hot的10个,当然这个展示的个数是随我们定的,最后可以考虑把这个放到我们的配置文件中去,甚至是放到数据库中,

为的是灵活和方便维护。下面是我们在后台的处理逻辑:

代码语言:javascript
复制
public IActionResult GetHotKey(string key="")
        {
            if (string.IsNullOrEmpty(key))
            {//default
                var res = _redis.ZRevRange(_searchKey, , );
                var list = (from i in res select i.ToString());
                return Json(list);
            }
            else
            {//by user input
                var res = _redis.ZRevRange(_searchKey, , -1);
                var list = (from i in res select i.ToString()).Where(x => x.Contains(key)).Take().ToList();
                return Json(list);
            }
        }

对于查询的处理是非常的简单的,用户不小心输入空格的时候就展示最热的10个关键词,如果用户有输入的话,就把关键词中包含用户输入的展示出来。那么我们在页面上要做些什么呢?下面就是我们演示用的搜索框。

代码语言:javascript
复制
<div class="row">
    <div class="col-md-6 col-md-offset-4" style="padding-top:50px;">
        <input id="key" name="key" placeholder="search" class="form-control col-md-4">
        <button class="btn btn-primary" type="button" id="searchSubmit">Search</button>
        <div id="result"></div>
    </div>
</div>

相应的js是写到 scripts 这个section中的,js的话是比较简单的就是用ajax去请求我们要展示的数据。更多的应该是jquery-ui的api问题,大家也可以换用自己比较熟悉的组件,举一反三即可。下面是autocomplete的api ,如果有需要可以去看一下。

代码语言:javascript
复制
@section scripts{
    <script type="text/javascript">
        $(function () {
            //show hot keyword
            $("#key").autocomplete({                
                source: function (request, response) {
                    $.ajax({
                        url: "@Url.Action("GetHotKey", "Auto")",
                        dataType: "json",
                        data: {
                            key: request.term
                        },
                        success: function (data) {
                            response(data);
                        }
                    });
                },
            });            
    </script>
}

到这里,用户搜索前的操作,我们是做好了,下面先来看一下效果。

那么用户点击了搜索之后我们要做些什么处理呢?无论是新的关键字还是已有的关键字,我们都是要做处理的,当然redis中zincrby命令来处理这个是十分合适的,存在的就把分数加1,不存在就创建一个分数为1的成员。下面是搜索时的后台逻辑处理:

代码语言:javascript
复制
[HttpPost]
        public IActionResult SetHotKey(string key)
        {
            if (!string.IsNullOrWhiteSpace(key))
            {
                _redis.ZIncrby(_searchKey,key);
                //other 
                //...
                return Json(new { code = "000", msg = "OK" });
            }
            else
            {
                return Json(new { code = "999", msg = "keyword can not be empty!" });
            }
        }

限制了用户不能搜索空关键字,在把这个关键字存储或者分数加一之后,就是展示我们的搜索的结果。这个搜索的结果一般是从solr等全文检索的地方查出来的,不是我们讲的重点,所以就忽略了。然后我们还要加一段js去处理我们搜索的时候应该做的操作。当然,都是些比较简单的操作。

代码语言:javascript
复制
//search
            $("#searchSubmit").click(function () {
                $.ajax({
                    url: "@Url.Action("SetHotKey", "Auto")",
                    dataType: "json",
                    type: "POST",
                    data: { key: $("#key").val() },
                    success: function (data) {
                        if (data.code == "000") {
                            $("<p>search successful!</p>").appendTo("#result");
                        } else {
                            $("<p>"+data.msg+"</p>").appendTo("#result");
                        }
                    }
                });
            });

下面是效果图:

在演示的时候,我们搜索了“我爱你”和“我不信”,在Redis的客户端我们找出搜索次数最少的6个,然后就可以看到我们那两个关键字最的分数都是1。确定是刚插入的数据。

到这里,我们做的这个热搜词可以说是大功告成了。当然这可以说是最最最简单的一个雏形。我们还可以适当的添加一些东西让这个功能变得更加完善。比如我可以在搜索展示的时候显示一下搜索的次数等。

最后是完整的控制器和页面代码:

代码语言:javascript
复制
using AutoCompleteDemo.Common;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;

namespace AutoCompleteDemo.Controllers
{
    public class AutoController : Controller
    {
        private readonly IRedis _redis;
        private readonly string _searchKey = "search";        
        public AutoController(IRedis redis)
        {
            _redis = redis;
        }

        public IActionResult Index()
        {
            //keys
            IList<string> keys = new List<string>()
            {
                "kobe","johnson","jabbar","west","o'neal","baylor","mccann","worthy","gasol","chamberlain",
                "fisher","odom","bynum","horry","rambis","riley","clarkson","Williams","young","Russell",
                "ingram","randle","nance","brown","deng","yi","ariza","artest","walton","vujacic",
                "james","paul","curry","park","yao","kevin","wade","rose","popovich","leonard",
                "aldridge","ginobili","duncan","lavine","rubio","garnett","wiggins","westbrook","durant","ibaka",
                "nowitzki","pierce","crawford","love","smith","iguodala","barnes","green","thompson","harden",
                "lillard","mccollum","lin","jackson","nash","stoudemire","whiteside","dragic","Howard","batum"
            };

            //init
            Random random = new Random();
            var tran = _redis.GetTransaction();
            for (int i = ; i < ; i++)
            {
                tran.SortedSetIncrementAsync(_searchKey, keys[random.Next(, )], );
            }
            tran.ExecuteAsync();

            return View();
        }

        public IActionResult GetHotKey(string key="")
        {
            if (string.IsNullOrEmpty(key))
            {//default
                var res = _redis.ZRevRange(_searchKey, , );
                var list = (from i in res select i.ToString());
                return Json(list);
            }
            else
            {//by user input
                var res = _redis.ZRevRange(_searchKey, , -1);
                var list = (from i in res select i.ToString()).Where(x => x.Contains(key)).Take().ToList();
                return Json(list);
            }
        }

        [HttpPost]
        public IActionResult SetHotKey(string key)
        {
            if (!string.IsNullOrWhiteSpace(key))
            {
                _redis.ZIncrby(_searchKey,key);
                //other 
                //...
                return Json(new { code = "000", msg = "OK" });
            }
            else
            {
                return Json(new { code = "999", msg = "keyword can not be empty!" });
            }
        }
    }
}

AutoController
代码语言:javascript
复制
@{
    ViewData["Title"] = "Auto Complete";
}
<div class="row">
    <div class="col-md-6 col-md-offset-4" style="padding-top:50px;">
        <input id="key" name="key" placeholder="search" class="form-control col-md-4">
        <button class="btn btn-primary" type="button" id="searchSubmit">Search</button>
        <div id="result"></div>
    </div>
</div>
@section scripts{
    <script type="text/javascript">
        $(function () {
            //show hot keyword
            $("#key").autocomplete({                
                source: function (request, response) {                    
                    $.ajax({
                        url: "@Url.Action("GetHotKey", "Auto")",
                        dataType: "json",
                        data: {
                            key: request.term
                        },
                        success: function (data) {
                            response(data);
                        }
                    });
                },
            });

            //search
            $("#searchSubmit").click(function () {
                $.ajax({
                    url: "@Url.Action("SetHotKey", "Auto")",
                    dataType: "json",
                    type: "POST",
                    data: { key: $("#key").val() },
                    success: function (data) {
                        if (data.code == "000") {
                            $("<p>search successful!</p>").appendTo("#result");
                        } else {
                            $("<p>"+data.msg+"</p>").appendTo("#result");
                        }
                    }
                });
            });
        });
    </script>
}

Index.cshtml

--完--

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-07-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 对线JAVA面试 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档