Source: gongzhong 呺
Author: Hello Mr. Ward
Search is a very common function, we must have used, such as: Baidu search, Google search, e-commerce products search, Meituan merchants/food search and so on. With the explosive growth of Internet information, netizens need more effective personalized search services. Therefore, there is almost no Internet application that does not have a search function, and since this function is so important, as a qualified programmer must understand the implementation principle behind it. Arrangement!
This article will implement a simple version of e-commerce search system through Spring Boot + Elasticsearch + Vue, convenient for everyone to understand the principle behind.
Case analysis
According to the figure above, the business logic of search is actually quite simple. After the user enters the keyword of the product to be searched, the search keyword and page number information will be sent to the background, and the result set will be processed and returned after the background query. The following points need to be noted:
The query speed should be fast and the data matching degree should be high to facilitate sorting return result highlightingCopy the code
MySQL
The core table (goods) is as follows:
MySQL commodity table fields are very many, and the mall search page needs data is nothing more than commodity ID, commodity name, commodity price, number of commodity comments and commodity original map, etc. (according to their actual development situation, here according to the case front style analysis).
General commodity table will add commodity keyword field specially convenient search engine use, so a single table SQL query I believe should not be difficult to you.
Putting aside database performance issues, let’s look at what needs to be done simply to implement this functionality:
Suppose the search condition is Huawei mobile phone and tablet computer, the requirement is as long as any combination of words to meet the data to be queried, how to write SQL? In other words, how many pieces of SQL are you going to write?
Suppose you did the last step, you got so many result sets do you want to think about reordering?
You’ve done the first two steps, and now I’m asking you to highlight the combination of words in the search criteria when you return to the front. WTF, if you’re asked to use a relational database to do that, it’s a bit of a stretch. How to do? To look down.
Elasticsearch
With the explosive growth of Internet information, traditional search methods have been unable to provide effective search services for Internet users. If we are engaged in Intranet projects with few users, and the search fields are all short fields, such as name, number and so on, we can completely use database like statement. However, the query performance of database like is very low. If there are too many search requests, Or if you need to search for large-text content (full-text search), this search scheme is also not desirable.
The rapid development of Internet urgently needs a fast, comprehensive, accurate and stable information query method. Since we want to do high performance full text search, this requirement can not rely on the database, we have to implement by ourselves. We wanted it not only to be full-text search, but also to be stable and fast enough, with keyword highlighting, sorted by various matching scores, and to be able to switch between different word segmentation algorithms to meet various word segmentation requirements.
To sum up, if we want to make a full-text search function, powerful performance is not easy, and full-text search is a common requirement, in this environment, there are some open source solutions on the market. When these solutions are open source, they get a lot of support from the community of developers, who constantly develop plug-ins for them, so that they are constantly optimized and improved, and this becomes what we call a search engine. The most famous of them are Elasticsearch and Solr.
Elasticsearch is a search server based on Apache Lucene. It provides a distributed multi – user – capable full – text search engine based on RESTful Web interface. Elasticsearch, developed in Java and released as open source under the Apache license, is a popular enterprise-level search engine. Designed for cloud computing, can achieve real-time search, stable, reliable, fast, easy to install and use. Elasticsearch is the most popular enterprise search engine, followed by Apache Solr, also based on Lucene.
-
Elasticsearch is easy to install and configure, cheap to learn and use, and out of the box.
Elasticsearch supports both standalone and distributed systems with built-in distributed coordination and management functions. Elasticsearch provides the sharding and copy mechanism. An index can be divided into multiple shards. Elasticsearch focuses on core features. Advanced features are provided by third-party add-ons such as Kibana graphical interface. Elasticsearch provides quick indexing and real-time query, and is suitable for emerging real-time search applications. Elasticseach has great text analysis (word segmentation) and inverted indexing to make search faster.Copy the code
participles
In the question we mentioned just now, assuming that the search condition is Huawei mobile phone and tablet computer, the requirement is to query the data as long as any combination of terms is satisfied. The text analysis feature of Elasticseach makes it easy to parse search terms and search quickly with inverted indexes. Elasticseach provides three word segmentation methods: single word segmentation, dichotomous word segmentation, and word library word segmentation.
Word segmentation, such as: “huawei mobile phone tablet” effect: “China”, “for” and “hand”, “machine”, “flat”, “plate”, “electricity” and “brain”
Dichotomy: To divide two words into two parts. Such as: “huawei mobile phone tablet” effect: “huawei”, “hand”, “mobile phone”, “machine”, “tablet” and “electricity”, “computer”.
Thesaurus segmentation: according to a certain algorithm to construct words, and then to match the built thesaurus set, if matched, segmented into words. In general, thesaurus segmentation is regarded as the most ideal algorithm for Chinese word segmentation. The most commonly used word participle is the IK participle.
IK word divider provides two word segmentation modes: IK_max_word For example, “National anthem of the People’s Republic of China” will be split into “People’s Republic of China, People’s Republic of China, People’s Republic of China, People’s Republic of China, People’s Republic of China, People’s Republic of China, republic of China, and Guo Guo, national anthem”, which will exhaust all possible combinations and is suitable for Term Query.
Ik_smart: the text will be split in the coarsest granularity, for example, “The national anthem of the People’s Republic of China” will be split into “the National anthem of the People’s Republic of China”, which is suitable for Phrase Query.
Inverted index
For search engines: the forward index is the document ID to the document content, word association. That is, get the contents of the document by ID.
An inverted index is an association of words to document ids. That is, search for the document ID by word.
The query process of inverted index is as follows: first, search the corresponding document ID according to the keyword, then search the complete content of the document ID according to the forward index, and finally return the result that the user wants.
Part of the
An inverted index is the core of a search engine and consists of two parts: Trem Dictionary: It records the results of all the word segmentation of documents. Generally, B+Tree is used to ensure high efficiency.
Posting List: A collection of documents for Posting words, consisting of Posting index entries.
Posting is used to retrieve information about the original document.
Word Frequency (TF, Term Frequency) records the Frequency of occurrence of the word in the document for subsequent correlation score.
Position: records the word segmentation Position (multiple) in the document for word search.
Offset, which records the beginning and end positions of words in the document for highlighting.
case
Inverted index reference “This is the Search engine: A Detailed explanation of core technology” by Zhang Junlin. Suppose the document collection contains five documents, each with the contents as shown in the figure below. In the far left column of the figure is the corresponding document number for each document. Our task is to create an inverted index of this collection of documents.
First, a word segmentation system is used to automatically cut the document into word sequences. In order to facilitate the subsequent processing of the system, it is necessary to assign a unique word number to each different word and record which documents contain the word. After such processing, the simplest inverted index can be obtained, as shown in the figure below.
In the figure, the “Word ID” column records the word number of each word, the second column is the corresponding word, and the third column is the inverted list of each word. The word Google, for example, has a word number of 1 and an inverted list of 1,2,3,4,5, indicating that every document in the document collection contains the word.
For your convenience, the figure above is a simple inverted index of which documents contain which words. In fact, indexing systems can record much more than that.
Under the plan is a relatively complex inverted index, not only in words corresponding inverted list record the document number, also recorded the word frequency (TF) information, namely the number of occurrences of the word in a document, is to record the information, because the word frequency information in the search results sorted, calculate the query and the document similarity calculation factor is an important one, Therefore, it is recorded in the inverted list to facilitate the subsequent sorting of the score calculation.
The word number of the word founder in the figure is 7, and the corresponding inverted list content is (3; 1), where 3 represents that the document numbered 3 contains this word, and the number 1 represents word frequency information, that is, this word only appears once in the document no. 3.
Practical inverted index can also record more information. As shown in the figure below, the index system records two additional types of information besides document number and word frequency information, that is, the “document frequency information” corresponding to each word and the position information (POS) of the word in a certain document in the inverted list.
With this indexing system, the search engine can easily respond to the user’s query, such as the user input the query word “Facebook”, the search system looks up the inverted index, from which the documents containing the word, these documents are provided to the user’s search results. Word frequency information and document frequency information can be used to sort these candidate search results, calculate the similarity of documents and queries, and sort the output according to the similarity score from high to low.
So with that said, you know, without further ado, let’s get to the real thing. The image below is from: db-engines.com/en/ranking
The preparatory work
The environment
Elasticsearch: 7.9.3 Spring Boot: 2.4.3 JDK: 11.0.7 front-end: copy JINGdong store template + Vue, article with detailed code IDE: IntelliJ IDEA
Elasticsearch
This document uses the Elasticsearch cluster environment and has the IK Chinese word segmentation installed. The following figure shows the cluster information displayed by the ElasticSearch-head plug-in.
Spring Boot
Create a project
Use Spring Initializr to initialize the Spring Boot project, add Spring Web, Spring Data Elasticsearch, MySQL, MyBatis, Lombok.
The configuration file
Application. yml Configures information about MySQL, Elasticsearch, and MyBatis. Spring: # datasource: driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/example? serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: 123456
# elasticsearch
elasticsearch:
rest:
uris: 192.16810.10.:9200.192.16810.11.:9200.192.16810.12.:9200
mybatis:
configuration:
map-underscore-to-camel-case: true# Enable hump mappingCopy the code
Start the class
Startup class adds Mapper interface scanning.
package com.example;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MapperScan("com.example.mapper") // Mapper interface scanning
@SpringBootApplication
public class SearchDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SearchDemoApplication.class, args); }}Copy the code
The front end
Add the front-end resource files I prepared for you to the static directory in the resources directory of the project. Front-end resource file acquisition method: please pay attention to the public 呺 “code without bugs” reply to search.
Add Vue and Axios using CDN in list.html without downloading the file.
Start the
Visit: http://localhost:8080/list.html effect is as follows:
The function development
Create indexes
specifications
This function is used to import MySQL commodity information into Elasticseach.
If you set up an ELK system, you can use Logstash to import MySQL commodity information into Elasticseach. I will update this later, so stay tuned, we won’t talk about it today.
Since there is no Logstash we will import MySQL data into Elasticseach using code.
MySQL commodity table fields are very many, and the mall search page needs data is nothing more than commodity ID, commodity name, commodity price, number of commodity comments and commodity original map, etc. (according to their actual development situation, here according to the case front style analysis). General commodity table will add commodity keyword field specially convenient search engine use, so a single table SQL query I believe should not be difficult to you.
Entity class
Create the entity class goods.java and add the @document annotation to map the Elasticsearch library. Shards: Fragment number replicas: number of replicas createIndex: Whether to create an index database. @ID: primary key. @field: Set an index rule. Text, default analyzer ="ik_max_word"Ik_max_word will split the text at the finest granularity. Type = fieldType. Double: Double and indivisible as a whole type = fieldtype. Short: Type = fieldtype. Keyword: text, and as a whole is indivisible package com.example.pojo;import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
@NoArgsConstructor
** ** @document Specifies the name of the Elasticsearch index. * shards: number of fragments * replicas: number of copies * createIndex: whether to create an index */
@Document(indexName = "goods", shards = 5, replicas = 1, createIndex = true)
public class Goods implements Serializable {
private static final long serialVersionUID = -1989082640160920658L;
@Id
private Integer goodsId; ID / / commodities
@Field(type = FieldType.Text, analyzer = "ik_max_word") / / word segmentation
private String goodsName; // Product name
@Field(type = FieldType.Text, analyzer = "ik_max_word") / / word segmentation
private String keywords; // Product keyword
@Field(type = FieldType.Double)
private BigDecimal marketPrice; / / the market price
@Field(type = FieldType.Short)
private Short commentCount; // Number of product reviews
@Field(type = FieldType.Keyword)
private String originalImg; // The original map address of the commodity
private String goodsRemark; // A brief description of the product
private String goodsContent; // Commodity description, storage commodity detail map address
private Integer catId; / / category ID
private Integer extendCatId; // Extend the class ID
private String goodsNo; // Product id
private Integer clickCount; / / clicks
private Short brandId; / / brand ID
private Short storeCount; // Inventory quantity
private Integer weight; // Product weight (unit: grams
private BigDecimal shopPrice; / / this shop price
private BigDecimal costPrice; // The cost of goods
private Byte isReal; // Whether it is a real object
private Byte isOnSale; // Whether it is available
private Byte isFreeShipping; 0 No, 1 Yes
private Integer onTime; // Product shelf time
private Short sort; // Sort items
private Byte isRecommend; // Whether recommended
private Byte isNew; // Is it new
private Byte isHot; // Is it hot
private Integer lastUpdate; // Last updated
private Short goodsType; // The type ID of the item
private Short specType; // Product specification type
private Integer giveIntegral; // Free points for purchases
private Integer exchangeIntegral; // Points exchange: 0 does not participate in points exchange
private Short suppliersId; // Supplier ID
private Integer salesSum; // Product sales
private Byte promType; // 0 ordinary order, 1 flash sale, 2 group purchase, 3 promotional offers
private Integer promId; // ID of a special event
private BigDecimal commission; // Commission for distribution
private String spu; // SPU
private String sku; // SKU
}
Copy the code
GoodsMapper.java
MySQL > select * from product where ID, name, keyword, market price, number of product reviews, address of original product map; These fields are used by search engines.
package com.example.mapper;
import com.example.pojo.Goods;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/** * @author Mr. Harry Ward * @wechat public account Mr. Harry Ward * @website https://mrhelloworld.com * @wechat 124059770 */
public interface GoodsMapper {
/** ** Query commodity ID, commodity name, commodity keyword, market price, commodity comment number, commodity original map address ** @return commodity list */
@Select("select goods_id, goods_name, keywords, market_price, comment_count, original_img from goods")
List<Goods> selectGoodsList(a); } goodsRepository.java application.yml configuration file adds a custom configuration that controls the creation of indexes and the setting of mappings.Copy the code
Custom Configuration
search:
index:
enabled: true# whether to create index and set mapping. The default isfalseInject ElasticsearchRestTemplate GoodsRepository. Java, writing create indexes and setting up the mapping code. package com.example.repository;import com.example.pojo.Goods;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
/** * @author Mr. Harry Ward * @wechat public account Mr. Harry Ward * @website https://mrhelloworld.com * @wechat 124059770 */
@Repository
public class GoodsRepository {
@Resource
private ElasticsearchRestTemplate elasticsearchRestTemplate;
// Whether to create an index and set the mapping. The default is false
@Value("${search.index.enabled}")
private boolean indexEnabled;
/** * Add commodity info to Elasticsearch ** @param goodsList */
public void save(List<Goods> goodsList) {
// Whether to create an index and set the mapping
if (indexEnabled) {
createIndexAndPutMapping(a); }// Batch add
elasticsearchRestTemplate.save(goodsList);
}
/** * Added single item info to Elasticsearch ** @param goods */
public void save(Goods goods) {
save(Arrays.asList(goods));
}
/** * Create index and set mapping */
private void createIndexAndPutMapping(a) {
// Set index information (entity class)
IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(Goods.class);
// Create index
indexOperations.create(a);// Create the mapping
Document mapping = indexOperations.createMapping(a);// Write the map to the index
indexOperations.putMapping(mapping); }} SearchService.java searchService.java uses GoodsMapper and GoodsRepository to import MySQL commodity information into Elasticsearch. package com.example.service;import com.example.mapper.GoodsMapper;
import com.example.repository.GoodsRepository;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/** * @author Mr. Harry Ward * @wechat public account Mr. Harry Ward * @website https://mrhelloworld.com * @wechat 124059770 */
@Service
public class SearchService {
@Resource
private GoodsMapper goodsMapper;
@Resource
private GoodsRepository goodsRepository;
/** * import MySQL commodity info into Elasticsearch */
public void importGoods(a) {
goodsRepository.save(goodsMapper.selectGoodsList()); }}Copy the code
Unit testing
For the current case, there is no need to expose an interface to call this function, just write unit tests in-house.
package com.example.service;
import com.example.SearchDemoApplication;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
@SpringBootTest(classes = SearchDemoApplication.class)
public class SearchServiceTest {
@Resource
private SearchService searchService;
@Test
public void testImportGoods(a) {
searchService.importGoods();
}
}
Copy the code
After executing the unit test, refresh the ElasticSearch-head plugin page and the result is as follows:
Take a look at the mapping information for the Goods index library.
MySQL > alter table goods;
Elasticsearch goods index library:
If the preceding command output is displayed, all MySQL commodity information has been imported to Elasticsearch.
search
Next comes today’s topic, the implementation of the search function. To do a complete set, we use imitation JINGdong mall template + Vue to achieve a real e-commerce search function.
The search logic is pretty simple. After the user enters the keyword of the product to be searched, the search keyword and page number information will be sent to the background, and then the Spring Data Elasticseach can be easily done.
GoodsRepository.java
According to international conventions, the DAO layer is only responsible for data processing, one line of code./** ** paged, highlighted query ** @param query * @return */
public SearchHits<Goods> selectGoodsListForPage(NativeSearchQuery query) {
return elasticsearchRestTemplate.search(query, Goods.class);
}
Copy the code
PageInfo.java
This function is certainly needed with paging processing, encapsulation of a paging object for easy use.
package com.example.result;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/** * @author Mr. Harry Ward * @wechat public account Mr. Harry Ward * @website https://mrhelloworld.com * @wechat 124059770 */
@Data
@NoArgsConstructor
public class PageInfo<T> implements Serializable {
private static final long serialVersionUID = 6260163970867016563L;
private int currentPage; / / the current page
private int pageSize; // Display the number of entries per page
private int total; // Total number of records
private int totalPage; / / the total number of pages
private int prePage; / / back
private int nextPage; / / the next page
private boolean hasPre; // Is there a previous page
private boolean hasNext; // Is there a next page
private List<T> result; // Return the result set
public PageInfo(int currentPage, int pageSize) {
/ / the current page
this.currentPage = currentPage < 1 ? 1 : currentPage;
// Display the number of entries per page
this.pageSize = pageSize;
// Is there a previous page
this.hasPre = currentPage == 1 ? false : true;
// Is there a next page
this.hasNext = currentPage == totalPage ? false : true;
/ / back
if (hasPre) {
this.prePage = currentPage - 1;
}
/ / the next page
if (hasNext) {
this.nextPage = currentPage + 1; }}public PageInfo(int currentPage, int pageSize, int total) {
/ / the current page
this.currentPage = currentPage < 1 ? 1 : currentPage;
// Display the number of entries per page
this.pageSize = pageSize;
// Total number of records
this.total = total;
// Count the total pages
if (total == 0) {
this.totalPage = 0;
} else {
this.totalPage = (total - 1) / pageSize + 1;
}
// Is there a previous page
this.hasPre = currentPage == 1 ? false : true;
// Is there a next page
this.hasNext = currentPage == totalPage ? false : true;
/ / back
if (hasPre) {
this.prePage = currentPage - 1;
}
/ / the next page
if (hasNext) {
this.nextPage = currentPage + 1; }}}Copy the code
SearchService.java
The Service business logic layer focuses on the following details:
<span style= "box-sizing: border-box! Important'color:red; '></ SPAN > Build search criteria object Set search criteria goodsName and keywords Settings Highlight set paging call DAO to complete the search processing search result set build paging object and return/** * @param searchStr Search criteria * @param pageNum Number of pages * @param pageSize Number of pages to display per page * @return */
public PageInfo<GoodsVo> doSearch(String searchStr, Integer pageNum, Integer pageSize) {
HighlightBuilder.Field field = new HighlightBuilder.Field("goodsName");
field.preTags("");
field.postTags("</span>");
// Build the search criteria object
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(a);// Set the search criteria goodsName and keywords
boolQueryBuilder.must(QueryBuilders.multiMatchQuery(searchStr, "goodsName"."keywords"));
// Build the search object
NativeSearchQuery query = new NativeSearchQueryBuilder().withQuery(boolQueryBuilder) // Search criteria
.withHighlightFields(field) / / highlight
.withPageable(PageRequest.of(pageNum - 1, pageSize)) // paging, the current page starts at 0
.build(a);/ / search
SearchHits<Goods> searchHits = goodsRepository.selectGoodsListForPage(query);
/ / the total number of article
Long total = searchHits.getTotalHits(a);if (0 > total) {
return new PageInfo<>(pageNum, pageSize, 0);
}
// Initialize the return result set
List<GoodsVo> goodsVoList = new ArrayList<>();
// Process the search result set
for (SearchHit<Goods> searchHit : searchHits) {
// Initialize the return result object
GoodsVo goodsVo = new GoodsVo(a);// Get the result object
Goods goods = searchHit.getContent(a);// Copy attributes
BeanUtils.copyProperties(goods, goodsVo);
// Process the highlighting information
Map<String, List<String>> highlightFields = searchHit.getHighlightFields(a);// Whether there is highlighting information
if (highlightFields.containsKey("goodsName")) {
String goodsNameHl = highlightFields.get("goodsName").get(0);
goodsVo.setGoodsNameHl(goodsNameHl);
} else {
goodsVo.setGoodsNameHl(goods.getGoodsName());
}
goodsVoList.add(goodsVo);
}
// Initialize the split-page object
PageInfo<GoodsVo> pageInfo = new PageInfo<GoodsVo>(pageNum, pageSize, total.intValue());
pageInfo.setResult(goodsVoList);
return pageInfo;
}
Copy the code
SearchController.java
The control layer provides a /search GET interface.
package com.example.controller;
import com.example.result.PageInfo;
import com.example.service.SearchService;
import com.example.vo.GoodsVo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/** * @author Mr. Harry Ward * @wechat public account Mr. Harry Ward * @website https://mrhelloworld.com * @wechat 124059770 */
@RestController
@RequestMapping("search")
public class SearchController {
@Resource
private SearchService searchService;
/** * @param searchStr Search criteria * @param pageNum Number of pages * @param pageSize Number of pages to display per page * @return */
@GetMapping
public PageInfo<GoodsVo> doSearch(String searchStr, Integer pageNum, Integer pageSize) {
return searchService.doSearch(searchStr, pageNum, pageSize); }}Copy the code
list.html
Add Vue and Axios using CDN in list.html without downloading the file.
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js"></script>
Copy the code
The page processing
Modify the form section of the header search (good with Ctrl + F) :
Add ID = to <input/> of the commodity keyword"searchStr"Add two <input/> of type hidden to hold page number information <form action="" name="search" method="get" class="fl" onsubmit="return false;">
<input id="searchStr" type="text" class="txt" value="Please enter the product keyword"/>
<input type="submit" class="btn" value="Search"/>
<input id="pageNum" type="hidden" value="1"/>
<input id="pageSize" type="hidden" value="12"/> </form> Modifies the list of items (good with Ctrl + F), leaving only a <li></li> : <! Start --> <div id="goodsList" class="goodslist mt10">
<ul>
<li>
<dl>
<dt><a href=""><img src="images/goods1.jpg" alt=""/></a></dt>
<dd><a href=""><p class="beyondHidden"Word-wrap: break-word! Important; "> < p style =" max-width: 100%2G 500G DVD keyboard & Mouse) tape20Inch monitor </p></ A ></dd> <dd><strong>¥2399.00</strong></dd>
<dd><a href=""> < em > already10</em></a></dd> </dl> </li> </ul> </div> <! End --> Change page information (good with Ctrl + F) : <! Start --> <div id="page" class="page mt20">
<a href=""> </a> <a href=""< span style = "max-width: 100%; clear: both; min-height: 1em"" class="cur">3</a>
<a href=""> next page </a> <a href=""> back < / a > & have spent < span > < em > altogether8Page & have spent To the < input type ="text" id="num" class="page_num"/> page </em> <a href="" class="skipsearch"</a> </span> </div> <! End -->Copy the code
Vue and Axios
First add a div globally and set id=”app”.
You then initialize the Vue object, bind the elements, and define the component data and component methods. <script> var app =new Vue({
// Short for element, mount the element, bind the HTML snippet with the id app
el: '#app'.// Define component data
data: {
goodsList: [],
page: []
},
// Define component methods
methods: {
/ / search
doSearch() {
axios({
url: "http://localhost:8080/search",
method: "GET",
params: {
searchStr: $("#searchStr").val(),
pageNum: $("#pageNum").val(),
pageSize: $("#pageSize").val()}}).then(response => { // Return the result
$("#pageNum").val(1);// Reset the current page
if (response.data.total <= 0) {$('#goodsList').append(' Sorry, not found with ' + $("#searchStr").val() + '" for related products, please make sure your search terms are correct. ');
}
this.goodsList = response.data.result;
this.page = response.data;
}).catch(error => {// Exception catch
alert('System is being upgraded, please try again later! ');
});
},
/ / back
prePage() {
// Get the value of the current page and subtract one, then reassign to the current page
let page = parseInt($("#pageNum").val- ())1;
$("#pageNum").val(page);
// Call the search function
this.doSearch(a); },/ / the next page
nextPage() {
// Gets the value of the current page and increments it, then reassigns it to the current page
let page = parseInt($("#pageNum").val()) + 1;
$("#pageNum").val(page);
// Call the search function
this.doSearch(a); },/ / what page
whichPage(num) {
// Get the button clicked (home page, 1, 2... Last page), and then reassign to the current page
$("#pageNum").val(num);
// Call the search function
this.doSearch(a); },// To what page
goToPage() {
// Get the entered page number value and reassign it to the current page
$("#pageNum").val($("#num").val());
// Call the search function
this.doSearch(a); }}}); </script>Copy the code
The binding element
Modify the form section of the header search (good with Ctrl + F) :
Add v - on: click ="doSearch"(@click="doSearch") to the search button <form action="" name="search" method="get" class="fl" onsubmit="return false;">
<input id="searchStr" type="text" class="txt" value="Please enter the product keyword"/>
<input v-on:click="doSearch" type="submit" class="btn" value="Search"/>
<input id="pageNum" type="hidden" value="1"/>
<input id="pageSize" type="hidden" value="12"/>
</form>
Copy the code
Modify the list of items (good with Ctrl + F) :
List rendering: <li></li> add V -for="(goods, index) in goodsList"<img/> Add v-bind: SRC ="goods.originalImg"(Short: SRC ="goods.originalImg") commodity name and highlight: <p></p> Add V-html ="goods.goodsNameHl"Merchandise Price: Add {{goods.marketPrice}} Merchandise comment: Add {{goods.commentCount}} <! Start --> <div id="goodsList" class="goodslist mt10">
<ul>
<li v-for="(goods, index) in goodsList">
<dl>
<dt><a href=""><img v-bind:src="goods.originalImg" alt=""/></a></dt>
<dd><a href=""><p class="beyondHidden" v-html="goods.goodsNameHl"> < / p > < / a > < / dd > < dd > < strong > ${{goods. MarketPrice}} < / strong > < / dd > < dd > < a href =""> < em > {{goods.com mentCount}} people evaluation < / em > < / a > < / dd > < / dl > < / li > < / ul > < / div > <! End -->Copy the code
Modify page information (good with Ctrl + F) :
Home page: v - on: click ="whichPage(1)"Back: v - on: click ="whichPage(page.totalPage)"Previous page: V -if="page.hasPre" v-on:click="prePage"Next page: V -if="page.hasNext" v-on:click="nextPage"A page: Loop render: V -for="i in page.totalPage"Page: V-on :click="whichPage(i)"Current page style handling: V-bind :class="{ cur:i == page.currentPage }"{{page. TotalPage}} to page number: V-on :click="goToPage"<! Start --> <div id="page" class="page mt20">
<a v-on:click="whichPage(1)" href="javascript:void(0);"< p style = "max-width: 100%; clear: both; min-height: 1emif="page.hasPre" v-on:click="prePage" href="javascript:void(0);"< p style = "max-width: 100%; clear: both; min-height: 1emfor="i in page.totalPage"
v-on:click="whichPage(i)"
v-bind:class="{ cur:i == page.currentPage }"
href="javascript:void(0);">{{ i }}</a>
<a v-if="page.hasNext" v-on:click="nextPage" href="javascript:void(0);"> <a > <a v-on:click="whichPage(page.totalPage)" href="javascript:void(0);"> back < / a > & have spent <span> <em> total {{page. TotalPage}} page & NBSP; To the < input type ="text" id="num" class="page_num"/> page </em> <a v-on:click="goToPage" href="javascript:void(0);" class="skipsearch"</a> </span> </div> <! End -->Copy the code
test
Access:http://localhost:8080/list.htmlJust type whatever you want and test it out.