# 友情提示
> 1. **学习来自**:[慕课网 ](https://www.imooc.com/)- [龙虾三少](http://www.imooc.com/t/7143508)。
> 2. **全套学习教程**:[《ES7+Spark 构建高相关性搜索服务&千人千面推荐系统》](https://coding.imooc.com/class/391.html) 。
> 3. **Cappuccino个人博客地址**:[在线博客地址](https://cappuccinoj.cn/)。
ps: 如有侵权请联系作者进行删除。
# dianping系统架构

# 项目介绍
基于大众点评搜索以及推荐业务,使用SpringBoot加mybatis结合前端模板搭建运营后台门店管理功能,借助ElasticSearch的最新版本ES7,完成高相关性进阶搜索服务,并基于spark mllib2.4.4构建个性化千人千面推荐系统。
### 组织结构
```
dianping
├── dianping-canal -- 客户端以及调度索引增量
├── dianping-common -- 工具类及通用代码
├── dianping-config -- 项目配置
├── dianping-controller -- 控制层
├── dianping-dao -- 数据库持久层
├── dianping-model -- 实体模块
├── dianping-request -- 请求参数模块
└── dianping-service -- 逻辑层接口
```
### 开发环境
| 工具 | 版本号 | 下载 |
| ------------- | ------ | ------------------------------------------------------------ |
| JDK | 1.8 | https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html |
| JDK | 11 | https://repo.huaweicloud.com/java/jdk/ |
| Mysql | 5.7 | https://www.mysql.com/ |
| Elasticsearch | 7.7.0 | https://www.elastic.co/downloads/elasticsearch |
| Logstash | 7.7.0 | https://www.elastic.co/cn/downloads/logstash |
| Kibana | 7.7.0 | https://www.elastic.co/cn/downloads/kibana |
| Canal | 1.1.5 | https://github.com/alibaba/canal |
| Spark MLlib | 2.4.4 | https://spark.apache.org/docs/latest/mllib-guide.html#data-types-algorithms-and-utilities |
# ES的搜索原理
- 基于Lucene的分布式、高可用、全文检索的搜索引擎
* 独立的网络上的一个或一组进程节点(可以独立部署运行、中间件、支持分布式)
* 对外提供搜索服务(http或transport协议) Elastic Search 7.x 逐渐废弃 transport 协议
* 对内就是一个搜索数据库
# 名词定义
> 提示:7版本中Type被废弃,索引和类型合并为索引!
| Relational database | ElasticSearch |
| :----: | :----:|
| Database | Index |
| Table | Type |
| Row | Document |
| Column | Field |
| Schema | Mapping |
| Index | Everything is indexed |
| SQL | Query DSL |
| SQL | Query DSL |
| SELECT * FROM table... | GET http://.. |
| UPDATE table SET | PUT http://.. |
| DELETE FROM table ... | DELETE http://.. |
| INSERT INFO ... | PUT http://.. |
## 索引
* 搜索中的数据库或表定义
* 构建文档的时候的缩影创建
## 分词
* 搜索是以词为单位做最基本的搜索单元
* 依靠分词器构建分词
* 用分词器构建倒排索引
* 正向索引(以document为索引的入口)和倒排索引(以分词为索引的入口)
## TF-IDF打分
* TF:token frequency, 分词在document字段(待搜索的字段)中出现的次数
* IDF:inverse document frequency, 逆文档频率,代表分词在整个文档中出现的频率,取反
* TFNORM:token frequency normalized 词频归一化
* BM25:解决词频问题(TF公式的分母)
| 单词ID | 单词 | 文档频率 | 倒排列表(DocID;TF;<POS>)|
| :----: | :----:| :----: | :----:|
| 1 | 谷歌 | 5 | (1;1;<1>),(2;1;<1>),(3;2;<1;6>),(4;1;<1>),(5;1;<1>)|
| 2 | 地图 | 5 | (1;1;<2>),(2;1;<2>),(3;1;<2>),(4;1;<2>),(5;1;<2>)|
| 3 | 之父 | 4 | (1;1;<3>),(2;1;<3>),(4;1;<3>),(5;1;<3>)|
| 4 | 跳槽 | 2 | (1;1;<4>),(4;1;<4>)|
| 5 | Facebook | 5 | (1;1;<5>),(2;1;<5>),(3;1;<8>),(4;1;<5>),(5;1;<8>)|
| 6 | 加盟 | 3 | (2;1;<4>),(3;1;<7>),(5;1;<5>)|
| 7 | 创始人 | 1 | (3;1;<3>)|
| 8 | 拉斯 | 2 | (3;1;<4>),(5;1;<4>)|
| 9 | 离开 | 1 | (3;1;<5>)|
| 10 | 与 | 1 | (4;1;<6>)|
# 分布式原理
* 分片 主从 路由
* 负载均衡和读写分离
* 主分片和副本数
# 分布式部署
> 配置文件 elasticsearch.yml
```
#集群名称
cluster.name: dianping-app
#节点名称
node.name: node-1
#绑定IP地址
network.host: 127.0.0.1
#http监听端口
http.port: 9200
# 如果是使用阿里云、华为云、腾讯云等进行集群配置可以了解一下这个配置
# network.publish_host: 116.xxx.225.xxx
#集群之间的通信端口
transport.tcp.port: 9300
#允许跨域
http.cors.enabled: true
http.cors.allow-origin: "*"
#发现集群节点
discovery.seed_hosts: ["127.0.0.1:9300", "127.0.0.1:9301", "127.0.0.1:9302"]
#是否有资格竞选主节点
cluster.initial_master_nodes: ["127.0.0.1:9300", "127.0.0.1:9301", "127.0.0.1:9302"]
```
# ES语法
```
DELETE /test
PUT /test
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 2
}
}
DELETE /employee
//非结构化方式新建索引
PUT /employee
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1
}
}
PUT /employee/_doc/1
{
"name":"凯杰2",
"age":30
}
PUT /employee/_doc/1
{
"name":"凯杰3",
"age":30
}
PUT /employee/_doc/1
{
"name":"凯杰3"
}
//获取索引记录
GET /employee/_doc/1
//指定字段修改
POST /employee/_update/1
{
"doc":{
"name":"凯杰4",
"age":30
}
}
//强制指定创建,若已存在, 则失败
POST /employee/_create/5
{
"name":"兄弟2",
"age":29
}
//删除某个文档
DELETE /employee/_doc/2
//查询全部文档
GET /employee/_search
//使用结构化的方式创建索引
PUT /employee
{
"settings": {
"number_of_replicas": 1,
"number_of_shards": 1
},
"mappings": {
"properties": {
"name":{
"type":"text"
},
"age":{
"type":"integer"
}
}
}
}
//不带条件查询所有记录
GET /employee/_search
{
"query":{
"match_all": {
}
}
}
//分页查询
GET /employee/_search
{
"query":{
"match_all": {
}
},
"from":0,
"size":2
}
//带关键字条件的查询
GET /employee/_search
{
"query":{
"match":{
"name":"cappucicno"
}
}
}
// 排序
GET /employee/_search
{
"query":{
"match_all": {}
},
"sort":[{
"age":{
"order":"desc"}
}
]
}
//带filter
GET /employee/_search
{
"query":{
"bool": {
"filter": [
{"match": {
"name": "兄"
}}
]
}
}
}
//带聚合
GET /employee/_search
{
"query": {"match": {
"name": "兄"
}},
"sort":[
{"age":{"order":"desc"}}
]
, "aggs": {
"group_by_age": {
"terms": {
"field": "age",
"size": 10
}
}
}
}
//新建一个索引
PUT /movie/_doc/1
{
"name":"Eating an apple a day & keeps the doctor away"
}
GET /movie/_search
{
"query": {
"match": {
"name": "awai"
}
}
}
//使用analyze api 查看分词状态
GET /movie/_analyze
{
"field": "name",
"text":"Eating an apple a day & keeps the doctor away"
}
GET /movie/_analyze
{
"field": "name",
"text":"eat"
}
DELETE /movie
//使用结构化的方式重新创建索引
PUT /movie
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1
},
"mappings": {
"properties": {
"name":{
"type": "text"
, "analyzer": "english"
}
}
}
}
GET /movie/_search
{
}
//开始玩转tmdb
PUT /movie
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1
},
"mappings": {
"properties": {
"title":{
"type": "text",
"analyzer": "english"
},
"tagline":{
"type":"text",
"analyzer": "english"
},
"rel