一,安装
直接在github找到elasticsearch-rtf,下载下来,解压
教程
二、概念
集群:一个或者多个节点组织在一起
节点:一个节点是集群中的服务器,由一个名字来标识,通常是一个随机的漫威角色名字
分片:将索引划分为多份的能力,允许水平分割和扩展容量,多个分片响应请求,提高性能和吞吐量
副本:创建分片的一份或多份的能力,在一个节点失败其余节点可以顶上
HTTP方法:刚开始是GET,POST,HEAD,后来又新增了五种 OPTIONS,PUT,DELETE,TRACE,CONNECT
倒排索引 :倒排索引源于实际应用中需要根据属性的值来查找记录。这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引(inverted index)。带有倒排索引的文件我们称为倒排索引文件,简称倒排文件(inverted file)。
倒排索引待解决问题:1.大小写转换问题,如python和PYTHON 应该是同一个词
2.词干抽取,looking和look应该处理为一个词
3.分词,若屏蔽系统应该分词为“屏蔽”、“系统” 还是应该为“屏蔽系统”
4.倒排索引文件过大-压缩编码
二 基础
1 创建索引
一旦创建的索引,分片数是不可以改的,副本数可以改
PUT nga #创建索引
{
"settings": {
"index":{
"number_of_shards":5, #分片数
"number_of_replicas":1 #副本数
}
}
}
2 获取setting信息
GET nga/_settings #获取索引名为nga的setting信息
GET _all/_settings #获取所有索引的setting
GET .kibana,nga/_settings #获取指定索引的setting
3 修改setting
PUT nga/_settings
{
"number_of_replicas": 2
}
4 获取索引信息
#获取索引信息
GET _all
GET nga
5保存文档
PUT nga/job/1 #指明 索引/type/id 也可以写成PUT nga/job/ 将会自动成一个id
{
"title":"python分布式爬虫开发",
"salary_min":15000,
"city":"beijing",
"company":{
"name":"baidu",
"company":"beijingruanjianyuan"
},
"publish_date":"2017-8-21",
"comments":23
}
6 获取
GET nga/job/1 #指定id
GET nga/job/1?_source=title,city #指定id指定字段
GET nga/job/1?_source #获取指定id所有字段
7 修改
方式一、完全覆盖原有内容
PUT nga/job/1 #要指定id
{
"title":"python分布式爬虫开发",
"salary_min":15000,
"company":{
"name":"baidu",
"company":"beijingruanjianyuan"
},
"publish_date":"2017-8-21",
"comments":23
}
方式二:修改指定字段
POST nga/job/1/_update
{
"doc":{ #doc里面放的就是要修改的内容
"comments":5
}
}
7删除
1 删除文档
DELETE nga/job/1
2 删除索引
DELETE nga
3.删除index下的type里面的所有数据
POST lagou/testjob2/_delete_by_query?conflicts=proceed
{
"query": {
"match_all": {}
}
}
三、批量操作
查询type为job1且id为1,job2且id为2的数据,这个方法也可以查不同索引下的数据
GET _mget
{
"docs":[{
"_index":"testdb",
"_type":"job1",
"_id":1
},{
"_index":"testdb",
"_type":"job2",
"_id":1
}
]
}
查询相同index下的不同type还可以这样
GET testdb/_mget
{
"docs":[{
"_type":"job1",
"_id":1
},{
"_type":"job2",
"_id":1
}
]
}
如果type也相同
GET testdb/job1/_mget
{
"docs":[{
"_id":1
},{
"_id":2
}
]
}
这样的情况可以进一步简写:
GET testdb/job1/_mget
{
"ids":[1,2]
}
四、bulk批量操作
批量导入可以合并多个操作,比如index,delete,update,create等等,也可以从一个索引导入到另一个索引
bulk把一次请求的数据发送给一个节点,再给多个分片进行操作,全部执行完成后再返回结果,如果数据量比较大就会造成延迟,所以也不能一次性发送太多
用bulk操作插入数据
POST lagou/testjob/_bulk
{"index":{"_id":1}}
{"salary":10,"title":"python"}
{"index":{"_id":2}}
{"salary":20,"title":"scrapy"}
{"index":{"_id":3}}
{"salary":30,"title":"django"}
{"index":{"_id":4}}
{"salary":40,"title":"elasticsearch"}
五 映射(mapping)
创建索引的时候可以预定字段类型和相关属性
elasticsearch会根据json源数据的基础类型猜测你想要的字段映射,将输入的数据转变成可搜索的索引项,Mapping就是我们自己定义的字段的数据类型,同时告诉elasticsearch如何索引数据,以及是否可以被搜索
作用:会让索引建立更加细致和完善
类型:静态映射 动态映射
内置类型:
Sting类型:text,keyword(string类型在es5开始已经废弃)(keyword不会进行分词)
数字类型:long,inteher,short,byte,double,float
日期类型:date
bool类型:boolean
binary类型:binary
复杂类型:object,nested
geo类型:geo-point(经纬度),geo-shape
专业类型:ip,competion
object:"company":{ "name":"baidu", "company":"beijingruanjianyuan" },
这就是一个object。
nexted:“company”:{
“name”:“baidu”,
“company”:“beijingruanjianyuan”,
“emplyee”:[{“name”:“tom”,“age”:26}],
“emplyee”:[{“name”:“tony”,“age”:23}]
},这里 "emplyee":[{"name":"tom","age":26}], "emplyee":[{"name":"tony","age":23}] },
就是一个nested
属性:
创建索引
PUT test_index
{
"mappings":{
"job":{
"properties":{
"title":{
"type":"text"
},
"salary_min":{
"type":"keyword"
},
"company":{
"properties":{
"name":{
"type":"text"
},
"company_addr":{
"type":"text"
},
"emplyee-count":{
"type":"integer"
}
}
},
"publish_date":{
"type":"date",
"format":"yyyy-MM-dd"
},
"comments":{
"type":"integer"
}
}
}
}
}
已经建好的索引不能修改,可以新增,如果要修改只能先删除原来的索引,再新建索引,再导入数据
查询分类:
基本查询:使用elasticsearch内置的查询条件进行查询
组合查询:把多个查询组合在一起进行复合查询
过滤:查询同时,通过filter条件在不影响打分的条件下筛选
会对搜索进行解析,match匹配指定字段的值,做分词处理以后再做查询
GET test_search/job/_search
{
"query": {
"match": {
"title": "python"
}
}
}
不会对传进来的值进行分词处理
GET test_search/job/_search
{
"query": {
"term": {
"title": "python学习"
}
}
}
GET test_search/job/_search
{
"query": {
"terms": {
"title":[ "python", "java"]
}
}
}
GET test_search/job/_search
{
"query": {
"match": {
"title":"python"
}
},
"from":1, #从第几个结果开始
"size":1 #返回多少条结果
}
GET test_search/job/_search
{
"query": {
"match_all": {
}
}
}
要满足查询关键词中的所有词,比如说“python系统”,要有“python”,还要有“系统”,slop是两个词之间的最大距离
GET test_search/job/_search
{
"query":{
"match_phrase": {
"title": {
"query": "python系统",
"slop":6
}
}
}
}
可以查询多个字段
title,desc 是字段名 title^3意思是title的权重比desc大三倍
GET test_search/job/_search
{
"query":{
"multi_match": {
"query": "python",
"fields":["title^3","desc"]
}
}
}
GET test_search/job/_search
{
"stored_fields": ["title","company"],
"query": {
"match": {
"title": "python"
}
}
}
按comment排序:
GET test_search/job/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"comments": {
"order": "asc"
}
}
]
}
gt:大于 gte:大于等于 lt:小于 lte:小于等于 boost:所属字段的权重
GET test_search/job/_search
{
"query": {
"range": {
"comments": {
"gte": 10,
"lte": 20,
"boost": 1
}
}
}
}
查询时间的
GET test_search/job/_search
{
"query": {
"range": {
"add_time": {
"gte": "2017-4-01",
"lte": "now"
}
}
}
}
wildcard里面的关键词支持通配符
GET test_search/job/_search
{
"query": {
"wildcard": {
"title": {
"value":"pyth*n",
"boost":2.0
}
}
}
}
用bool包括 must should must_not filter来完成
写法如下:
bool{
"filter":[], #字段过滤 不参与打分
"must":[], #里面的字段必须同时满足
"should":[], #只要满足一个或者多个
"must_not":[], #一个都不能满足
}
如果不止一个条件,可以用“[ ]”来包括,只有一个就用"{ }"
例如,查询薪资为20的工作
测试数据:
POST lagou/testjob/_bulk
{"index":{"_id":1}}
{"salary":10,"title":"Python"}
{"index":{"_id":2}}
{"salary":20,"title":"scrapy"}
{"index":{"_id":3}}
{"salary":30,"title":"django"}
{"index":{"_id":4}}
{"salary":40,"title":"elasticsearch"}
sql语句为:select * from testjob where salary=20
GET lagou/testjob/_search
{
"query": {
"bool": {
"must": {
"match_all":{}
},
"filter": {
"term": {
"salary": 20
}
}
}
}
}
此处把term改为match结果也一样,因为salary是int类型,不会被分词,但是在filter里面一些精确的值最好用term来做,也可以查询多个值,如10和20,则改成 “salary”: 20
"must": {
"match_all":{}
}
实际上可以省略,因为是默认查询所有的,但是这里可以改成过滤标题之类的,比如
GET lagou/testjob/_search
{
"query": {
"bool": {
"must": {
"match":{"title":"scrapy"}
},
"filter": {
"match": {
"salary": 20
}
}
}
}
}
关于text类型大小写
GET lagou/testjob/_search
{
"query": {
"bool": {
"must": {
"match_all":{}
},
"filter": {
"term": {
"title": "Python"
}
}
}
}
}
这样是查询不到结果的,把Python改为python就可以查询出来,因为title是text类型的,text字段在做索引的时候会进行分词并且进行大小写转换,这里用term查询,不会对查询的python做预处理,会直接拿这个词去做搜索,但是这里面已经转成小写了所以查询不到。
如果改成match就能查到。
如果一定要用大写来查询,就要在做mapping的时候把这里的title的index设为false,不做倒排索引。但是这样查询就不方便了
查看分析器的解析结果
可以查看分析器的所有解析结果,ik_max_word分析出来的结果集比较大,比较仔细,用ik_smart分析出来的结果是比较小的结果集
GET _analyze
{
"analyzer": "ik_max_word",
"text":"Python网络开发工程师"
}
组合过滤查询
查询薪资等于20或者工作为python的工作,排除薪资为30的
sql:select * from testjob where (salary=20 or title=“Python”) and (salary!=30)
GET lagou/testjob/_search
{
"query": {
"bool": {
"should":[
{"term":{"salary":20}},
{"term":{"title":"python"}}
],
"must_not":{
"term":{"price":30}
}
}
}
}
嵌套查询
sql: select * from testjob where title=“python” or (title=“django” and salary=30)
GET lagou/testjob/_search
{
"query": {
"bool": {
"should": [
{"term":{"title":"python"}},
{
"bool": {
"must":[
{"term":{"title":"django"}},
{"term":{"salary":30}}
]
}
}
]
}
}
}
过滤空和非空
测试数据
POST lagou/testjob2/_bulk
{"index":{"_id":1}}
{"tags":["search"]}
{"index":{"_id":2}}
{"tags":["search","python"]}
{"index":{"_id":3}}
{"other_field":["some data"]}
{"index":{"_id":4}}
{"tags":null}
{"index":{"_id":"5"}}
{"tags":["search",null]}
处理null 空值的方法:
sql:select tags from testjob2 where tags is not NULL
GET lagou/testjob2/_search
{
"query":{
"bool":{
"filter":{
"exists":{
"field":"tags"
}
}
}
}
}
“exists”:{
“field”:“tags”
} 这个组合是固定的,如果要查询不存在的就是
"exists":{ "field":"tags" }
安装
安装es接口:elasticsearch-dsl:进入虚拟环境 输入 install elasticsearch-dsl
官方文档:官方文档 例子在 persistence-doctype
创建一个pipeline
创建一个models文件夹,新建一个es-type的python文件,参考官方文档的例子,在里面写一个duowanType,创建索引,然后连接服务器,代码如下:
from datetime import datetime
from elasticsearch_dsl import DocType, Keyword, Text, Date, Integer
from elasticsearch_dsl.connections import connections
# 连接服务器 允许连接多个服务器
connections.create_connection(hosts=["localhost"])
class DuowanType(DocType):
# 多玩视频文章类型
url = Keyword()
url_id = Keyword()
front_img_url = Keyword()
front_img_url_path = Keyword()
title = Text(analyzer="ik_max_word")
update_time = Date()
author = Text(analyzer="ik_max_word")
playnum = Integer()
comment_count = Integer()
like_count = Integer()
class Meta:
# 定义type index
index = "duowan"
doc_type = "video"
if __name__== "__main__":
# 通过定义的类 直接生成mapping
DuowanType.init()
但是由于我们有不同的items,需要不同的type,所以把type的定义放到items中
import datetime
from scrapy.loader.processors import MapCompose, TakeFirst
from NgaSpider.models.es_types import DuowanType
from w3lib.html import remove_tags
from scrapy.loader import ItemLoader
class NgaItemLoder(ItemLoader):
default_output_processor = TakeFirst()
def update_time(value):
if value:
time = value.replace('上传于', '')
date = time.strip()
else:
date = datetime.datetime.now().date()
return date
def get_nums(value):
if value:
nums = value
else:
nums = 0
if re.match('(.*?)万', nums):
# 字符串含有万字去掉万字 乘以1000
nums_str = str(nums).replace('万', '')
nums = float(nums_str)
nums = nums*10000
if re.match('^[0-9]+,[0-9]+$', str(nums)):
# 字符串含有逗号
nums = float(str(nums).replace(',', '').strip())
else:
nums = float(nums)
return nums
class NgaspiderItem(scrapy.Item):
url = scrapy.Field()
url_id = scrapy.Field()
front_img_url = scrapy.Field()
front_img_url_path = scrapy.Field()
title = scrapy.Field()
update_time = scrapy.Field(
input_processor=MapCompose(update_time)
)
author = scrapy.Field()
playnum_text = scrapy.Field()
playnum = scrapy.Field(
input_processor=MapCompose(get_nums)
)
comment_count = scrapy.Field(
input_processor=MapCompose(get_nums)
)
comment_count_text = scrapy.Field()
like_count = scrapy.Field(
input_processor=MapCompose(get_nums)
)
like_count_text = scrapy.Field()
def save_to_es(self):
duowan = DuowanType()
duowan.url = self['url']
duowan.url_id = self['url_id']
if 'front_img_url' in self:
duowan.front_img_url = self['front_img_url']
duowan.front_img_url_path = self['front_img_url_path']
duowan.title = self['title']
duowan.update_time = self['update_time']
duowan.playnum_text = self['playnum_text']
duowan.playnum = self['playnum']
duowan.author = self['author']
duowan.comment_count = self['comment_count']
duowan.like_count = self['like_count']
# duowan.meta.id = item["url_object_id"]
duowan.save()
return
pipelines中改为:
class ElasticsearchPipeline(object):
# 把爬取的数据保存到elasticsearch
def process_item(self, item, spider):
# 将item转换为es数据
item.save_to_es()
return item
如果type中需要用到html网页内容可以这样写:
from w3lib.html import remove_tags
.....
duowan.html = remove_tags(html内容)