1. Elasticsearch概述
1.1 搜索是什么
概念:用户输入想要的关键词,返回含有该关键词的所有信息。
场景:
1. 互联网搜索:谷歌、百度、各种新闻首页
2. 站内搜索(垂直搜索):企业OA查询订单、人员、部门,
电商网站内部搜索商品(淘宝、京东)场景。
1.2 数据库做搜索弊端
1.2.1 站内搜索(垂直搜索):
(若数据量小,简单搜索,可以使用数据库)
问题出现:
1. 存储问题: 电商网站商品上亿条时,涉及到单表数据过大必须拆分表,
数据库磁盘占用过大必须分库(mycat)。
2. 性能问题:解决上面问题后,若查询“笔记本电脑”等关键词时,上亿条数据
的商品名字段逐行扫描,性能跟不上。
3. 不能分词搜索: 如搜索“笔记本电脑”,只能搜索完全和关键词一样的数据,
那么数据量小时,搜索“笔记电脑”,“电脑”数据要不要给用户。
1.2.2互联网搜索:
肯定不会使用数据库搜索,数据量太大(PB级)
可以使用搜索引擎来解决数据库搜索的问题:
搜索也是一款数据库,搜索可以进行分词搜索---搜索速度非常快
1.3 常见的搜索引擎
【solr】:
Solr是一个独立的企业级搜索应用服务器,
它对外提供类似于Web-service的API接口。
用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引;
也可以通过Http Get操作提出查找请求,并得到XML格式的返回结果,
特点:是一个高性能,采用Java开发,基于Lucene的全文搜索服务器.
同时对其进行了扩展,提供了比Lucene更为丰富的查询语言,
同时实现了可配置、可扩展并对查询性能进行了优化,
并且提供了一个完善的功能管理界面,是一款非常优秀的全文搜索引擎。
【ElasticSearch】:
ElasticSearch是一个基于Lucene的搜索服务器。
它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。
Elasticsearch是用Java语言开发的,并作为Apache许可条款下的开放源码发布,是一种流行的企业级搜索引擎。
ElasticSearch用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。
官方客户端在Java、.NET(C#)、PHP、Python、Apache Groovy、Ruby和许多其他语言中都是可用的。
根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr,也是基于Lucene。
ElasticSearch与Solr比较:
-
当单纯的对已有数据进行搜索时,Solr更快
-
当实时建立索引时,Solr会产生io阻塞,查询性能较差,ElasticSearch具有明显的优势
-
随着数据量的增加,Solr的搜索效率会变得更低,而ElasticSearch却没有明显的变化
总结:
1. es基本是开箱即用(解压就可以用!) ,非常简单。Solr 安装略微复杂一丢丢!
2. Solr 利用Zookeeper进行分布式管理,而 Elasticsearch自身带有分布式协调管理功能.
3. Solr 支持更多格式的数据,比如JSON、XML、CSV ,而 Elasticsearch仅支持json文件格式。
4. Solr 官方提供的功能更多,而Elasticsearch本身更注重于核心功能,高级功能多有第三方插件提供,例如图形化界面需要kibana支撑
5. Solr 查询快,但更新索引时慢(即插入删除慢) ,用于电商等查询多的应用;
6. ES建立索引快(即查询慢) ,即实时性查询快,用于facebook新浪等搜索。
7. Solr是传统搜索应用的有力解决方案,但Elasticsearch更适用于 新兴的实时搜索应用。
8. Solr比较成熟,有一个更大,更成熟的用户、开发和贡献者社区, 而Elasticsearch相对开发维护者较少,更新太快,学习使用成本较高。
1.4 Elasticsearch介绍
The Elastic Stack, 包括 Elasticsearch【搜索,分析】、 Kibana【可视化】、 Beats 和 Logstash【数据的搜集】(也称为 ELK Stack)。
能够安全可靠地获取任何来源、任何格式的数据,然后实时地对数据进行搜索、分析和可视化。
Elaticsearch,简称为 ES, ES 是一个开源的高扩展的分布式全文搜索引擎, 是整个 ElasticStack 技术栈的核心。
它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理 PB 级别的数据
它是搜索的一款数据库,支持的数据格式是json.
1.5 Elasticsearch的使用场景
国外:
1. 维基百科,类似百度百科,“网络七层协议”的维基百科,全文检索,高亮,搜索推荐
2. Stack Overflow(国外的程序讨论论坛),相当于程序员的贴吧。遇到it问题去上面发帖,热心网友下面回帖解答。
3. GitHub(开源代码管理),搜索上千亿行代码。
4. 电商网站,检索商品
5. 日志数据分析,logstash采集日志,ES进行复杂的数据分析(ELK技术elasticsearch+logstash+kibana
6. 商品价格监控网站,用户设定某商品的价格阈值,当低于该阈值的时候,
发送通知消息给用户,比如说订阅《java编程思想》的监控,如果价格低于27块钱,就通知我,我就去买
7. BI系统,商业智能(Business Intelligence)。大型连锁超市,分析全国网点传回的数据,
分析各个商品在什么季节的销售量最好、利润最高。
成本管理,店面租金、员工工资、负债等信息进行分析。从而部署下一个阶段的战略目标。
国内:
1. 百度搜索,第一次查询,使用es。
2. OA、ERP系统站内搜索。
2. ES的安装
步骤:
(1)安装JDK,至少1.8.0_73以上版本,验证:java -version。
(2)下载和解压缩Elasticsearch安装包,查看目录结构。
下载地址:https://www.elastic.co/cn/downloads/past-releases/
这俩版本即可
目录结构
解压后,进入 bin 文件目录,点击 elasticsearch.bat 文件启动 ES 服务 。
注意:
9300 端口为 Elasticsearch 集群间组件的通信端口, 9200 端口为浏览器访问的 http协议 RESTful 端口。
打开浏览器,输入地址: http://localhost:9200,测试返回结果.
返回结果如下:
3. Kibana的安装
上面已经下载了kibanna直接解压 ,启动Kibana:bin目录下的kibana.bat
注意这个启动有点慢
用浏览器访问 http://localhost:5601
点这个小扳手之后就可以写相关代码了
4. ES中常见的概念
Elasticsearch 是面向文档型数据库,一条数据在这里就是一个文档。
为了方便理解,可以将 Elasticsearch 里存储文档数据和关系型数据库MySQL 存储数据的概念进行一个类比
Index 可以看做一个库,
Types 相当于表,
Documents 则相当于表的行。
Types 的概念已经被逐渐弱化,
Elasticsearch 6.X 中,一个 index 下已经只能包含一个type,
Elasticsearch 7.X 中, Type 的概念已经被删除了。
5. ES常用API接口
5.1 基本操作
5.1.1 创建一个索引
#创建索引的第一种方式
#创建索引并往索引中添加一条文档
#结构 这里的1 表示id为1 若不加id 则为随机值
# PUT /索引名称/类型名称/1
# {
# 数据
# }
PUT /qy152/student/1
{
"name":"张三",
"age":18
}
#创建索引的第二种方式
#创建索引并指定具体字段的数据类型
PUT /qy153
{
"mappings": {
"properties": {
"name":{
"type": "text"
},
"age":{
"type": "integer"
}
}
}
}
字段数据类型
字符串类型 :text、 keyword
- text:支持分词,全文检索,支持模糊、精确查询,
不支持聚合,排序 操作;
text类型的最大支持的字符长度无限制,适合大字段存储;
- keyword:不进行分词,直接索引、支持模糊、支持精确匹配,支持聚合、排序操作。
最大支持的长度为——32766个UTF-8 类型的字符,
可以通过设置ignore_above指定自持字符长度,
超过给定长度后的数据将不被索引,无法通过term精确匹配检索返回结果。
数值型 :long、Integer、short、byte、double、float、half float、scaled float
日期类型:date
布尔类型:boolean
二进制类型:binary
5.1.2 删除索引
#删除索引
DELETE /qy153
5.1.3 查询全部索引
GET /_cat/indices?v
5.1.4 查询具体索引结构
GET /qy151
5.1.5 添加文档
#指定id值 --该方式和建立索引一样
PUT /qy153/student/1
{
"name":"李四",
"age":25
}
#不指定id值
POST /qy153/student
{
"name":"王五",
"age":26
}
5.1.6 添加文档
# 删除文档 -- 根据id
DELETE /qy153/student/1
5.1.7 修改文档
#这种修改 必须指定所有列 若指定部分列 则原来的列会消失 如下图
PUT /qy153/student/2
{
"name":"李四",
"age":16
}
#只修改部分列
POST /qy153/student/3/_update
{
"doc":{
"name":"李四"
}
}
所以
PUT /索引名称/类型名称/id值
{
数据
}
这种结构可用于创建索引,添加文档,修改文档
若不存在则创建,若存在则修改
5.1.8 查询文档
查询的提交方式必须为GET
5.1.8.1 根据id查询
#GET /索引名称/类型名称/id值
GET /qy153/student/1
5.1.8.2 查询全部
#GET /索引名称/类型名称/_search
GET /qy153/student/_search
5.1.8.3 条件查询
#GET /索引名称/类型名称/_search?q=字段名:值
#两种查询方式
#第一种 查询年龄为15的
GET /qy153/student/_search?q=age:15
#第二种查询 使用match匹配 (type也可以省略)
GET /qy153/student/_search
{
"query": {
"match": {
"age": 15
}
}
}
5.1.8.4 范围(range)、匹配(match)、term(精准匹配)
# 数据准备 创建索引 插入三条数据
PUT /user/_doc/1
{
"name":"刘民谐",
"age": 18,
"desc": ["有趣","幽默","开朗"]
}
PUT /user/_doc/2
{
"name":"马老师",
"age": 18,
"desc": ["严谨","冷漠"]
}
PUT /user/_doc/3
{ "name":"流油",
"age": 3,
"desc":["美丽","年轻","苗条"]
}
PUT /user/_doc/4
{
"name":"刘民X",
"age": 18,
"desc": ["有趣","幽默","开朗"]
}
GET /user
索引结构
可以看到name是keywords类型
Ⅰ. 范围匹配:range
range 查询可同时提供包含(inclusive)和不包含(exclusive)这两种范围表达式,
可供组合的选项如下:
gt: > 大于(greater than)
lt: < 小于(less than)
gte: >= 大于或等于(greater than or equal to)
lte: <= 小于或等于(less than or equal to)
#查询年龄大于3的人
GET /user/_search
{
"query": {
"range": {
"age": {
"gt":3
}
}
}
}
Ⅱ. 匹配数组 match
match匹配会拆词 就是会将一个像搜索的词分为一个一个
如:查询刘民谐这个人 它就会把刘民谐分为'刘' 、'民'、'谐' 然后去匹配
这里注意 汉字会被拆分 但是英文就仅仅是一个词 不会拆分
#查询刘民谐
GET /user/_search
{
"query": {
"match": {
"name": "刘民谐"
}
}
}
Ⅲ. 精准匹配 term
精准匹配不会像match匹配,精准匹配不会拆词
所以查询刘民谐就只有一条记录
#查询刘民谐
GET /user/_search
{
"query": {
"term": {
"name.keyword": "刘民谐"
}
}
}
5.1.8.5 查询部分列
使用 _source
#查询部分列 查询年龄为18的人并且只查询其name和desc 使用_source
GET /user/_search
{
"query":{
"match": {
"age": "18"
}
},
"_source": ["name","desc"]
}
5.1.8.6 分页查询
#显示第二页的数据 每页显示1条
GET /user/_search
{
"query":{
"range": {
"age":{
"gt":16
}
}
},
"from": 1,
"size":1
}
5.1.8.7 多条件查询
(1) 使用must ===>等价于and
GET /user/_search
{
"query":{
"bool": {
"must": [
{
"match": {
"name": "刘"
}
},
{
"match": {
"age": "18"
}
}
]
}
}
}
(2) should ===>等价于 or
GET /user/_search
{
"query":{
"bool": {
"should": [
{
"match": {
"name": "刘"
}
},
{
"match": {
"age": "18"
}
}
]
}
}
}
(3) must_not ===>等价于 ! 即取反
GET /user/_search
{
"query":{
"bool": {
"must_not": [
{
"match": {
"age": "18"
}
}
]
}
}
}
5.1.8.8 排序
#查询年龄大于16的人且按年龄降序排列
GET /user/_search
{
"query":{
"range": {
"age":{
"gt":2
}
}
},
"sort":[
{
"age":{
"order":"desc"
}
}
],
"from": 0,
"size":10
}
5.1.8.9 高亮显示
#默认高亮
GET /user/_search
{
"query": {
"match": {
"name": "刘"
}
},
"highlight": {
"fields": {
"name":{}
}
}
}
#自定义高亮
GET /user/_search
{
"query": {
"match": {
"name": "刘"
}
},
"highlight": {
"pre_tags": "<font color='red'>",
"post_tags": "</font>",
"fields": {
"name":{}
}
}
}
6. springboot整合ES
(1)创建一个Springboot工程并加入相关的依赖 注意springboot的版本改为2.3.12.RELEASE
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
(2) 创建一个配置,获取ES工具类对象。
@Configuration
public class ESConfig {
@Bean
public RestHighLevelClient client(){
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(new HttpHost("127.0.0.1",9200,"http")));
return client;
}
}
(3)可进行相关对ES操作
6.1 ES相关操作
6.1.1 索引相关操作
@Autowired
private RestHighLevelClient client;
@Test
void test01() throws Exception{
CreateIndexRequest createIndexRequest = new CreateIndexRequest("demo01");
CreateIndexResponse createIndexResponse = client.indices().create(createIndexRequest, RequestOptions.DEFAULT);
System.out.println(createIndexResponse.isAcknowledged( ));
}
@Test
void test02() throws Exception{
DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("demo01");
AcknowledgedResponse delete = client.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
System.out.println(delete.isAcknowledged());
}
@Test
void test03() throws Exception{
GetIndexRequest getIndexRequest = new GetIndexRequest("demo01");
boolean exists = client.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
System.out.println(exists);
}
6.1.2 文档相关操作
(1)添加文档
@Test
void test04() throws Exception{
IndexRequest indexRequest = new IndexRequest("demo01");
indexRequest.id("1");
indexRequest.source(JSON.toJSONString(new User("张三","北京",18)), XContentType.JSON);
IndexResponse indexResponse = client.index(indexRequest, RequestOptions.DEFAULT);
System.out.println(indexResponse.getResult());
}
}
(2)查询文档–通过id
@Test
void test05() throws Exception{
GetRequest getRequest = new GetRequest("demo01");
getRequest.id("1");
GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
String string = getResponse.getSourceAsString();
User user = JSON.parseObject(string, User.class);
System.out.println(user);
Map<String, Object> sourceAsMap = getResponse.getSourceAsMap();
System.out.println(sourceAsMap.get("address"));
}
(3)判断文档是否存在
@Test
void test06() throws Exception{
GetRequest getRequest = new GetRequest("demo01");
getRequest.id("1");
boolean exists = client.exists(getRequest, RequestOptions.DEFAULT);
System.out.println(exists);
}
(4)删除文档
@Test
void test07() throws Exception{
DeleteRequest deleteRequest = new DeleteRequest("demo01");
deleteRequest.id("1");
DeleteResponse deleteResponse = client.delete(deleteRequest, RequestOptions.DEFAULT);
System.out.println(deleteResponse.getResult());
}
(5)修改文档
@Test
void test08() throws Exception{
UpdateRequest updateRequest = new UpdateRequest("demo01","1");
User user = new User();
user.setName("李四");
updateRequest.doc(JSON.toJSONString(user),XContentType.JSON);
UpdateResponse update = client.update(updateRequest, RequestOptions.DEFAULT);
System.out.println(update.getResult());
}
(6)批量添加文档
@Test
void test09() throws Exception{
BulkRequest bulkRequest = new BulkRequest("demo01");
List<User> list = new ArrayList<>();
list.add(new User("1","张三","北京",18));
list.add(new User("2","张三三","上海",19));
list.add(new User("3","李四","北京",18));
list.add(new User("4","李四四","上海",19));
list.add(new User("5","王五","北京",18));
list.add(new User("6","王五五","上海",19));
list.stream().forEach(item->bulkRequest.add(new IndexRequest().id(item.getId()).source(JSON.toJSONString(item),XContentType.JSON)));
BulkResponse bulk = client.bulk(bulkRequest, RequestOptions.DEFAULT);
System.out.println(bulk.hasFailures());
}
(7)条件查询
步骤:
1. 搜索请求对象SearchRequest
2. 构建一个条件对象SearchSourceBuilder
3. 把条件对象放入搜索请求对象中
4. 执行搜索功能
@Test
void test10() throws Exception{
SearchRequest searchRequest = new SearchRequest("demo01");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "王");
SearchSourceBuilder query = sourceBuilder.query(termQueryBuilder);
SearchRequest source = searchRequest.source(query);
SearchResponse search = client.search(source, RequestOptions.DEFAULT);
SearchHit[] hits = search.getHits().getHits();
Arrays.stream(hits).forEach(item-> System.out.println(item.getSourceAsString()));
}
}
@Test
void test11() throws Exception{
SearchRequest searchRequest = new SearchRequest("demo01");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder must = QueryBuilders
.boolQuery()
.must(QueryBuilders.rangeQuery("age").gt(17))
.must(QueryBuilders.matchQuery("name", "王"));
SearchSourceBuilder query = sourceBuilder.query(must);
sourceBuilder.from(0);
sourceBuilder.size(10);
sourceBuilder.sort("age", SortOrder.DESC);
HighlightBuilder highlightBuilder = new HighlightBuilder();
HighlightBuilder field = highlightBuilder.field("name");
highlightBuilder.preTags("<font color='red'>");
highlightBuilder.postTags("</font>");
sourceBuilder.highlighter(highlightBuilder);
SearchRequest source = searchRequest.source(query);
SearchResponse search = client.search(source, RequestOptions.DEFAULT);
SearchHit[] hits = search.getHits().getHits();
System.out.println("总条数: "+search.getHits().getTotalHits().value);
Arrays.stream(hits).forEach(item-> System.out.println(item.getSourceAsString()));
Arrays.stream(hits).forEach(item-> System.out.println(item.getHighlightFields()));
}
}
7.综合案例–爬取京东数据存储在es中并结合vue显示出来
(1)引入依赖
这里注意springboot的版本要低一点 <version>2.3.12.RELEASE</version>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.11.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
(2)在kibana中建立索引
PUT /qy151-jd
{
"mappings": {
"properties": {
"title":{
"type": "text",
"analyzer": "ik_max_word"
},
"price":{
"type":"keyword"
},
"imgUrl":{
"type": "keyword"
}
}
},
"settings": {
"index.analysis.analyzer.default.type":"ik_max_word"
}
}
(3)工具类准备
public class HtmlParseUtil {
public static List<Product> parseJd(String keyword) throws Exception {
String path="https://search.jd.com/Search?keyword="+keyword;
//Document整个网页对象
Document document = Jsoup.parse(new URL(path), 30000);
Element j_goodsList = document.getElementById("J_goodsList");
Elements li = j_goodsList.getElementsByTag("li");
List<Product> list=new ArrayList<>();
for (Element element:li){
String pprice = element.getElementsByClass("p-price").eq(0).text();
String pname = element.getElementsByClass("p-name").eq(0).text();
String img = element.getElementsByTag("img").eq(0).attr("data-lazy-img");
list.add(new Product(pname,pprice,img));
}
return list;
}
}
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Result {
private Integer code;
private String msg;
private Object data;
}
(4)实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {
private String title;
private String price;
private String imgUrl;
}
(5)controller层
@RestController
@RequestMapping("/product")
@CrossOrigin
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping ("/export/{keyword}")
public Result export(@PathVariable String keyword) throws Exception{
return productService.export(keyword);
}
@PostMapping("/search/{keyword}/{currentPage}/{pageSize}")
public Result search(@PathVariable String keyword,@PathVariable Integer currentPage,@PathVariable Integer pageSize) throws Exception{
return productService.search(keyword,currentPage,pageSize);
}
}
(5)业务层service
@Service
public class ProductService {
@Autowired
private RestHighLevelClient restHighLevelClient;
/*存储es中爬取的数据*/
public Result export(String keyword) throws Exception {
List<Product> productList = HtmlParseUtil.parseJd(keyword);
//这里先在kibana上建立这个索引 上述代码存在
BulkRequest bulkRequest = new BulkRequest("qy151-jd");
//批量添加
productList.stream().forEach(item->bulkRequest.add(new IndexRequest().source(JSON.toJSONString(item),XContentType.JSON)));
BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
if(bulk.hasFailures()){
return new Result(5000,"批量添加失败",null);
}else{
return new Result(2000,"批量添加成功",null);
}
}
/*从es中搜索缓存的数据*/
public Result search(String keyword,Integer currentPage,Integer pageSize) throws Exception{
SearchRequest searchRequest = new SearchRequest("qy151-jd");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//设置查询条件
MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("title", keyword);
searchSourceBuilder.query(queryBuilder);
//设置分页
searchSourceBuilder.from((currentPage-1)*pageSize);
searchSourceBuilder.size(pageSize);
//设置高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("title");
highlightBuilder.preTags("<font style='color:red;font-size:14px;'>");
highlightBuilder.postTags("</font>");
searchSourceBuilder.highlighter(highlightBuilder);
searchRequest.source(searchSourceBuilder);
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits hits = search.getHits();
//总条数
long totalHits = hits.getTotalHits().value;
List<Map<String,Object>> result = new ArrayList<>();
SearchHit[] hitsHits = hits.getHits();
for(SearchHit hit:hitsHits){
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
//获取高亮字段
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
Text[] texts = highlightFields.get("title").getFragments();
StringBuffer sb = new StringBuffer();
Arrays.stream(texts).forEach(item->sb.append(item));
//替换原来的title中值始得现在的值有高亮效果
sourceAsMap.put("title",sb);
//将总条数保存在map中
sourceAsMap.put("total",totalHits);
result.add(sourceAsMap);
}
return new Result(2000,"搜索成功",result);
}
}
(6)前端页面
<template>
<div class="page">
<div id="app" class=" mallist tmall- page-not-market ">
<div id="header" class=" header-list-app">
<div class="headerLayout">
<div class="headerCon ">
<h1 id="mallLogo">
<img src="../assets/logo.webp" width="115px" alt="">
</h1>
<div class="header-extra">
<div id="mallSearch" class="mall-search">
<form name="searchTop" class="mallSearch-form clearfix">
<fieldset>
<legend>天猫搜索</legend>
<div class="mallSearch-input clearfix">
<div class="s-combobox" id="s-combobox-685">
<div class="s-combobox-input-wrap">
<input v-model="keyword" type="text" autocomplete="off" id="mq"
class="s-combobox-input" aria-haspopup="true">
</div>
</div>
<button type="submit" @click.prevent="searchKey" id="searchbtn">搜索</button>
</div>
</fieldset>
</form>
<ul class="relKeyTop">
<li><a>老闫说Java</a></li>
<li><a>老闫说前端</a></li>
<li><a>老闫说Linux</a></li>
<li><a>老闫说大数据</a></li>
<li><a>老闫聊理财</a></li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div id="content">
<div class="main">
<form class="navAttrsForm">
<div class="attrs j_NavAttrs" style="display:block">
<div class="brandAttr j_nav_brand">
<div class="j_Brand attr">
<div class="attrKey">
品牌
</div>
<div class="attrValues">
<ul class="av-collapse row-2">
<li><a href="#"> 老闫说 </a></li>
<li><a href="#"> Java </a></li>
</ul>
</div>
</div>
</div>
</div>
</form>
<div class="filter clearfix">
<a class="fSort fSort-cur">综合<i class="f-ico-arrow-d"></i></a>
<a class="fSort">人气<i class="f-ico-arrow-d"></i></a>
<a class="fSort">新品<i class="f-ico-arrow-d"></i></a>
<a class="fSort">销量<i class="f-ico-arrow-d"></i></a>
<a class="fSort">价格<i class="f-ico-triangle-mt"></i><i class="f-ico-triangle-mb"></i></a>
</div>
<div class="view grid-nosku" >
<div class="product" v-for="item in results">
<div class="product-iWrap">
<div class="productImg-wrap">
<a class="productImg">
<img :src="item.imgUrl">
</a>
</div>
<p class="productPrice">
<em>{{item.price}}</em>
</p>
<p class="productTitle">
<a v-html="item.title"> </a>
</p>
<div class="productShop">
<span>店铺: 老闫说Java </span>
</div>
<p class="productStatus">
<span>月成交<em>999笔</em></span>
<span>评价 <a>3</a></span>
</p>
</div>
</div>
</div>
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="pageSizes"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "jd",
data(){
return {
keyword: '',
results:[],
pageSizes:[5,10,15,20],
currentPage:1,
pageSize:15,
total:0,
}
},
methods:{
handleSizeChange(val) {
this.pageSize = val;
this.searchKey();
},
handleCurrentChange(val) {
this.currentPage = val;
this.searchKey();
},
searchKey(){
var keyword = this.keyword;
this.$http.post('product/search/'+keyword+"/"+this.currentPage+"/"+this.pageSize).then(response=>{
console.log(response.data)
this.results=response.data.data;
this.total = response.data.data[0].total;
})
}
}
}
</script>
<style>
body,button,fieldset,form,h1,input,legend,li,p,ul{margin:0;padding:0}body,button,input{font:12px/1.5 tahoma,arial,"\5b8b\4f53";-ms-overflow-style:scrollbar}button,h1,input{font-size:100%}em{font-style:normal}ul{list-style:none}a{text-decoration:none}a:hover{text-decoration:underline}legend{color:#000}fieldset,img{border:0}#content,#header{margin-left:auto;margin-right:auto}html{zoom:expression(function(ele){ ele.style.zoom = "1"; document.execCommand("BackgroundImageCache", false, true); }(this))}@font-face{font-family:mui-global-iconfont;src:url(//at.alicdn.com/t/font_1401963178_8135476.eot);src:url(//at.alicdn.com/t/font_1401963178_8135476.eot?#iefix) format('embedded-opentype'),url(//at.alicdn.com/t/font_1401963178_8135476.woff) format('woff'),url(//at.alicdn.com/t/font_1401963178_8135476.ttf) format('truetype'),url(//at.alicdn.com/t/font_1401963178_8135476.svg#iconfont) format('svg')}#mallPage{width:auto;min-width:990px;background-color:transparent}#content{width:990px;margin:auto}#mallLogo{float:left;z-index:9;padding-top:28px;width:280px;height:64px;line-height:64px;position:relative}.page-not-market #mallLogo{width:400px}.clearfix:after,.clearfix:before,.headerCon:after,.headerCon:before{display:table;content:"";overflow:hidden}#mallSearch legend{display:none}.clearfix:after,.headerCon:after{clear:both}.clearfix,.headerCon{zoom:1}#mallPage #header{margin-top:-30px;width:auto;margin-bottom:0;min-width:990px;background:#fff}#header{height:122px;margin-top:-26px!important;background:#fff;min-width:990px;width:auto!important;position:relative;z-index:1000}#mallSearch #mq,#mallSearch fieldset,.mallSearch-input{position:relative}.headerLayout{width:990px;padding-top:26px;margin:0 auto}.header-extra{overflow:hidden}#mallSearch{float:right;padding-top:25px;width:390px;overflow:hidden}.mallSearch-form{border:solid #FF0036;border-width:3px 0 3px 3px}.mallSearch-input{background:#fff;height:30px}#mallSearch #mq{color:#000;margin:0;z-index:2;width:289px;height:20px;line-height:20px;padding:5px 3px 5px 5px;outline:0;border:none;font-weight:900;background:url(data:image/gif;base64,R0lGODlhAQADAJEAAObm5t3d3ff39wAAACH5BAAAAAAALAAAAAABAAMAAAICDFQAOw==) repeat-x;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}#mallSearch button{position:absolute;right:0;top:0;width:90px;border:0;font-size:16px;letter-spacing:4px;cursor:pointer;color:#fff;background-color:#FF0036;height:30px;overflow:hidden;font-family:'\5FAE\8F6F\96C5\9ED1',arial,"\5b8b\4f53"}#mallSearch .s-combobox{height:30px}#mallSearch .s-combobox .s-combobox-input:focus{outline:0}button::-moz-focus-inner{border:0;padding:0;margin:0}.page-not-market #mallSearch{width:540px!important}.page-not-market #mq{width:439px!important}
#mallSearch{float:none}.page-not-market #mallLogo{width:280px}.header-list-app #mallSearch{width:448px!important}.header-list-app #mq{width:347px!important}@media (min-width:1210px){#header .headerCon,#header .headerLayout,.main{width:1190px!important}.header-list-app #mallSearch{width:597px!important}.header-list-app #mq{width:496px!important}}@media (min-width:600px) and (max-width:800px) and (orientation:portrait){.pg .page{min-width:inherit!important}.pg #mallPage,.pg #mallPage #header{min-width:740px!important}.pg #header .headerCon,.pg #header .headerLayout,.pg .main{width:740px!important}.pg #mallPage #mallLogo{width:260px}.pg #header{min-width:inherit}.pg #mallSearch .mallSearch-input{padding-right:95px}.pg #mallSearch .s-combobox{width:100%!important}.pg #mallPage .header-list-app #mallSearch{width:auto!important}.pg #mallPage .header-list-app #mallSearch #mq{width:100%!important;padding:5px 0 5px 5px}}i{font-style:normal}.main,.page{position:relative}.page{overflow:hidden}@font-face{font-family:tm-list-font;src:url(//at.alicdn.com/t/font_1442456441_338337.eot);src:url(//at.alicdn.com/t/font_1442456441_338337.eot?#iefix) format('embedded-opentype'),url(//at.alicdn.com/t/font_1442456441_338337.woff) format('woff'),url(//at.alicdn.com/t/font_1442456441_338337.ttf) format('truetype'),url(//at.alicdn.com/t/font_1442456441_338337.svg#iconfont) format('svg')}::selection{background:rgba(0,0,0,.1)}*{-webkit-tap-highlight-color:rgba(0,0,0,.3)}b{font-weight:400}.page{background:#fff;min-width:990px}#content{margin:0!important;width:100%!important}.main{margin:auto;width:990px}.main img{-ms-interpolation-mode:bicubic}.fSort i{background:url(//img.alicdn.com/tfs/TB1XClLeAY2gK0jSZFgXXc5OFXa-165-206.png) 9999px 9999px no-repeat}#mallSearch .s-combobox{width:auto}::-ms-clear,::-ms-reveal{display:none}.attrKey{white-space:nowrap;text-overflow:ellipsis}.attrs{border-top:1px solid #E6E2E1}.attrs a{outline:0}.attr{background-color:#F7F5F5;border-color:#E6E2E1 #E6E2E1 #D1CCC7;border-style:solid solid dotted;border-width:0 1px 1px}.attr ul:after,.attr:after{display:block;clear:both;height:0;content:' '}.attrKey{float:left;padding:7px 0 0;width:10%;color:#B0A59F;text-indent:13px}.attrKey{display:block;height:16px;line-height:16px;overflow:hidden}.attrValues{position:relative;float:left;background-color:#FFF;width:90%;padding:4px 0 0;overflow:hidden}.attrValues ul{position:relative;margin-right:105px;margin-left:25px}.attrValues ul.av-collapse{overflow:hidden}.attrValues li{float:left;height:22px;line-height:22px}.attrValues li a{position:relative;color:#806F66;display:inline-block;padding:1px 20px 1px 4px;line-height:20px;height:20px;white-space:nowrap}.attrValues li a:hover{color:#ff0036;text-decoration:none}.brandAttr .attr{border:2px solid #D1CCC7;margin-top:-1px}.brandAttr .attrKey{padding-top:9px}.brandAttr .attrValues{padding-top:6px}.brandAttr .av-collapse{overflow:hidden;max-height:60px}.brandAttr li{margin:0 8px 8px 0}.brandAttr li a{text-overflow:ellipsis;overflow:hidden}.navAttrsForm{position:relative}.relKeyTop{padding:4px 0 0;margin-left:-13px;height:16px;overflow:hidden;width:100%}.relKeyTop li{display:inline-block;border-left:1px solid #ccc;line-height:1.1;padding:0 12px}.relKeyTop li a{color:#999}.relKeyTop li a:hover{color:#ff0036;text-decoration:none}.filter i{display:inline-block;overflow:hidden}.filter{margin:10px 0;padding:5px;position:relative;z-index:10;background:#faf9f9;color:#806f66}.filter i{position:absolute}.filter a{color:#806f66;cursor:pointer}.filter a:hover{color:#ff0036;text-decoration:none}.fSort{float:left;height:22px;line-height:20px;line-height:24px\9;border:1px solid #ccc;background-color:#fff;z-index:10}.fSort{position:relative}.fSort{display:inline-block;margin-left:-1px;overflow:hidden;padding:0 15px 0 5px}.fSort:hover,a.fSort-cur{color:#ff0036;background:#F1EDEC}.fSort i{top:6px;right:5px;width:7px;height:10px;line-height:10px}.fSort .f-ico-arrow-d{background-position:-22px -23px}.fSort-cur .f-ico-arrow-d,.fSort:hover .f-ico-arrow-d{background-position:-30px -23px}i.f-ico-triangle-mb,i.f-ico-triangle-mt{border:4px solid transparent;height:0;width:0}i.f-ico-triangle-mt{border-bottom:4px solid #806F66;top:2px}i.f-ico-triangle-mb{border-top:4px solid #806F66;border-width:3px\9;right:6px\9;top:12px}:root i.f-ico-triangle-mb{border-width:4px\9;right:5px\9}i.f-ico-triangle-mb,i.f-ico-triangle-mt{border:4px solid transparent;height:0;width:0}i.f-ico-triangle-mt{border-bottom:4px solid #806F66;top:2px}i.f-ico-triangle-mb{border-top:4px solid #806F66;border-width:3px\9;right:6px\9;top:12px}:root i.f-ico-triangle-mb{border-width:4px\9;right:5px\9}.view:after{clear:both;content:' '}.productImg,.productPrice em b{vertical-align:middle}.product{position:relative;float:left;padding:0;margin:0 0 20px;line-height:1.5;overflow:visible;z-index:1}.product:hover{overflow:visible;z-index:3;background:#fff}.product-iWrap{position:absolute;background-color:#fff;margin:0;padding:4px 4px 0;font-size:0;border:1px solid #f5f5f5;border-radius:3px}.product-iWrap *{font-size:12px}.product:hover .product-iWrap{height:auto;margin:-3px;border:4px solid #ff0036;border-radius:0;-webkit-transition:border-color .2s ease-in;-moz-transition:border-color .2s ease-in;-ms-transition:border-color .2s ease-in;-o-transition:border-color .2s ease-in;transition:border-color .2s ease-in}.productPrice,.productShop,.productStatus,.productTitle{display:block;overflow:hidden;margin-bottom:3px}.view:after{display:block}.view{margin-top:10px}.view:after{height:0}.productImg-wrap{display:table;table-layout:fixed;height:210px;width:100%;padding:0;margin:0 0 5px}.productImg-wrap a,.productImg-wrap img{max-width:100%;max-height:210px}.productImg{display:table-cell;width:100%;text-align:center}.productImg img{display:block;margin:0 auto}.productPrice{font-family:arial,verdana,sans-serif!important;color:#ff0036;font-size:14px;height:30px;line-height:30px;margin:0 0 5px;letter-spacing:normal;overflow:inherit!important;white-space:nowrap}.productPrice *{height:30px}.productPrice em{float:left;font-family:arial;font-weight:400;font-size:20px;color:#ff0036}.productPrice em b{margin-right:2px;font-weight:700;font-size:14px}.productTitle{display:block;color:#666;height:14px;line-height:12px;margin-bottom:3px;word-break:break-all;font-size:0;position:relative}.productTitle *{font-size:12px;font-family:\5FAE\8F6F\96C5\9ED1;line-height:14px}.productTitle a{color:#333}.productTitle a:hover{color:#ff0036!important}.productTitle a:visited{color:#551A8B!important}.product:hover .productTitle{height:14px}.productShop{position:relative;height:22px;line-height:20px;margin-bottom:5px;color:#999;white-space:nowrap;overflow:visible}.productStatus{position:relative;height:32px;border:none;border-top:1px solid #eee;margin-bottom:0;color:#999}.productStatus span{float:left;display:inline-block;border-right:1px solid #eee;width:39%;padding:10px 1px;margin-right:6px;line-height:12px;text-align:left;white-space:nowrap}.productStatus a,.productStatus em{margin-top:-8px;font-family:arial;font-size:12px;font-weight:700}.productStatus em{color:#b57c5b}.productStatus a{color:#38b}.productImg-wrap{position:relative}.product-iWrap{min-height:98%;width:210px}.view{padding-left:5px;padding-right:5px}.view{width:1023px}.view .product{width:220px;margin-right:33px}@media (min-width:1210px){.view{width:1210px;padding-left:5px;padding-right:5px}.view .product{width:220px;margin-right:20px}}@media (min-width:600px) and (max-width:800px) and (orientation:portrait){.view{width:775px;padding-left:5px;padding-right:5px}.view .product{width:220px;margin-right:35px}}.product{height:372px}.grid-nosku .product{height:333px}
</style>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)