es的几个骚操作

前言

上一篇博客写到ElasticSearch有中文分词检索的能力,但如果仅仅就这个就完全没办法体现ElasticSearch的强大了,ElasticSearch还能支持短语搜索,近似搜索,搜索推荐,搜索纠正等搜索引擎高级特性。可以极大地方便用户,极大地提高用户体验。站内垂直搜索几乎在所有互联网产品中都有运用,往往首页最显眼处都有一个搜索框,如天猫,京东,拼多多,当当网,美团,饿了么,优酷,爱奇艺,哔哩哔哩,QQ音乐,网易云音乐,携程,途牛等等等等,不出意外的话应该都是用ElasticSearch或者solr来实现的。

ES不错的教学视频:

  1. https://www.bilibili.com/video/av50555094/?p=66
  2. https://www.bilibili.com/video/av50555916

es会把每一个分词器分好的词建立倒排索引,存储每一个包含该词语的id等信息,个人猜测分词的列表还会建立Hash表或者B树,因为词语的数据量一大,达到千万级亿级,性能无法保证,如果这样检索单个词语的时候时间复杂度则为O(1)或者O(log(M,N)),当然纯属个人臆测啦,并没有找到相关资料。
倒排索引图

ElasticSearch

短语搜索&近似搜索

es会对搜索关键字进行分词,然后到倒排索引中去匹配,但如果用户希望根据输入的短语匹配,比如就是要搜索‘是一个靓仔’一定连一起的,跟数据库的LIKE关键字效果一样,而不是分词之后只要有【是,一个,靓仔】其中一个就OK,这个时候需要用到短语搜索了;短语搜索也支持任意两个词设置一个最大的距离,只要满足词语词之间不要超过这个距离都可以搜索出来,比如包含【是不是 一个鲜肉 靓仔】需要搜索出来,那么距离就要设置为2了,这种可以称之为近似搜索。
原理:因为文档分词后建立倒排索引时会记录该词在文档中的position,匹配到之后再比较一下position的差值即可,如果必须连在一起,那么差值不能超过1。es也可以使用prefix,wildcard,regexp来检索,但是这些是不能利用es索引的。

-- 建立映射
PUT /parse_search
{
  "mappings": {
    "properties": {
      "con_parse": {
        "type": "text"
      },
      "con_notana": {
        "type": "text",
        "fields": {
          "raw": {
            "type": "text",
            "index": false
          }
        }
      }
    }
  }
}

-- 插入数据
PUT /parse_search/_doc/1
{
  "con_parse":"凌章是一个靓仔",
  "con_notana":"凌章是一个靓仔"
}
PUT /parse_search/_doc/2
{
  "con_parse":"ling is a good looking boy",
  "con_notana":"ling is a good looking boy"
}

可以搜到,不符合要求
GET /parse_search/_search
{
  "query": {
    "match": {
      "con_parse": "a good"
    }
  }
}

可以搜到,不符合要求
GET /parse_search/_search
{
  "query": {
    "match": {
      "con_parse": "is good"
    }
  }
}

搜不到,不符合要求
GET /parse_search/_search
{
  "query": {
    "term": {
      "con_parse": {
        "value": "a good"
      }
    }
  }
}

搜不到,不符合要求
GET /parse_search/_search
{
  "query": {
    "term": {
      "con_parse": {
        "value": "a good"
      }
    }
  }
}

搜不到,符合要求
GET /parse_search/_search
{
  "query": {
    "match_phrase": {
      "con_parse": "is good"
    }
  }
}

可以搜到,符合要求
GET /parse_search/_search
{
  "query": {
    "match_phrase": {
      "con_parse": "a good"
    }
  }
}
可以搜到,间隔一个词,符合要求
GET /parse_search/_search
{
  "query": {
    "match_phrase": {
      "con_parse": {
        "query": "is good",
        "slop": 2
      }
    }
  }
}

搜不到,不符合要求
GET /parse_search/_search
{
  "query": {
    "match_phrase": {
      "con_parse": {
        "query": "is goo",
        "slop": 2
      }
    }
  }
}
可以搜到,符合要求
GET /parse_search/_search
{
  "query": {
    "match_phrase_prefix": {
      "con_parse": {
        "query": "is goo",
        "slop": 2
      }
    }
  }
}

搜索推荐&搜索纠正

有些时候,用户去做搜索是,未必记得全名,这个时候我们需要给用户一些推荐词列表,唤起用户的记忆,友好的帮助用户搜索;又有些时候,用户会有意或无意把词语拼错,比如靓仔拼成靓崽,good拼成god,像笔者这种相当爱国、过四级还差两分、又要经常搜一些代码资料的人自然是无可避免(拿他真没办法),所以搜索纠正对于es来说也是必不可少。
原理:es的实现使用的N-gram算法模型,我查了一下,他是自然语言处理NLP的一种模型,说起来有一点AI的意味了。使用fuzzy也可以完成检索,但是不能利用es索引。

--- 搜索推荐操作
PUT /ngram_search
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "my_tokenizer"
        }
      },
      "tokenizer": {
        "my_tokenizer": {
          "type": "edge_ngram",
          "min_gram": 1,
          "max_gram": 30,
          "token_chars": [
            "letter",
            "digit"
          ]
        }
      }
    }
  },"mappings": {
    "properties": {
      "con_ngram":{
        "type": "text",
        "analyzer": "my_analyzer"
      }
    }
  }
}
-- 测试
POST ngram_search/_analyze
{
  "analyzer": "my_analyzer",
  "text": "ling is a good looking boy"
}
PUT /parse_search/_doc/1
{
  "con_parse":"凌章是一个靓仔"
}
PUT /parse_search/_doc/2
{
  "con_parse":"ling is a good looking boy"
}
---  搜索纠正操作
PUT /autoc_search
{
  "settings": {
    "analysis": {
      "filter": {
        "autocomplete_filter" : {
          "type" : "edge_ngram",
          "min_gram" : 1,
          "max_gram" : 20
        }
      },
      "analyzer": {
        "autocomplete" : {
          "type" : "custom",
          "tokenizer" : "standard",
          "filter" : [
            "lowercase",
            "autocomplete_filter"
          ]
        }
      } 
    }
  }
  , "mappings": {
    "properties": {
       "con_ngram":{
        "type": "text",
        "analyzer": "autocomplete"
      }
    }
  }
}
-- 查看分词
POST autoc_search/_analyze
{
  "analyzer": "autocomplete",
  "text": "ling is a good looking boy"
}

PUT /autoc_search/_doc/1
{
  "con_ngram":"凌章是一个靓仔"
}
PUT /autoc_search/_doc/2
{
  "con_ngram":"ling is a good looking boy"
}
-- 可以查到,搜索纠正
GET /autoc_search/_search
{
  "query": {
    "match_phrase": {
      "con_ngram":{
        "query": "is a god"
      }
    }
  }
}
-- 不可以查到,隔了一个
GET /autoc_search/_search
{
  "query": {
    "match_phrase": {
      "con_ngram":{
        "query": "is god"
      }
    }
  }
} 
--可以查到,搜索纠正
GET /autoc_search/_search
{
  "query": {
    "match_phrase": {
      "con_ngram":{
        "query": "is god",
        "slop": 1
      }
    }
  }
} 
-- 搜索不到
GET /autoc_search/_search
{
  "query": {
    "match_phrase_prefix": {
      "con_ngram":{
        "query": "is god"
      }
    }
  }
} 
-- 可以搜到
GET /autoc_search/_search
{
  "query": {
    "match_phrase_prefix": {
      "con_ngram":{
        "query": "is god",
        "slop": 1
      }
    }
  }
}

PostgreSQL再提一笔

PostgreSQL也支持N-gram,所以再提一笔,需要添加pg_trgm扩展,使用gist_trgm后对左右匹配都可以利用索引了,也可对正则表达式利用索引,可以实现近似搜索,但是搜索推荐和搜索纠正貌似还是不行。

-- 创建扩展
create extension pg_trgm;
 
create table pg_ngram(
	id serial,
	con text,
	con_btree text,
	con_gin text,
	con_gist text
);

-- 索引
create index k_btree on pg_ngram using btree(con_btree);
create index k_gin on pg_ngram using gin(con_gin gin_trgm_ops);
create index k_gist on pg_ngram using gist(con_gist gist_trgm_ops);

-- 查询
explain select * from pg_ngram where con like '%god looking%';
explain select * from pg_ngram where con_btree like '%god looking%';
explain select * from pg_ngram where con_gin like '%good looking%';
explain select * from pg_ngram where con_gist like '%good looking%';

explain select * from pg_ngram where con_gist ~ 'good looking';
explain select * from pg_ngram where con_gist ~ 'good looking[^。]';

--查看分解
select show_trgm(con_gin) from pg_ngram;
select show_trgm(con_gist) from pg_ngram;
select similarity(con_gin,'god looking') from pg_ngram;
select similarity(con_gist,'god looking') from pg_ngram;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值