搜索引擎(search engine )通常意义上是指:根据特定策略,运用特定的爬虫程序从互联网上搜集信息,然后对信息进行处理后,为用户提供检索服务,将检索到的相关信息展示给用户的系统。
而我们讲解的是捜索的索引和检索,不涉及爬虫程序的内容爬取。大部分公司的业务也不会有爬取工作,而只提供查询服务,而且Elasticsearch也只是提供这方面的功能。
Elasticsearch是一个分布式、RESTful风格的搜索和数据分析引撃。通过它,能够执行及合并多种类型的搜索(结构化数据、非结构化数据、地理位置、指标),解决不新涌现出的各种需求。
Elasticsearch使用的是标准的RESTful风格的API,使用JSON提供多种语言(Java、 Python、.Net、SQL和PHP)的支持,它可以快速地存储、搜索和分析海量数据。
Elasticsearch是用Java语言开发的,并使用Lucene作为其核心来实现所有索引和搜索的功能。它的目的是:通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。
Elasticsearch是一个开源的高扩展的分布式全文检索引擎,可以近乎实时地存储、检索数据; 本身扩展性很好,允许多台服务器协同工作,每台服务器可以运行多个实例。单个实例称为一个节点(node), 一组节点构成一个集群(cluster)。分片是底层的工作单元,文档保存在分片内,分片又被分配到集群内的各个节点里,每个分片仅保存全部数据的一部分。
当Elasticsearch的节点启动后,它会使用多播(multicast)或单播(用户更改了配置)寻找集群中的其他节点,并与之建立连接。
除这些公司外,Stack Overflow、新浪、阿里、360、携程、有赞、苏宁都在使用它。它被广泛地用于各大公司的站内搜索、IT系统搜索(OA、CRM、ERP)、数据分析等工作中。
尽管将Elasticsearch与MySQL进行对比并不科学,但是这样的对比能区分Elasticsearch 和MySQL数据库的区别,便于快速用熟悉的知识来理解Elasticsearch 。所以,本节采用对比的方式来讲解Elasticsearch。Elasticsearch与MySQL的结构对比见表13-1。

图 13-1
客户端主要通过"方法(PUT/POST/GET/ DELETE ) + http://ip:端口/索引名称/类型/主键” 来访问内容。
Spring-data-elasticsearch 是 Spring 提供的操作 Elasticsearch 的数据接口,它封装了大量的基础操作。通过它可以很方便地操作Elasticsearch的数据。
通过继承ElasticsearchRepository来完成基本的CRUD及分页操作,和普通的 JPA没有什 么区别。比如下面实体Product的Repository继承ElasticsearchRepository后,可以在 Elasticsearch文档中进行查找和比较等操作。具体使用方法见以下代码:
@Component
public interface UserRepository extends ElasticsearchRepository<User,Long> {
Optional<User> findById(Long id);
User findByUsername(String username);
List<User> findByEmail(String email);
}
ElasticsearchRepository有几个特有的search方法,用来构建一些Elasticsearch查询, 主要由QueryBuilder和SearchQuery两个参数来完成一些特殊查询。
实现类NativeSearchQuery实现了 QueryBuilder和SearchQuery方法,要构建复杂查询, 可以通过构建NativeSearchQuery类来实现。
—般情况下,不是直接新建NativeSearchQuery类,而是使用NativeSearchQueryBuilder 来完成NativeSearchQuery的构建。具体用法见以下代码:
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
builder.withQuery()
.withFilter()
.withSort()
.withXXX().build();
ElasticsearchTemplate是Spring对Elasticsearch的API进行的封装,主要用来对索引进行创建、删除等操作。它继承了 ElasticsearchOperations 和 ApplicationContextAware 接口。 ElasticSearchTemplate 提供一些比 ElasticsearchRepository 更底层的方法。
ElasticsearchOperations接口中常用的方法如下。
注解@Document作用于类,用于标记实体类为文档对象。
存储在Elasticsearch中的一条数据,即是一个文档,类似关系型数据库的一行数据。 Elasticsearch会索引每个文档的内容,以便搜索。它使用JSON格式,将数据存储到Elasticsearch 中,实际上是将JSON格式的字符串发送给了 Elasticsearch。
document有三个核心元数据,分别是 _index、_type、_id
(1)_index。代表一个document存放在哪个index中,类似的数据放在一个索引中,非类似的数据放在不同的索引中。index中包含了很多类似的document,这些document的field很大一 部分是相同的。索引名称必须小写,不能用下画线开头,不包含逗号。
(2)_type。代表document属于index的哪个类别,一个索引通常会划分为多个type,逻辑 index不同的数据进行分类。type名称可以是大写或小写,但是不能用下画线开头,不能包含逗号。
(3)_id。代表document的唯一标识,与_index和_type —起可以标识和定位一个 document。默认自动创建id,也可以手动指定document的id。
如果需要从某些其他系统中导入一些数据到Elasticsearch,则会采用手动指定id的形式,因 为一般情况下系统中已有数据的唯一标识,可以用作Elasticsearch中的document的id。
其语法格式为:
put /index/type/id
{
"json"
}
其语法格式为:
post /index/type
{
"json"
}
自动生成的id长度为20个字符,URL安全、Base64编码、GUID、分布式系统并行生成时不会发生冲突。
_source元数据是在创建document时放在body中的JSON数据。在默认情况下,查找数据时会返回全部数据。如果要定制返回结果,则可以指定_source中返回哪些field
例如:
GET /_index/_type/1?_source=field
下面代码是根据实体类创建一个名为 "ec" 的索引,并定义type是“product”。由于是单机环境,所以定义副本为0,分片为默认值5。
@Data
@Document(indexName = "user",type = "user",replicas = 0,shards = 5)
public class User implements Serializable {
private int id;
private String username;
private String password;
}
代码解释如下。
可以使用createindex方法手动指定indexName和Settings,再进行映射。在使用前,要先注入ElasticsearchTemplate,使用方法如下。
elasticsearchTemplate.createIndex("indexname");
elasticsearchTemplate.createIndex(User.class);
elasticsearchTemplate.indexExists("indexname");
elasticsearchTemplate.indexExists(User.class);
可以根据索引名和类名对索引进行删除。
elasticsearchTemplate.deleteIndex("indexname");
elasticsearchTemplate.deleteIndex(User.class);
ELK 是 Elasticsearch+Logstash+Kibana 的简称。
Logstash负责将数据信息从输入端传输到输出端,比如将信息从MySQL传入Elasticsearch, 还可以根据自己的需求在中间加上滤网。Logstash提供了很多功能强大的滤网,以满足各种应用场景。
Logstash有以下两种工作方式。
Kibana是一个开源的分析与可视化平台,和Elasticsearch —起使用。可以用Kibana搜索、 查看、交互存放在Elasticsearch索引里的数据。使用各种不同的图标、表格、地图等,Kibana能够很轻昜地展示高级数据分析与可视化。
ELK架构为数据分布式存储、日志解析和可视化创建了一个功能强大的管理链。三者相互配合, 取长补短,共同完成分布式大数据处理工作。
(1)通过官网下载Elasticsearch。
(2)在下载完成后,首先将其解压到合适的目录,然后进入解压目录下的bin目录,双击 bat文件启动Elasticsearch。这里需要确保安装的Java版本在1.8及以上。
(3)访问“http://localhost:9200/”,当看到返回一串JSON格式的代码时,则说明已经安装成功了。
根据应用需要,还可以安装Elasticsearch必要的一些插件,如Head、kibana、IK (中文分 词)、graph。
(1)访问 Elasticsearch 官网下载 Logstash
(2)将下载文件解压到自定义的目录即可。
(1 )在解压文件的config目录下新建log4j_to_es.conf文件,写入以下代码:
input {
beats {
port => 5044
codec => "json"
}
}
output {
elasticsearch {
hosts => "127.0.0.1:9200"
codec => json
}
}
这里一定要注意:这是UTF-8的格式,不要帯BOM。如果启动时岀现错误,则可以用“logstash -f ../config/xxx.conf -t“命令检查配置文件是否错误。
(2)新建文件 run.bat。写入代码 logstash -f .\config\log4j_to_es.conf保存。然后双击该配置文件,启动Logstash。
(3)访问 localhost:9600 如出现以下內容,则代表配置成功。
{"host":"DESKTOP-0VSQ1JE","version":"8.3.3","http_address":"127.0.0.1:9600",
"id":"f9a14845-005b-42f2-8725-9b3f67dafe86","name":"DESKTOP-0VSQ1JE",
"ephemeral_id":"9ffa0b64-c9bb-4385-8924-6565b4cd8167","status":"green",
"snapshot":false,"pipeline":{"workers":8,"batch_size":125,"batch_delay":50},
"build_date":"2022-07-23T19:31:54Z","build_sha":"0205f0c5f2ff21118c161e769e8f2bbb79ee81a3",
"build_snapshot":false}
Kibana是官方推出的Elasticsearch数据可视化工具。
(1)通过访问Elasticsearch官网下载Kibana。
(2)解压下载的压缩文件,进入解压目录,双击Kibana目录的bin/kibana.bat,以启动 Kibana,当岀现以下提示时,代表启动成功。
[2022-08-16T16:30:45.443+08:00][INFO ][status] Kibana is now available (was degraded)
(3)访问localhost:5601就可以逬入Kibana控制台。
单击控制台左边导航栏的“Dev-tools”按钮,可以进入Dev-tools界面。单击"Get to work", 然后在控制台输入“GET/_cat/health?”命令,可以查看服务器状态。如果在右侧返回的结果中看到green或yellow ,则表示服务器状态正常。
(1)添加项目依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
<version>1.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>7.2</version>
</dependency>
(2)添加配置文件logback.xml,这里在Spring Boot项目里添加一个配置又件,见以下代码:
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>localhost:9601</destination>
<encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder"/>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder charset="UTF-8">
<pattern>%d{HH:mm:ss.SSS}[%thread]%-5level %logger - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="LOGSTASH"/>
<appender-ref ref="STDOUT"/>
</root>
</configuration>
在Spring Boot项目中创建logTest类,用于测试将日志通过Logstash发送到Elasticsearch, 见以下代码:
实现增加、删除、修改、查询文档的功能
Spring Boot 提供了 Starter ( spring-boot-starter-data-elasticsearch )来集成 Elasticsearch
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
</dependency>
spring:
data:
elasticsearch:
repositories:
enabled: true #是否开启本地存储
cluster-nodes: 127.0.0.1:9200
cluster-name: elasticsearch
这里根据类的信息自动生成,也可以手动指定索引名称。ElasticsearchTemplate中提供了创建素引的API,因为进行本机测试,没做集群,所以replicas副本先设置为0。见以下代码:
package com.intehel.demo.domain;
import lombok.AllArgsConstructor;
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;
@Document(indexName = "ec",replicas = 0,shards = 5,type = "product")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Product implements Serializable {
//@Id注解必须是org.springframework.data.annotation.Id;
@Id
private Long id;
//ik_max_word使用ik分词器
@Field(type = FieldType.Text,analyzer = "ik_max_word")
private String name;
//在存储数据时,不会对category进行分词
@Field(type = FieldType.Keyword)
private String category;
//价格
@Field(type = FieldType.Double)
private Double price;
//index = false 表示不建立索引
@Field(index = false,type = FieldType.Keyword)
private String images;
private String body;
}
代码解释如下,
package com.intehel.demo.repository;
import com.intehel.demo.domain.Product;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import java.util.Optional;
public interface ProductRepository extends ElasticsearchRepository<Product,Long> {
@Override
Optional<Product> findById(Long id);
Product findByName(String name);
}
在测试类中,实Elasticsearch文档进行增加、删除、修改和查询的功能,见以下代码:
package com.intehel.demo;
import com.intehel.demo.domain.Product;
import com.intehel.demo.domain.User;
import com.intehel.demo.repository.ProductRepository;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Sort;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Optional;
@SpringBootTest
@RunWith(SpringRunner.class)
class ElasticApplicationTests {
private Integer PAGESIZE = 10;
@Autowired
private ProductRepository productRepository;
@Test
public void save(){
long id = System.currentTimeMillis();
Product product = new Product(id,"红富士","水果",7.99,"jpg","测试");
try {
productRepository.save(product);
}catch (Exception e){
e.printStackTrace();
}
System.out.println(product.getId());
}
@Test
public void getProduct(){
Product product = productRepository.findByName("红富士");
System.out.println(product.getId());
}
@Test
public void updateProduct(){
long id = 1660653233939L;
Product product = new Product(id,"烤冷面","小吃",7.00,"jpg","测试");
productRepository.save(product);
}
@Test
public void getProductById(){
Optional<Product> product = productRepository.findById(1660653233939L);
System.out.println(product.get().getName()+product.get().getBody());
}
@Test
public void deleteProduct(){
long id = 1660653233939L;
productRepository.deleteById(id);
}
@Test
public void getAll(){
Iterable<Product> list = productRepository.findAll(Sort.by("id").ascending());
for (Product product : list) {
System.out.println(product);
}
}
}
可以根据Spring Data提供的方法名称,实现自己想自定义的查询功能:无须写实现类,只要继承ElasticsearchRepository接口即可。如"findByTitle"表示根据"title”进行查询,具体方法见表13~2


图 13-2
如果要查询价格在7 ~ 8元的商品,则可以在接口类加上"List<Product> findByPriceBetween(Double min, Double max);"方法,见以下代码:
public interface ProductRepository extends ElasticsearchRepository<Product,Long> {
@Override
List<Product> findByPriceBetween(Double min, Double max);
}
然后,在测试类中直接使用自定义的"findByPriceBetween"方法查询出数据,见以下代码:
@Test
public void queryByPriceBetween(){
Iterable<Product> list = productRepository.findByPriceBetween(7.00,8.00);
for (Product product : list) {
System.out.println(product);
}
}
用法见以下代码:
QueueBuilder queueBuilder = QueueBuilder.termQuery("字段名","查询值");
它是不分词查询。因为不分词,所以汉字只能查询一个字,而多字母的英语单词算一个字
具体实现见以下代码:
@Test
public void queryByPriceBetween(){
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
nativeSearchQueryBuilder.withQuery(QueryBuilders.termQuery("name","富"));
//查询词,只能查询一个汉字,或一个英文单词
Page<Product> page = productRepository.search(nativeSearchQueryBuilder.build());
//搜索,获取结果
for (Product product : page){
System.out.println(product);
}
}
terms可以提供n个查询的参数对一个字段进行查询,用法见以下代码。注意,这里是term的复数形式terms
QueueBuilder queueBuilder = QueueBuilder.termsQuery("字段名","查询值","查询值");
@Test
public void queryByPriceBetween(){
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
nativeSearchQueryBuilder.withQuery(QueryBuilders.termsQuery("name","富","帅"));
//查询词,只能查询一个汉字,或一个英文单词
Page<Product> page = productRepository.search(nativeSearchQueryBuilder.build());
//搜索,获取结果
for (Product product : page){
System.out.println(product);
}
}
分词查询采用默认的分词器.用法见以下代码
QueueBuilder queueBuilder = QueueBuilder.matchQuery("字段名","查询值");
具体实现见以下代码:
@Test
public void matchQuery() throws Exception {}{
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
nativeSearchQueryBuilder.withQuery(QueryBuilders.matchQuery("name","红士"));
//查询词
Page<Product> page = productRepository.search(nativeSearchQueryBuilder.build());
//搜索,获取结果
for (Product product : page){
System.out.println(product);
}
}
@Test
public void matchQuery(){
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
nativeSearchQueryBuilder.withQuery(QueryBuilders.multiMatchQuery("红富士Gila","name","body"));
//查询词
Page<Product> page = productRepository.search(nativeSearchQueryBuilder.build());
//搜索,获取结果
for (Product product : page){
System.out.println(product);
}
}
常见的模糊查询的方法有4种
QueryBuilders.queryStringQuery("查询值").field("字段名")
具体实现见以下代码:
@Test
public void matchQuery(){
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
nativeSearchQueryBuilder.withQuery(QueryBuilders.queryStringQuery("我觉得红富士好吃").field("name"));
//查询词
Page<Product> page = productRepository.search(nativeSearchQueryBuilder.build());
//搜索,获取结果
for (Product product : page){
System.out.println(product);
}
}
如果字段没分词,则匹配整个字段前缀,用法见以下代码:
QueryBuilders.prefixQuery("字段名","查询值")
具体实现见以下代码:
@Test
public void matchQuery(){
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
nativeSearchQueryBuilder.withQuery(QueryBuilders.prefixQuery("name","士"));
//查询词
Page<Product> page = productRepository.search(nativeSearchQueryBuilder.build());
//搜索,获取结果
for (Product product : page){
System.out.println(product);
}
}
使用通配符方式进行查询,支持通配符”*“和”?“,”*“代表任意字符串,”? ”代表任意一个字符。
(1)使用通配符”*“
通配符可以匹配多个值,用法见以下代码:
@Test
public void matchQuery(){
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
nativeSearchQueryBuilder.withQuery(QueryBuilders.wildcardQuery("name","*士"));
//查询词
Page<Product> page = productRepository.search(nativeSearchQueryBuilder.build());
//搜索,获取结果
for (Product product : page){
System.out.println(product);
}
}
(2)使用通配符“?”
@Test
public void matchQuery(){
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
nativeSearchQueryBuilder.withQuery(QueryBuilders.wildcardQuery("name","红富?"));
//查询词
Page<Product> page = productRepository.search(nativeSearchQueryBuilder.build());
//搜索,获取结果
for (Product product : page){
System.out.println(product);
}
}
分词模糊查询即匹配截取字符串为字前或后加1个词的文档,这里通过增加fuzziness (模糊) 属性来查询,fuzziness的含义是检索的term前后增加或减少n个词的匹配查询。用法见以下代码
QueryBuilders.fuzzyQuery("字段名","查询值").fuzziness(Fuzziness.ONE)
具体实现见以下代码:
@Test
public void matchQuery(){
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
nativeSearchQueryBuilder.withQuery(QueryBuilders.fuzzyQuery("name","士").fuzziness(Fuzziness.ONE));
//查询词
Page<Product> page = productRepository.search(nativeSearchQueryBuilder.build());
//搜索,获取结果
for (Product product : page){
System.out.println(product);
}
}
相似内容的推荐是给定一篇文档信息,然后向用户推荐与该文档相似的文档。通过 Elasticsearch的More like this查询接口,可以非常方便地实现基于内容的推荐,用法见以下代码: QueryBuilders.moreLikeThisQuery(new StnngQ ("WfiS"}).addLikeText("@i®ffi");
如果不指定字段名,则默认全部,常用在相似内容的推荐上。
QueryBuilders.moreLikeThisQuery(new String[]{"字段名"}).addLikeText("查询值")
闭区间查询:QueryBuilders.rangeQuery("字段名").from("值1").to("值2")
开区间查询:QueryBuilders.rangeQuery("字段名").from("值1").to("值2").includeLower(false).includeUpper(false)
大于:QueryBuilders.rangeQuery("字段名").gt("查询值")
大于或等于:QueryBuilders.rangeQuery("字段名").gte("查询值")
小于:QueryBuilders.rangeQuery("字段名").lt("查询值")
小于或等于:QueryBuilders.rangeQuery("字段名").lte("查询值")
组合查询是可以设置多个条件的查询方式,用来组合多个查询。有4种方式。
使用NativeSearchQueryBuilder实现分页查询,用法见以下代码:
@Test
public void matchQuery(){
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
nativeSearchQueryBuilder.withQuery(QueryBuilders.termQuery("name","富"));
nativeSearchQueryBuilder.withPageable(PageRequest.of(0,5));
//查询词
Page<Product> page = productRepository.search(nativeSearchQueryBuilder.build());
//搜索,获取结果
for (Product product : page){
System.out.println(product);
}
}
如果要进行排序,只要在分页查询上构建withSort参数即可,用法见以下代码:
@Test
public void matchQuery(){
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
nativeSearchQueryBuilder.withQuery(QueryBuilders.termQuery("name","富"));
nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("id").order(SortOrder.DESC));
nativeSearchQueryBuilder.withPageable(PageRequest.of(0,5));
//查询词
Page<Product> page = productRepository.search(nativeSearchQueryBuilder.build());
//搜索,获取结果
for (Product product : page){
System.out.println(product);
}
}
聚合(aggregation )是Elasticsearch的一个强大功能,可以极其方便地实现对数据的统计、分析工作。搜索是查找某些具体的文档,聚合就是对这些搜索到的文档进行统计,可以聚合出更加细致的数据。它有两个重要概念。
聚合由AggregationBuilders类来构建,它提供的静态方法见表13-3

表 13-3
具体用法见以下代码:
public List<StringTerms.Bucket> searchBybucket(){
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""},null));
//指定索引的类型,只先从各分片中查询匹配的文档,再重新排名,取前size个文档
queryBuilder.withSearchType(SearchType.QUERY_THEN_FETCH);
//添加一个新的聚合,聚合类型为terms,聚合名称为brands,聚合字段为brand
queryBuilder.addAggregation(AggregationBuilders.terms("brand").field("brand"));
//查询,需要把结果强转为Aggregatedpage类型,Aggregatedpage:聚合查询的结果类,它是Page<T>的子接口
AggregatedPage<Product> productsPage = (AggregatedPage<Product>) productRepository.search(queryBuilder.build());
//从结果中取出名为brands的聚合解析
//强转为StringTerm类型
StringTerms aggregations = (StringTerms) productsPage.getAggregation("brands");
//获取桶
List<StringTerms.Bucket> buckets = aggregations.getBuckets();
//遍历
for (StringTerms.Bucket bucket : buckets) {
//获取桶中的key
System.out.println(bucket.getKey());
//获取桶中的文档数量
System.out.println(bucket.getDocCount());
}
return buckets;
}
还可以嵌套聚合,在聚合AggregationBuilders中使用subAggregation,用法见以下代码:
queryBuilder.addAggregation(AggregationBuilders.terms("brand").field("brand")
.subAggregation(AggregationBuilders.avg("price_avg").field("price"))
//在品牌聚合桶内进行嵌套聚合
);
我在app/helpers/sessions_helper.rb中有一个帮助程序文件,其中包含一个方法my_preference,它返回当前登录用户的首选项。我想在集成测试中访问该方法。例如,这样我就可以在测试中使用getuser_path(my_preference)。在其他帖子中,我读到这可以通过在测试文件中包含requiresessions_helper来实现,但我仍然收到错误NameError:undefinedlocalvariableormethod'my_preference'.我做错了什么?require'test_helper'require'sessions_hel
我一直很高兴地使用DelayedJob习惯用法:foo.send_later(:bar)这会调用DelayedJob进程中对象foo的方法bar。我一直在使用DaemonSpawn在我的服务器上启动DelayedJob进程。但是...如果foo抛出异常,Hoptoad不会捕获它。这是任何这些包中的错误...还是我需要更改某些配置...或者我是否需要在DS或DJ中插入一些异常处理来调用Hoptoad通知程序?回应下面的第一条评论。classDelayedJobWorker 最佳答案 尝试monkeypatchingDelayed::W
前置步骤我们都操作完了,这篇开始介绍jenkins的集成。话不多说,看操作1、登录进入jenkins后会让你选择安装插件,选择第一个默认的就行。安装完成后设置账号密码,重新登录。2、配置JDK和Git都需要执行路径,所以需要先把执行路径找到,先进入服务器的docker容器,2.1JDK的路径root@69eef9ee86cf:/usr/bin#echo$JAVA_HOME/usr/local/openjdk-82.2Git的路径root@69eef9ee86cf:/#whichgit/usr/bin/git3、先配置JDK和Git。点击:ManageJenkins>>GlobalToolCon
不知何故,我似乎无法获得包含我的聚合的响应...使用curl它按预期工作:HBZUMB01$curl-XPOST"http://localhost:9200/contents/_search"-d'{"size":0,"aggs":{"sport_count":{"value_count":{"field":"dwid"}}}}'我收到回复:{"took":4,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":90,"max_score":0.0,"hits":[]},"a
三分钟集成Tap防沉迷SDK(Unity版)一、SDK介绍基于国家对上线所有游戏必须增加防沉迷功能的政策下,TapTap推出防沉迷SDK,供游戏开发者进行接入;允许未成年用户在周五、六、日以及法定节假日晚上8:00-9:00进行游戏,防沉谜时间段进入游戏会弹窗进行提示!开发环境要求:Unity2019.4或更高版本iOS10或更高版本Android5.0(APIlevel21)或更高版本🔗Unity集成Demo参考链接🔗UnityTapSDK功能体验APK下载链接二、集成前准备1.创建应用进入开发者后台,按照提示开始创建应用;2.开通服务在使用TDS实名认证和防沉迷服务之前,需要在上面创建的应
1.回顾.TransportServicepublicclassTransportServiceextendsAbstractLifecycleComponentTransportService:方法:1publicfinalTextendsTransportResponse>voidsendRequest(finalTransport.Connectionconnection,finalStringaction,finalTransportRequestrequest,finalTransportRequestOptionsoptions,TransportResponseHandlerT>
我被这个难住了。到目前为止教程中的一切都进行得很顺利,但是当我将这段代码添加到我的/spec/requests/users_spec.rb文件中时,事情开始变得糟糕:describe"success"doit"shouldmakeanewuser"dolambdadovisitsignup_pathfill_in"Name",:with=>"ExampleUser"fill_in"Email",:with=>"ryan@example.com"fill_in"Password",:with=>"foobar"fill_in"Confirmation",:with=>"foobar"cl
我有一个Rails应用程序,现在设置了ElasticSearch和Tiregem以在模型上进行搜索,我想知道我应该如何设置我的应用程序以对模型中的某些索引进行模糊字符串匹配。我将我的模型设置为索引标题、描述等内容,但我想对其中一些进行模糊字符串匹配,但我不确定在何处进行此操作。如果您想发表评论,我将在下面包含我的代码!谢谢!在Controller中:defsearch@resource=Resource.search(params[:q],:page=>(params[:page]||1),:per_page=>15,load:true)end在模型中:classResource'Us
我需要一些指导来了解如何将Angular整合到rails中。选择Rails的原因:我喜欢他们偏执的做事方式。还有迁移,gem真的很酷。使用angular的原因:我正在研究和寻找最适合SPA的框架。Backbone似乎太抽象了。我不得不在Angular和Ember之间做出选择。我首先开始阅读Angular,它对我来说很有意义。所以我从来没有去读过关于ember的文章。使用Angular和Rails的原因:我研究并尝试使用小型框架,例如grape、slim(是的,我也使用php)。但我觉得需要坚持项目的长期范围。我个人喜欢用Rails的方式做事。这就是我需要帮助的地方,我在Rails4中有
有没有人有在Maven中运行用Ruby编写的单元测试的经验。任何输入,如要使用的库/maven插件,将不胜感激!我们已经在使用Maven+hudson+Junit。但是我们正在引入Ruby单元测试,找不到任何同样好的组合。 最佳答案 我建议让Maven使用ExecMavenPlugin启动rake测试(exec:exec目标)并使用ci_reportergem生成单元测试结果的XML文件,Hudson、Bamboo等可以读取该文件,以与JUnit测试相同的格式显示测试结果。如果您不需要使用mvntest运行Ruby测试,您也可以只使