在nodejs中使用ElasticSearch(二)核心概念,应用

核心概念

1、索引(index)

索引在Elasticsearch7之后,相当于关系型数据库中的表,之前相当于关系型数据库中的数据库。

Haskell 复制代码
# 创建索引
PUT /my_index

# 获取指定索引
GET /my_index

# 获取所有索引
GET /_cat/indices?v

# 模糊匹配索引
POST *index*/_search

# 删除索引
DELETE /my_index

# 创建索引,同时指定索引结构
# number_of_shards:主分片数量,数据会被分割到这些分片中,创建后不可修改,需根据数据量预估,默认5
# number_of_replicas:每个主分片的副本数,用于故障转移和提升查询性能,可动态修改,默认1
PUT /student_index
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 1
  },
  "mappings": {
    "properties": {
      "name":{
        "type": "text"
      },
      "age":{
        "type": "integer"
      },
      "enrolled_date":{
        "type": "date"
      }
    }
  }
}

# 修改索引设置
PUT /student_index/_settings
{
  "settings": {
    "number_of_replicas": 2
  }
}

# 添加索引map属性
PUT /student_index/_mapping
{
  "properties":{
    "name":{
      "type":"integer"
    }
  }
}

#设置别名
#refresh_interval:指定了执行刷新操作的频率,默认值是 1s(秒)。
PUT my_index2
{
  "aliases": {
    "mi": {}
  },
  "settings": {
    "refresh_interval": "30s",
    "number_of_shards": 1,
    "number_of_replicas": 1
  }
}

# 多个索引有可以相同的别名,使用这个相同的别名的时候,这些索引的数据可以被一起查询
# 为索引添加别名
POST /_aliases 
{
  "actions":[
    {
      "add":{
        "index":"my_index2",
        "alias":"mi3"
      }
    }
  ]
}


# 批量创建索引增加数据,没有添加,有会报错
POST _bulk
{"create":{"_index":"my_index3","_id":1}}
{"id":1,"title":"hello3 1"}
{"create":{"_index":"my_index4","_id":1}}
{"id":2,"title":"hello4"}

# 批量创建索引增加数据,没有添加,有则更新
POST _bulk
{"index":{"_index":"my_index3","_id":1}}
{"id":1,"title":"hello3 1"}
{"index":{"_index":"my_index3","_id":2}}
{"id":1,"title":"hello3 2"}
{"index":{"_index":"my_index3","_id":3}}
{"id":1,"title":"hello3 3"}
{"index":{"_index":"my_index4","_id":4}}
{"id":2,"title":"hello4"}

2、映射(mapping)

类似于关系型数据库中的表结构,它决定了字段的类型、字段如何被索引和存储等。

Haskell 复制代码
# 设置content字段属性
POST /my_index/_mapping 
{
  "properties":{
    "content":{
      "type":"text",
      "analyzer":"ik_max_word",
      "search_analyzer":"ik_smart"
    }
  }
}

#"type": 指定该字段的类型。在本例中,content 字段的类型是 text。text 类型用于存储需要全文搜索的内容。

#"analyzer": 定义当索引文档时使用的分析器。这里设置的是 "ik_max_word" 分词器,它会将字段内容分词成尽可能多的词语。这在需要高召回率(recall)的搜索场景中特别有用。

#"search_analyzer": 定义在搜索字段时使用的分析器。这里设置的是 "ik_smart" 分词器,它会将查询内容分词成相对少的、但意义完整的词语。这在需要高精确度(precision)的搜索场景中特别有用。

3、文档(document)

文档是存储在索引中的基本单元,相当于关系型数据库中的行。每个文档是一个JSON格式的对象,包含一个或多个字段。

Haskell 复制代码
# 添加数据
POST /my_index/_create/1
{
  "content":"特朗普表示将对进口汽车征收 25% 左右的关税,这对全球汽车产业会带来哪些影响?我国车企该如何应对?"
}

# 查询数据
GET /my_index/_search
{
  "query": {
    "match": {
      "content": "特朗普对进口汽车征收关税"
    }
  }
}

# 获取10条数据
GET /my_index/_search
{
  "size": 10,
  "query": {
    "match_all": {}
  }
}

# 修改数据
POST /my_index/_update/1
{
  "doc": {
    "content": "梁文锋参与发表的 DeepSeek 新论文 NSA 注意力机制,有哪些信息值得关注?会带来哪些影响?"
  }
}

# 删除数据
DELETE /my_index/_doc/1

示例

Haskell 复制代码
# 获取所有索引
GET /_cat/indices?v

DELETE /employee

PUT /employee
{
  "settings": {
    "number_of_replicas": 1,
    "number_of_shards": 1
  },
  "mappings": {
    "properties": {
      "name":{
        "type": "keyword"
      },
      "sex":{
        "type": "integer"
      },
      "age":{
        "type": "integer"
      },
      "address":{
        "type": "text",
        "analyzer": "ik_max_word",
        "fields": {
          "keyword":{
            "type":"keyword"
          }
        }
      },
      "remark":{
        "type": "text",
        "analyzer": "ik_smart",
        "fields": {
          "keyword":{
            "type":"keyword"
          }
        }
      }
    }
  }
}

POST /employee/_bulk
{"index":{"_index":"employee","_id":"1"}}
{"name":"张三","sex":1,"age":25,"address":"广州天河公园","remark":"java developer"}
{"index":{"_index":"employee","_id":"2"}}
{"name":"李四","sex":1,"age":28,"address":"广州荔湾大厦","remark":"java assistant"}
{"index":{"_index":"employee","_id":"3"}}
{"name":"王五","sex":0,"age":20,"address":"广州白云山公园","remark":"php developer"}
{"index":{"_index":"employee","_id":"4"}}
{"name":"赵六","sex":0,"age":22,"address":"长沙橘子洲","remark":"python assistant"}
{"index":{"_index":"employee","_id":"5"}}
{"name":"张龙","sex":0,"age":19,"address":"长沙麓谷企业广场","remark":"java architect"}
{"index":{"_index":"employee","_id":"6"}}
{"name":"赵虎","sex":1,"age":32,"address":"长沙麓谷兴工国际产业园","remark":"java architect "}


# 获取所有数据
GET employee/_search

# 通过id查询
GET employee/_doc/1

# 通过多个id查询
GET employee/_mget
{
  "ids":[1,2,3]
}

#精确匹配
GET /employee/_search
{
  "query": {
    "term": {
      "name": {
        "value": "张三"
      }
    }
  }
}

# 分词检索
GET employee/_search 
{
  "query": {
    "match": {
      "address":"广州白云山"
    }
  }
}

# 范围查询
GET /employee/_search 
{
  "query": {
    "range": {
      "age": {
        "gte": 10,
        "lte": 20
      }
    }
  }
}

# 分页,4~6
GET /employee/_search 
{
  "from": 3,
  "size": 3
}

# 删除id
DELETE /employee/_doc/1

# 批量删除
POST _bulk
{"delete":{"_index":"employee","_id":2}}
{"delete":{"_index":"employee","_id":3}}

POST /employee/_bulk
{"delete":{"_id":4}}
{"delete":{"_id":5}}

# 通过条件删除
POST /employee/_delete_by_query
{
  "query": {
    "match": {
      "address": "广州"
    }
  }
}

# 更新一个字段
POST /employee/_update/1
{
  "doc":{
    "age":28
  }
}

# 批量更新
POST _bulk
{"update":{"_index":"employee","_id":3}}
{"doc":{"age":29}}
{"update":{"_index":"employee","_id":4}}
{"doc":{"age":27}}


# 查询更新
POST /employee/_update_by_query
{
  "query": {
    "term": {
      "name": "张三"
    }
  },
  "script": {
    "source": "ctx._source.age=30"
  }
}

# 保证线程安全更新
# 通过_id获取第一条数据,可查看_id,_seq_no
GET /employee/_doc/1

# 有就更新,没有创建
# 可以先获取_id,_primary_term,再修改
# if_seq_no=13:这是一个乐观并发控制(Optimistic Concurrency Control, OCC)参数,表示只有当文档的序列号(sequence number)为 13 时,才会执行该请求。这是为了防止在并发环境中对同一文档的冲突更新。
# if_primary_term=1:这是另一个乐观并发控制的参数,表示只有在文档的主分片版本号(primary term)为 1 时,才会执行请求。这两个参数结合使用,以确保对文档的更新是基于最新的状态。
POST /employee/_doc/1?if_seq_no=40&if_primary_term=2
{
 "name":"张三",
  "sex":1,
  "age":25
}

使用场景

1、微博、新闻、博客

2、按日期分割存储日志索引

在nodejs中使用ElasticSearch,博客文章搜索,无标准写法,自由发挥。

TypeScript 复制代码
import express, { response, request } from 'npm:express';
import fs from 'node:fs';
import { Client } from 'npm:@elastic/elasticsearch';

const client = new Client({ node: 'http://localhost:9200' });
const app = express();
const port = 3000;

// 解析 JSON 请求体
app.use(express.json());

// 解析 URL 编码表单数据
app.use(express.urlencoded({ extended: true }));

const message: string[] = [
  '梁文锋参与发表的 DeepSeek 新论文 NSA 注意力机制,有哪些信息值得关注?会带来哪些影响?',
  '如何看待浙大转专业机械工程报录比超计算机,总报名人数第一?机械专业前途此前是否被低估了?',
  '京东宣布为外卖骑手缴纳五险一金,系行业首个,如何看待此举?是否会引发外卖行业新的变革?',
  '看完《哪吒之魔童闹海》,有个疑问:「元始天尊」出关后,将如何处置大师兄「无量仙翁」及其阐教弟子?',
  '混天绫、风火轮、乾坤圈、火尖枪...有哪些「仙家法宝」如今已经有了科技平替?我们还能还原哪些「神器」?',
  '广州一街道招聘环卫工人年龄不超过 35 岁,街道办回应系单位用工正常需求,这一年龄限制有必要吗?',
  '中国的本地的常见食用淡水鱼,为什么大都刺多?纯粹是因为自然条件不好吗?',
  '小米集团总裁称小米将在 2 月底正式发布米家中央空调,产品都有哪些亮点?对此你有哪些期待?',
  '如果职场上领导告诉你,有另外一个领导看中了你,想调你去他们部门,你会怎么回答?',
  '特朗普表示将对进口汽车征收 25% 左右的关税,这对全球汽车产业会带来哪些影响?我国车企该如何应对?',
  '阿莫林带曼联 13 场英超 14 分,阿莫林执教的曼联成绩怎么样?如何评价他执教理念和风格?',
  '宇树人形机器人距离成为真正意义上的家务机器人还有多少年?',
  '《哪吒 2》超过《头脑特工队 2》成为全球动画电影票房冠军,你觉得该纪录能保持多久?',
  '蒸蛋要怎么做才嫩滑好吃?',
  '美俄两国同意恢复驻华盛顿和莫斯科大使馆的人员配置,未来将在多领域合作,释放了哪些信号?',
  '如果你是面试官,遇到了家人陪着一起来的面试者,你会不会录取他 (她)?',
  '你玩《天国拯救 2》的时候,遭遇过什么令人感动或者震撼的细节或故事?',
  '健身隔天练一个小时,是不是不如每天练半个小时?',
  '如何评价格力专卖店改名为「董明珠健康家」?品牌强绑个人 IP 带来的是流量还是隐患?',
  '为了让孩子「哪怕再高一公分」,家长花几十万带孩子打生长激素,这种方式安全吗?如何理解家长的身高焦虑?',
];

app.get('/', (_req: request, res: response) => {
  res.send('Hello World!');
});

// 添加文章
app.get('/add/article', async (_req: request, res: response) => {
  // 验证请求是否超时

  // 验证token,ip地址,一些非重要用户信息等

  // 验证cookie,HttpOnly,secure,与token关联,加密标识等

  // 验证请求参数

  // 验证用户权限

  // 防止数据不一致,开启事务,注意和其他接口对表执行修改的顺序,会造成死锁
  // 添加文章到数据

  // 添加文章标签关联,多对多

  // 添加elasticSearch数据,用于分词搜索
  const exists = await client.indices.exists({ index: 'index_article' });

  if (!exists) {
    await client.indices.create({
      index: 'index_article',
      aliases: {
        'article': {},
      },
      mappings: {
        properties: {
          id: { type: 'integer' },
          title: {
            type: 'text',
            analyzer: "ik_max_word",
            fields: { keyword: { type: 'keyword' } },
          },
          content: {
            type: 'text',
            analyzer: "ik_smart",
            fields: { keyword: { type: 'keyword' } },
          },
          like: { type: 'integer' },
          unlike: { type: 'integer' },
          follow: { type: 'integer' },
          createdAt: { type: 'date' },
          updatedAt: { type: 'date' },
        }
      },
      settings: {
        number_of_replicas: 1,
        number_of_shards: 1,
      },
    });
  }

  console.log(await client.count({ index: 'index_article' }));
  let id = await client.search({
    index: 'index_article',
    body: {
      aggs: {
        max_id: {
          max: {
            field: 'id'
          }
        }
      },
      size: 0  // 不需要返回具体的文档,只需要聚合结果
    }
  });

  console.log((id?.aggregations?.max_id as { value: number })?.value);

  const maxId = (id?.aggregations?.max_id as { value: number })?.value ? String((id?.aggregations?.max_id as { value: number })?.value + 1) : Math.random().toString().slice(2, -1);
  const data = {
    id: maxId,
    title: (message[Math.floor(Math.random() * message.length)]),
    content: (message[Math.floor(Math.random() * message.length)]),
    like: 0,
    unlike: 0,
    follow: 0,
    createdAt: new Date().toISOString(),
    updatedAt: new Date().toISOString(),
    aId: 1,
  };

  console.log(await client.index({ index: 'index_article', id: data.id, document: data }));

  // 修改redis缓存

  // 日志记录,可以发送给rabbitmq,kafka等队列工具后续处理,手动控制消息接收

  // 错误处理,最好前面每步都要有,还有修改数据部分执行成功处理

  res.send('Hello World!');
});

// 查询文章
app.get('/search/article', async (req: request, res: response) => {
  // 验证请求是否超时

  // 验证token,ip地址,一些非重要用户信息等

  // 验证cookie,HttpOnly,secure,与token关联,加密标识等

  // 验证请求参数

  // 验证用户权限

  // redis缓存查询

  // elasticsearch查询
  const currentPage = req.query?.currentPage || 1;
  const pageSize = req.query?.pageSize || 5;
  const title = req.query?.title || null;

  type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
  let list: UnwrapPromise<ReturnType<typeof client.search>>;

  if (title) {
    list = await client.search({
      index: 'index_article',
      query: { "match": { "title": title } },
      size: pageSize,
      from: (currentPage - 1) * pageSize,
    });
  } else {
    list = await client.search({
      index: 'index_article',
      size: pageSize,
      from: (currentPage - 1) * pageSize,
    });
  }

  // 数据库查询

  // 日志记录

  // 错误处理

  res.send(list);
});

// 修改文章
app.get('/update/article', async (req: request, res: response) => {
  // 验证请求是否超时

  // 验证token,ip地址,一些非重要用户信息等

  // 验证cookie,HttpOnly,secure,与token关联,加密标识等,内容不能设置太多

  // 验证请求参数

  // 验证用户权限

  // 修改数据库数据

  // 更新redis缓存

  // 更新elasticsearch
  const data = {
    title: (message[Math.floor(Math.random() * message.length)]),
    content: (message[Math.floor(Math.random() * message.length)]),
    like: 0,
    unlike: 0,
    follow: 0,
    createdAt: new Date().toISOString(),
    updatedAt: new Date().toISOString(),
  };

  const qr = await client.updateByQuery({
    index: 'index_article',
    refresh: true,
    "query": {
      "term": { // 精确查找
        "id": req.query?.id || 1,
      },
    },
    "script": {
      "source": `
        ctx._source.title="${data.title}";
        ctx._source.content="${data.content}";
      `
    }
  });

  console.log(qr);

  // 日志记录

  // 错误处理

  res.send('hello world!');
});

// 删除文章
app.get('/delete/article', async (req: request, res: response) => {
  // 验证请求是否超时

  // 验证token,ip地址,一些非重要用户信息等

  // 验证cookie,HttpOnly,secure,与token关联,加密标识等,内容不能设置太多

  // 验证请求参数

  // 验证用户权限

  // 开启事务,注意表执行顺序

  // 删除文章评论

  // 删除文章关联标签

  // 删除文章评论

  // 更新redis缓存

  // 删除elasticsearch中数据
  const qr = await client.deleteByQuery({
    index: 'index_article',
    "query": {
      "term": { // 精确查找
        "id": req.query?.id || 1,
      },
    },
  });
  console.log(qr);
  // 日志记录

  // 错误处理

  res.send('hello world');
});

app.listen(port, () => {
  console.log(`app listening on port ${port}`);
});
相关推荐
桃林春风一杯酒1 小时前
HADOOP_HOME and hadoop.home.dir are unset.
大数据·hadoop·分布式
桃木山人1 小时前
BigData File Viewer报错
大数据·java-ee·github·bigdata
B站计算机毕业设计超人1 小时前
计算机毕业设计Python+DeepSeek-R1高考推荐系统 高考分数线预测 大数据毕设(源码+LW文档+PPT+讲解)
大数据·python·机器学习·网络爬虫·课程设计·数据可视化·推荐算法
数造科技2 小时前
紧随“可信数据空间”政策风潮,数造科技正式加入开放数据空间联盟
大数据·人工智能·科技·安全·敏捷开发
逸Y 仙X5 小时前
Git常见命令--助力开发
java·大数据·git·java-ee·github·idea
元气满满的热码式5 小时前
logstash中的input插件(http插件,graphite插件)
网络·网络协议·http·elasticsearch·云原生
caihuayuan46 小时前
PHP建立MySQL持久化连接(长连接)及mysql与mysqli扩展的区别
java·大数据·sql·spring
B站计算机毕业设计超人6 小时前
计算机毕业设计Hadoop+Spark+DeepSeek-R1大模型民宿推荐系统 hive民宿可视化 民宿爬虫 大数据毕业设计(源码+LW文档+PPT+讲解)
大数据·hadoop·爬虫·机器学习·课程设计·数据可视化·推荐算法
silianpan6 小时前
文档检索服务平台
elasticsearch·搜索引擎·开源