Simplify Elasticsearch with Easy-Es: MyBatis‑Plus‑Style ORM Tutorial
This article introduces Easy-Es, an Elasticsearch ORM that mimics MyBatis‑Plus syntax, walks through its core features, integration steps, entity annotations, mapper and service implementations, and demonstrates simple, advanced, and recommendation search scenarios, highlighting the reduction in boilerplate code.
Easy-Es Overview
Easy-Es (EE) is an ORM framework built on Elasticsearch's RestHighLevelClient. It provides MyBatis‑Plus‑like APIs, allowing developers familiar with MyBatis‑Plus to quickly adopt EE. Its philosophy is to keep simple, easy‑to‑use features for users while handling complex details internally.
Key Features
Automatic index management – developers do not need to create, update or migrate indices manually.
SQL‑like syntax – only MySQL syntax is required to operate Elasticsearch.
Reduced code – EE can save 3‑5 times the code compared with using RestHighLevelClient directly.
No magic values – field names are obtained directly from entity classes.
Zero learning cost – MyBatis‑Plus users can migrate to EE without additional learning.
MySQL vs Easy-Es Syntax Comparison
MySQL
Easy-Es
es-DSL/es java api
and
and
must
or
or
should
=
eq
term
!=
not
boolQueryBuilder.mustNot(queryBuilder)
>
gt
QueryBuilders.rangeQuery('es field').gt()
>=
ge
.rangeQuery('es field').gte()
<
lt
.rangeQuery('es field').lt()
<=
le
.rangeQuery('es field').lte()
like '%field%'
like
QueryBuilders.wildcardQuery(field, value)
not like '%field%'
notLike
must not wildcardQuery(field, value)
like '%field'
likeLeft
QueryBuilders.wildcardQuery(field,*value)
like 'field%'
likeRight
QueryBuilders.wildcardQuery(field,value*)
between
between
QueryBuilders.rangeQuery('es field').from(xx).to(xx)
notBetween
notBetween
must not QueryBuilders.rangeQuery('es field').from(xx).to(xx)
is null
isNull
must not QueryBuilders.existsQuery(field)
is notNull
isNotNull
QueryBuilders.existsQuery(field)
in
in
QueryBuilders.termsQuery("xx es field", xx)
not in
notIn
must not QueryBuilders.termsQuery("xx es field", xx)
group by
groupBy
AggregationBuilders.terms()
order by
orderBy
fieldSortBuilder.order(ASC/DESC)
min
min
AggregationBuilders.min
max
max
AggregationBuilders.max
avg
avg
AggregationBuilders.avg
sum
sum
AggregationBuilders.sum
order by xxx asc
orderByAsc
fieldSortBuilder.order(SortOrder.ASC)
order by xxx desc
orderByDesc
fieldSortBuilder.order(SortOrder.DESC)
-
match
matchQuery
-
matchPhrase
QueryBuilders.matchPhraseQuery
-
matchPrefix
QueryBuilders.matchPhrasePrefixQuery
-
queryStringQuery
QueryBuilders.queryStringQuery
select *
matchAllQuery
QueryBuilders.matchAllQuery()
-
highLight
HighlightBuilder.Field
Integration and Configuration
Add the Easy-Es starter dependency to pom.xml:
<dependency>
<groupId>org.dromara.easy-es</groupId>
<artifactId>easy-es-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>Set the Elasticsearch client version to 7.17.28 and the server version to 7.17.3 in dependencyManagement:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.17.28</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.17.28</version>
</dependency>
</dependencies>
</dependencyManagement>Configure EE in application.yml:
easy-es:
enable: true
address: localhost:9200
banner: falseEnable mapper scanning and define a configuration class:
@Configuration
@EsMapperScan("com.macro.blog.easyes")
public class EasyEsConfig {
}Entity Annotations
Create a document class EsProduct and annotate it with EE annotations such as @IndexName, @IndexId, and @IndexField. Example:
@Data
@EqualsAndHashCode
@IndexName(value = "es_product")
public class EsProduct implements Serializable {
private static final long serialVersionUID = -1L;
@IndexId(type = IdType.CUSTOMIZE)
private Long id;
@IndexField(fieldType = FieldType.KEYWORD)
private String productSn;
private Long brandId;
@IndexField(fieldType = FieldType.KEYWORD)
private String brandName;
private Long productCategoryId;
@IndexField(fieldType = FieldType.KEYWORD)
private String productCategoryName;
private String pic;
@IndexField(fieldType = FieldType.TEXT, analyzer = "ik_max_word")
private String name;
@IndexField(fieldType = FieldType.TEXT, analyzer = "ik_max_word")
private String subTitle;
@IndexField(fieldType = FieldType.TEXT, analyzer = "ik_max_word")
private String keywords;
private BigDecimal price;
private Integer sale;
private Integer newStatus;
private Integer recommandStatus;
private Integer stock;
private Integer promotionType;
private Integer sort;
@IndexField(fieldType = FieldType.NESTED, nestedOrObjectClass = EsProductAttributeValue.class)
private List<EsProductAttributeValue> attrValueList;
}The nested type EsProductAttributeValue is defined similarly:
@Data
@EqualsAndHashCode
public class EsProductAttributeValue implements Serializable {
private static final long serialVersionUID = 1L;
@IndexField(fieldType = FieldType.LONG)
private Long id;
@IndexField(fieldType = FieldType.KEYWORD)
private Long productAttributeId;
@IndexField(fieldType = FieldType.KEYWORD)
private String value;
@IndexField(fieldType = FieldType.INTEGER)
private Integer type;
@IndexField(fieldType = FieldType.KEYWORD)
private String name;
}Mapper and Service Implementation
Define a mapper interface extending BaseEsMapper<EsProduct>:
public interface EsProductMapper extends BaseEsMapper<EsProduct> {}Implement service methods for importing, creating, deleting, and searching products. Example of a simple search method:
public EsPageInfo<EsProduct> search(String keyword, Integer pageNum, Integer pageSize) {
LambdaEsQueryWrapper<EsProduct> wrapper = new LambdaEsQueryWrapper<>();
if (StrUtil.isEmpty(keyword)) {
wrapper.matchAllQuery();
} else {
wrapper.multiMatchQuery(keyword, EsProduct::getName, EsProduct::getSubTitle, EsProduct::getKeywords);
}
return esProductMapper.pageQuery(wrapper, pageNum, pageSize);
}Advanced search combines filters, weighted field matching, and sorting based on user‑selected criteria:
public EsPageInfo<EsProduct> search(String keyword, Long brandId, Long productCategoryId,
Integer pageNum, Integer pageSize, Integer sort) {
LambdaEsQueryWrapper<EsProduct> wrapper = new LambdaEsQueryWrapper<>();
if (brandId != null) { wrapper.eq(EsProduct::getBrandId, brandId); }
if (productCategoryId != null) { wrapper.eq(EsProduct::getProductCategoryId, productCategoryId); }
if (StrUtil.isEmpty(keyword)) {
wrapper.matchAllQuery();
} else {
wrapper.and(w -> w.match(EsProduct::getName, keyword, 10f)
.or().match(EsProduct::getSubTitle, keyword, 5f)
.or().match(EsProduct::getKeywords, keyword, 2f));
}
// Sorting logic (by newness, sales, price, or relevance) omitted for brevity
return esProductMapper.pageQuery(wrapper, pageNum, pageSize);
}Recommendation method searches for related products based on the current product’s name, brand, and category while excluding the current product:
public EsPageInfo<EsProduct> recommend(Long id, Integer pageNum, Integer pageSize) {
LambdaEsQueryWrapper<EsProduct> wrapper = new LambdaEsQueryWrapper<>();
List<EsProduct> list = getAllEsProductList(id);
if (!list.isEmpty()) {
EsProduct p = list.get(0);
wrapper.not().eq(EsProduct::getId, id)
.and(w -> w.match(EsProduct::getName, p.getName(), 8f)
.or().match(EsProduct::getSubTitle, p.getName(), 2f)
.or().match(EsProduct::getKeywords, p.getName(), 2f)
.or().match(EsProduct::getBrandId, p.getBrandId(), 5f)
.or().match(EsProduct::getProductCategoryId, p.getProductCategoryId(), 3f));
}
return esProductMapper.pageQuery(wrapper, pageNum, pageSize);
}Conclusion
Rewriting the previous Spring Data product search with Easy‑Es demonstrates that EE greatly simplifies code while still allowing complex aggregation queries through the underlying RestHighLevelClient when needed.
References
Official documentation: https://www.easy-es.cn/
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
