Java客户端
在Elasticsearch中,为java提供了2种客户端,一种是REST风格的客户端,另一种是Java API的客户端。https://www.elastic.co/guide/...
1.REST客户端
Elasticsearch提供了2种REST客户端,一种是低级客户端,一种是高级客户端。
- Java Low Level REST Client:官方提供的低级客户端。该客户端通过http来连接Elasticsearch集群。用户在使用该客户端时需要将请求数据手动拼接成Elasticsearch所需JSON格式进行发送,收到响应时同样也需要将返回的JSON数据手动封装成对象。虽然麻烦,不过该客户端兼容所有的Elasticsearch版本。
- Java High Level REST Client:官方提供的高级客户端。该客户端基于低级客户端实现,它提供了很多便捷的API来解决低级客户端需要手动转换数据格式的问题。
2.构造数据
curl -X POST "http://47.101.129.45:9200/test/house/_bulk?pretty" -H 'Content-Type: application/json' --data-binary '
{"index":{"_index":"test","_type":"house"}}
{"id":"1001","title":"整租 · 南丹大楼 1居室 7500","price":"7500"}
{"index":{"_index":"test","_type":"house"}}
{"id":"1002","title":"陆家嘴板块,精装设计一室一厅,可拎包入住诚意租。","price":"8500"}
{"index":{"_index":"test","_type":"house"}}
{"id":"1003","title":"整租 · 健安坊 1居室 4050","price":"7500"}
{"index":{"_index":"test","_type":"house"}}
{"id":"1004","title":"整租 · 中凯城市之光+视野开阔+景色秀丽+拎包入住","price":"6500"}
{"index":{"_index":"test","_type":"house"}}
{"id":"1005","title":"整租 · 南京西路品质小区 21213三轨交汇配套齐* 拎包入住","price":"6000"}
{"index":{"_index":"test","_type":"house"}}
{"id":"1006","title":"祥康里简约风格 *南户型拎包入住看房随时","price":"7000"}
'
3.REST低级客户端
1)用IDEA创建SpringBoot工程spring-elasticsearch
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.2.0.RELEASE
com.example
elasticsearch
0.0.1-SNAPSHOT
elasticsearch
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter-web
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
junit
junit
4.12
org.elasticsearch.client
elasticsearch-rest-client
6.5.4
com.fasterxml.jackson.core
jackson-databind
2.9.4
org.springframework.boot
spring-boot-maven-plugin
2)编写测试
/**
* REST低级客户端
*/
public class TestElasticSearch {
private static final ObjectMapper MAPPER = new ObjectMapper();
private RestClient restClient;
@Before
public void init() {
RestClientBuilder restClientBuilder = RestClient.builder(
new HttpHost("47.101.129.45", 9200, "http"));
restClientBuilder.setFailureListener(new RestClient.FailureListener() {
@Override
public void onFailure(Node node) {
System.out.println("出错了 -> " + node);
}
});
this.restClient = restClientBuilder.build();
}
@After
public void after() throws IOException {
restClient.close();
}
/**
* 查询集群状态
*
* @throws IOException
*/
@Test
public void testGetInfo() throws IOException {
Request request = new Request("GET", "/_cluster/state");
request.addParameter("pretty", "true");
Response response = this.restClient.performRequest(request);
System.out.println(response.getStatusLine());
System.out.println(EntityUtils.toString(response.getEntity()));
}
/**
* 新增数据
*
* @throws IOException
*/
@Test
public void testCreateData() throws IOException {
Request request = new Request("POST", "/test/house");
request.addParameter("pretty", "true");
Map data = new HashMap<>();
data.put("id", "2001");
data.put("title", "张江高科");
data.put("price", "3500");
request.setJsonEntity(MAPPER.writeValueAsString(data));
Response response = this.restClient.performRequest(request);
System.out.println(response.getStatusLine());
System.out.println(EntityUtils.toString(response.getEntity()));
}
/**
* 根据id查询数据
*/
@Test
public void testQueryData() throws IOException {
Request request = new Request("GET", "/test/house/3xNNOW4BpJzEX51okOM5");
request.addParameter("pretty", "true");
Response response = this.restClient.performRequest(request);
System.out.println(response.getStatusLine());
System.out.println(EntityUtils.toString(response.getEntity()));
}
/**
* 搜索数据
*/
@Test
public void testSearchData() throws IOException {
Request request = new Request("POST", "/test/house/_search");
String searchJson = "{\"query\": {\"match\": {\"title\": \"拎包入住\"}}}";
request.setJsonEntity(searchJson);
request.addParameter("pretty", "true");
Response response = this.restClient.performRequest(request);
System.out.println(response.getStatusLine());
System.out.println(EntityUtils.toString(response.getEntity()));
}
}
从使用中,可以看出,基本和我们使用RESTful api使用几乎是一致的
4.REST高级客户端
pom.xml引入依赖
org.elasticsearch.client
elasticsearch-rest-high-level-client
6.5.4
编写测试
/**
* REST高级客户端
*/
public class TestRestHighLevel {
private RestHighLevelClient client;
@Before
public void init() {
RestClientBuilder restClientBuilder = RestClient.builder(
new HttpHost("47.101.129.45", 9200, "http")
);
this.client = new RestHighLevelClient(restClientBuilder);
}
@After
public void after() throws Exception {
this.client.close();
}
/**
* 新增文档,同步操作
*
* @throws Exception
*/
@Test
public void testCreate() throws Exception {
Map data = new HashMap<>();
data.put("id", "2002");
data.put("title", "南京西路 拎包入住 一室一厅");
data.put("price", "4500");
IndexRequest indexRequest = new IndexRequest("test", "house").source(data);
IndexResponse indexResponse = this.client.index(indexRequest, RequestOptions.DEFAULT);
System.out.println(indexResponse);
System.out.println("id->" + indexResponse.getId());
System.out.println("index->" + indexResponse.getIndex());
System.out.println("type->" + indexResponse.getType());
System.out.println("version->" + indexResponse.getVersion());
System.out.println("result->" + indexResponse.getResult());
System.out.println("shardInfo->" + indexResponse.getShardInfo());
}
/**
* 新增文档,异步操作
*/
@Test
public void testCreateAsync() throws Exception {
Map data = new HashMap<>();
data.put("id", "2003");
data.put("title", "南京东路最新房源二室一厅");
data.put("price", "5500");
IndexRequest indexRequest = new IndexRequest("test", "house").source(data);
this.client.indexAsync(indexRequest, RequestOptions.DEFAULT, new ActionListener() {
@Override
public void onResponse(IndexResponse indexResponse) {
System.out.println(indexResponse);
}
@Override
public void onFailure(Exception e) {
System.out.println(e);
}
});
Thread.sleep(2000);
}
/**
* 指定返回字段查询
*/
@Test
public void testQuery() throws Exception {
GetRequest request = new GetRequest("test", "house", "4hN-OW4BpJzEX51oe-Of");
//指定返回字段
String[] includes = new String[]{"title", "id"};
String[] excludes = Strings.EMPTY_ARRAY;
FetchSourceContext fetchSourceContext = new FetchSourceContext(true, includes, excludes);
request.fetchSourceContext(fetchSourceContext);
GetResponse response = this.client.get(request, RequestOptions.DEFAULT);
System.out.println("数据 -> " + response);
}
/**
* 判断是否存在
*/
@Test
public void testExists() throws Exception {
GetRequest getRequest = new GetRequest("test", "house", "4hN-OW4BpJzEX51oe-Of");
//不返回字段
getRequest.fetchSourceContext(new FetchSourceContext(false));
boolean exists = this.client.exists(getRequest, RequestOptions.DEFAULT);
System.out.println("exists -> " + exists);
}
/**
* 删除数据
*/
@Test
public void testDelete() throws Exception {
DeleteRequest deleteRequest = new DeleteRequest("test", "house", "4hN-OW4BpJzEX51oe-Of");
DeleteResponse response = this.client.delete(deleteRequest, RequestOptions.DEFAULT);
System.out.println(response.status());// OK or NOT_FOUND
}
/**
* 更新数据
*/
@Test
public void testUpdate() throws Exception {
UpdateRequest updateRequest = new UpdateRequest("test", "house", "4BN4OW4BpJzEX51o3-PZ");
Map data = new HashMap<>();
data.put("title", "南京西路2 一室一厅2");
data.put("price", "4000");
updateRequest.doc(data);
UpdateResponse response = this.client.update(updateRequest, RequestOptions.DEFAULT);
System.out.println("version -> " + response.getVersion());
}
/**
* 查询数据
*/
@Test
public void testSearch() throws Exception {
SearchRequest searchRequest = new SearchRequest("test");
searchRequest.types("house");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.matchQuery("title", "拎包入住"));
sourceBuilder.from(0);
sourceBuilder.size(5);
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
searchRequest.source(sourceBuilder);
SearchResponse search = this.client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println("搜索到->" + search.getHits().totalHits + "条数据");
SearchHits hits = search.getHits();
for (SearchHit hit : hits) {
System.out.println(hit.getSourceAsString());
}
}
}
SpringBoot整合Elasticsearch
Spring Data项目对Elasticsearch做了支持,其目的就是简化对Elasticsearch的操作,https://spring.io/projects/sp...。
1.导入依赖
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.2.0.RELEASE
com.example
elasticsearch
0.0.1-SNAPSHOT
elasticsearch
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter-web
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
junit
junit
4.12
org.elasticsearch.client
elasticsearch-rest-client
6.5.4
com.fasterxml.jackson.core
jackson-databind
2.9.4
org.elasticsearch.client
elasticsearch-rest-high-level-client
6.5.4
org.springframework.boot
spring-boot-starter-data-elasticsearch
org.springframework.boot
spring-boot-maven-plugin
2.编写application.yml
spring:
application:
name: spring-elasticsearch
data:
elasticsearch:
cluster-name: docker-cluster
cluster-nodes: 47.101.129.45:9300
这里要注意,使用的端口是9300,而并非9200,原因是9200是RESTful端口,9300是API端口。
ElasticSearch之ElasticsearchTemplate
3.编写测试
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = "demo", type = "person", createIndex = false)
public class Person {
/**
* 1.索引库(indices) indices是index的复数,代表许多的索引,
* 2.类型(type) 类型是模拟mysql中的table概念,一个索引库下可以有不同类型的索引,比如商品索引,订单索引,其数据格式不同。不过这会导致索引库混乱,因此未来版本中会移除这个概念
* 3.文档(document) 存入索引库原始的数据。比如每一条商品信息,就是一个文档
* 4.字段(field) 文档中的属性
* 5.映射配置(mappings) 字段的数据类型、属性、是否索引、是否存储等特性
*/
/**
* @Document 作用在类,标记实体类为文档对象,一般有两个属性
* 1.indexName:对应索引库名称
* 2.type:对应在索引库中的类型
* 3.shards:分片数量,默认5
* 4.replicas:副本数量,默认1
* @Id 作用在成员变量,标记一个字段作为id主键
* @Field 作用在成员变量,标记为文档的字段,并指定字段映射属性:
* 1.type:字段类型,是枚举:FieldType,可以是text、long、short、date、integer、object等
* 2.text:存储数据时候,会自动分词,并生成索引
* 3.keyword:存储数据时候,不会分词建立索引
* 4.Numerical:数值类型,分两类
* 基本数据类型:long、interger、short、byte、double、float、half_float
* 浮点数的高精度类型:scaled_float
* 需要指定一个精度因子,比如10或100。elasticsearch会把真实值乘以这个因子后存储,取出时再还原。
* 5.Date:日期类型
* elasticsearch可以对日期格式化为字符串存储,但是建议我们存储为毫秒值,存储为long,节省空间。
* 6.index:是否索引,布尔类型,默认是true
* 7.store:是否存储,布尔类型,默认是false
* 8.analyzer:分词器名称,这里的ik_max_word即使用ik分词器
*/
@Id
private Long id;
@Field(store = true)
private String name;
@Field
private Integer age;
@Field
private String mail;
@Field(store = true)
private String hobby;
}
1)新增数据
/**
* Spring Data ElasticSearch
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestSpringBootES {
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
/**
* 添加数据
*/
@Test
public void save() {
User user = new User();
user.setId(1001L);
user.setName("赵柳");
user.setAge(20);
user.setHobby("足球、篮球、听音乐");
IndexQuery indexQuery = new IndexQueryBuilder()
.withObject(user).build();
String index = this.elasticsearchTemplate.index(indexQuery);
System.out.println(index);
}
}
2)批量插入
@Test
public void testBulk() {
List list = new ArrayList<>();
for (int i = 0; i < 5000; i++) {
User person = new User();
person.setId(1001L + i);
person.setAge(i % 50 + 10);
person.setName("张三" + i);
person.setHobby("足球、篮球、听音乐");
IndexQuery indexQuery = new IndexQueryBuilder().withObject(person).build();
list.add(indexQuery);
}
Long start = System.currentTimeMillis();
this.elasticsearchTemplate.bulkIndex(list);
System.out.println("用时:" + (System.currentTimeMillis() - start));
}
3)局部更新,全部更新使用index覆盖即可
@Test
public void testUpdate() {
IndexRequest indexRequest = new IndexRequest();
indexRequest.source("age", "30");
UpdateQuery updateQuery = new UpdateQueryBuilder()
.withId("1002")
.withClass(User.class)
.withIndexRequest(indexRequest).build();
UpdateResponse response = this.elasticsearchTemplate.update(updateQuery);
System.out.println(response);
}
4)删除
@Test
public void testDelete() {
String result = this.elasticsearchTemplate.delete(User.class, "1002");
System.out.println(result);
}
5)查询
@Test
public void testSearch() {
PageRequest pageRequest = PageRequest.of(0, 10);//设置分页参数
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.matchQuery("name", "赵柳"))//match查询
.withPageable(pageRequest)
.build();
AggregatedPage persons = this.elasticsearchTemplate.queryForPage(searchQuery, User.class);
System.out.println("persons ->" + persons);
System.out.println("总页数:" + persons.getTotalPages()); //获取总页数
List content = persons.getContent();// 获取搜索到的数据
for (User p : content) {
System.out.println(p);
}
}
ElasticSearch之ElasticsearchRepository
1.创建实体Pojo
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;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = "commodity", type = "docs", shards = 1, replicas = 0)
public class Commodity {
@Id
private Long id;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String title; //标题
@Field(type = FieldType.Keyword)
private String category;// 分类
@Field(type = FieldType.Keyword)
private String brand; // 品牌
@Field(type = FieldType.Double)
private Double price; // 价格
@Field(index = false, type = FieldType.Keyword)
private String images; // 图片地址
}
2.继承ElasticsearchRepository
import com.example.elasticsearch.pojo.Commodity;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import java.util.List;
public interface CommodityRepository extends ElasticsearchRepository {
}
3.测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestSpringBootES2 {
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
@Resource
private CommodityRepository commodityRepository;
/**
* 创建索引
*/
@Test
public void createIndex() {
boolean index = elasticsearchTemplate.createIndex(Commodity.class);
System.out.println(index);
}
/**
* 添加数据
*/
@Test
public void testInsert() {
Commodity commodity = new Commodity(1L, "小米手机7", " 手机",
"小米", 3499.00, "http://image.baidu.com/13123.jpg");
Commodity save = commodityRepository.save(commodity);
System.out.println(save);
}
}
1)批量
@Test
public void testBulk() {
List list = new ArrayList<>();
list.add(new Commodity(2L, "坚果手机R1", " 手机", "锤子", 3699.00, "http://image.baidu.com/13123.jpg"));
list.add(new Commodity(3L, "华为META10", " 手机", "华为", 4499.00, "http://image.baidu.com/13123.jpg"));
list.add(new Commodity(4L, "小米手机7", "手机", "小米", 3299.00, "http://image.baidu.com/13123.jpg"));
list.add(new Commodity(5L, "坚果手机R1", "手机", "锤子", 3699.00, "http://image.baidu.com/13123.jpg"));
list.add(new Commodity(6L, "华为META10", "手机", "华为", 4499.00, "http://image.baidu.com/13123.jpg"));
list.add(new Commodity(7L, "小米Mix2S", "手机", "小米", 4299.00, "http://image.baidu.com/13123.jpg"));
list.add(new Commodity(8L, "荣耀V10", "手机", "华为", 2799.00, "http://image.baidu.com/13123.jpg"));
commodityRepository.saveAll(list);
}
2.修改
@Test
public void testUpdate() {
Commodity commodity = new Commodity(1L, "苹果XSMax", " 手机",
"小米", 3499.00, "http://image.baidu.com/13123.jpg");
commodityRepository.save(commodity);
}
3.降序、升序
/**
* 价格的降序、升序查询
*/
@Test
public void testQueryAll() {
Iterable commodities = commodityRepository.findAll(Sort.by("price").ascending());//降序
for (Commodity commodity : commodities) {
System.out.println(commodity);
}
}
4.按照价格区间查询
CommodityRepository接口新增findByPriceBetween方法
/**
* 根据价格区间查询
*
* @param price1
* @param price2
* @return
*/
List findByPriceBetween(double price1, double price2);
@Test
public void queryByPriceBetween() {
List list = commodityRepository.findByPriceBetween(2000.00, 3500.00);
for (Commodity commodity : list) {
System.out.println("commodity -> " + commodity);
}
}
5.词条匹配查询
@Test
public void testMatchQuery() {
// 构建查询条件,在queryBuilder对象中自定义查询
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
//matchQuery:底层就是使用的termQuery
queryBuilder.withQuery(QueryBuilders.matchQuery("title", "小米手机"));
//查询,search默认就是分页查找
Page commoditys = commodityRepository.search(queryBuilder.build());
// 总条数
long total = commoditys.getTotalElements();
System.out.println("total -> " + total);
for (Commodity item : commoditys) {
System.out.println("商品->" + item);
}
}
/**
* termQuery:功能更强大,除了匹配字符串以外,还可以匹配 int/long/double/float/....
*/
@Test
public void testTermQuery() {
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
builder.withQuery(QueryBuilders.termQuery("price", 3699));
// 查找
Page page = commodityRepository.search(builder.build());
for (Commodity item : page) {
System.out.println("商品->" + item);
}
}
6.bool查詢
@Test
public void testBooleanQuery() {
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
builder.withQuery(
QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("title", "华为"))
.must(QueryBuilders.matchQuery("brand", "华为"))
);
// 查找
Page page = commodityRepository.search(builder.build());
for (Commodity item : page) {
System.out.println("商品->" + item);
}
}
7.模糊查詢
@Test
public void testFuzzyQuery() {
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
builder.withQuery(QueryBuilders.fuzzyQuery("title", "华为"));
Page page = commodityRepository.search(builder.build());
for (Commodity item : page) {
System.out.println("商品->" + item);
}
}
8.分页查询
@Test
public void searchByPage() {
// 构建查询条件
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 添加基本分词查询
queryBuilder.withQuery(QueryBuilders.matchQuery("category", "手机"));
// 分页:
int page = 0;
int size = 10;
queryBuilder.withPageable(PageRequest.of(page, size));
// 搜索,获取结果
Page items = commodityRepository.search(queryBuilder.build());
// 总条数
long total = items.getTotalElements();
System.out.println("总条数 = " + total);
// 总页数
System.out.println("总页数 = " + items.getTotalPages());
// 当前页
System.out.println("当前页:" + items.getNumber());
// 每页大小
System.out.println("每页大小:" + items.getSize());
for (Commodity item : items) {
System.out.println("商品->" + item);
}
}
9.排序查询
@Test
public void searchAndSort() {
// 构建查询条件
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 添加基本分词查询
queryBuilder.withQuery(QueryBuilders.matchQuery("category", "手机"));
// 排序
queryBuilder.withSort(SortBuilders.fieldSort("price").order(SortOrder.ASC));
// 搜索,获取结果
Page items = commodityRepository.search(queryBuilder.build());
// 总条数
long total = items.getTotalElements();
System.out.println("总条数 = " + total);
for (Commodity item : items) {
System.out.println("商品->" + item);
}
}
10.按照品牌brand进行分组
@Test
public void testAgg() {
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 不查询任何结果
queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));
// 1、添加一个新的聚合,聚合类型为terms,聚合名称为brands,聚合字段为brand
queryBuilder.addAggregation(
AggregationBuilders.terms("brands").field("brand"));
// 2、查询,需要把结果强转为AggregatedPage类型
AggregatedPage aggPage = (AggregatedPage) commodityRepository.search(queryBuilder.build());
// 3、解析
// 3.1、从结果中取出名为brands的那个聚合,
// 因为是利用String类型字段来进行的term聚合,所以结果要强转为StringTerm类型
StringTerms agg = (StringTerms) aggPage.getAggregation("brands");
// 3.2、获取桶
List buckets = agg.getBuckets();
// 3.3、遍历
for (StringTerms.Bucket bucket : buckets) {
// 3.4、获取桶中的key,即品牌名称
System.out.println("key->" + bucket.getKeyAsString());
// 3.5、获取桶中的文档数量
System.out.println("count->" + bucket.getDocCount());
}
}
11.嵌套聚合,求平均值
@Test
public void testSubAgg() {
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 不查询任何结果
queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));
// 1、添加一个新的聚合,聚合类型为terms,聚合名称为brands,聚合字段为brand
queryBuilder.addAggregation(
AggregationBuilders.terms("brands").field("brand")
.subAggregation(AggregationBuilders.avg("priceAvg").field("price")) // 在品牌聚合桶内进行嵌套聚合,求平均值
);
// 2、查询,需要把结果强转为AggregatedPage类型
AggregatedPage aggPage = (AggregatedPage) commodityRepository.search(queryBuilder.build());
// 3、解析
// 3.1、从结果中取出名为brands的那个聚合,
// 因为是利用String类型字段来进行的term聚合,所以结果要强转为StringTerm类型
StringTerms agg = (StringTerms) aggPage.getAggregation("brands");
// 3.2、获取桶
List buckets = agg.getBuckets();
// 3.3、遍历
for (StringTerms.Bucket bucket : buckets) {
// 3.4、获取桶中的key,即品牌名称 3.5、获取桶中的文档数量
System.out.println(bucket.getKeyAsString() + ",共" + bucket.getDocCount() + "台");
// 3.6.获取子聚合结果:
InternalAvg avg = (InternalAvg) bucket.getAggregations().asMap().get("priceAvg");
System.out.println("平均售价:" + avg.getValue());
}
}