mongodb先注释 es测试
parent
320a04057e
commit
be59d6b2ad
@ -1,4 +1,6 @@
|
||||
package com.ruoyi.biemo.nlp;
|
||||
package com.ruoyi;
|
||||
|
||||
import com.ruoyi.biemo.nlp.NamedEntity;
|
||||
|
||||
public class Test {
|
||||
|
@ -0,0 +1,14 @@
|
||||
package com.ruoyi.biemo.elasticsearch.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author zcw
|
||||
* @version 1.0
|
||||
* @date 2021/1/14 11:54
|
||||
*/
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
public @interface EsId {
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package com.ruoyi.biemo.elasticsearch.controller;
|
||||
|
||||
import com.ruoyi.biemo.elasticsearch.entity.Topic;
|
||||
import com.ruoyi.biemo.elasticsearch.service.TestService;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* @author zcw
|
||||
* @version 1.0
|
||||
* @date 2021/1/14 11:19
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(value = "/es")
|
||||
public class TestESController {
|
||||
|
||||
@Autowired
|
||||
private TestService testService;
|
||||
|
||||
@PostMapping(value = "insert")
|
||||
public void insertOrUpdateOne(@RequestBody Topic entity) {
|
||||
testService.insertOrUpdateOne(entity);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/get")
|
||||
public List<Topic> get() {
|
||||
return testService.search(new SearchSourceBuilder());
|
||||
}
|
||||
|
||||
@GetMapping(value = "/test111")
|
||||
public List<Topic> test(){
|
||||
return testService.test();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.ruoyi.biemo.elasticsearch.entity;
|
||||
|
||||
import com.ruoyi.biemo.elasticsearch.annotation.EsId;
|
||||
import lombok.Data;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
|
||||
@Data
|
||||
@Document(indexName = "topic")
|
||||
public class Topic {
|
||||
|
||||
@EsId
|
||||
private Long id;
|
||||
private Long subject;
|
||||
private Long type;
|
||||
private String img;
|
||||
private String content;
|
||||
private String contentStr;
|
||||
private Long score;
|
||||
private String audio;
|
||||
private String video;
|
||||
private Boolean disabled;
|
||||
private Boolean deleted;
|
||||
private String createdAt;
|
||||
private String updatedAt;
|
||||
private String answer;
|
||||
private String three;
|
||||
private String checkvalue;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package com.ruoyi.biemo.elasticsearch.function;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.invoke.SerializedLambda;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* @author zcw
|
||||
* @version 1.0
|
||||
* @date 2021/1/14 13:50
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface GFunction<T, R> extends Function<T, R>, Serializable {
|
||||
|
||||
String get = "get";
|
||||
|
||||
String is = "is";
|
||||
|
||||
@SneakyThrows
|
||||
default SerializedLambda getSerializedLambda() {
|
||||
Method write = this.getClass().getDeclaredMethod("writeReplace");
|
||||
write.setAccessible(true);
|
||||
return (SerializedLambda) write.invoke(this);
|
||||
}
|
||||
|
||||
default String field() {
|
||||
SerializedLambda serializedLambda = this.getSerializedLambda();
|
||||
String methodName = serializedLambda.getImplMethodName();
|
||||
return resolveFieldName(methodName);
|
||||
}
|
||||
default String resolveFieldName(String getMethodName) {
|
||||
if (getMethodName.startsWith(get)) {
|
||||
getMethodName = getMethodName.substring(3);
|
||||
} else if (getMethodName.startsWith(is)) {
|
||||
getMethodName = getMethodName.substring(2);
|
||||
}
|
||||
getMethodName = getMethodName.substring(0, 1).toLowerCase() + getMethodName.substring(1);
|
||||
return getMethodName;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package com.ruoyi.biemo.elasticsearch.service;
|
||||
|
||||
import com.ruoyi.biemo.elasticsearch.entity.Topic;
|
||||
import com.ruoyi.biemo.elasticsearch.util.EsService;
|
||||
import org.elasticsearch.common.unit.Fuzziness;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author zcw
|
||||
* @version 1.0
|
||||
* @date 2021/1/14 11:59
|
||||
*/
|
||||
@Service
|
||||
public class TestService extends EsService<Topic> {
|
||||
|
||||
public List<Topic> test() {
|
||||
esLambdaQuery().notIn(Topic::getId, 1, 2).delete();
|
||||
List<Topic> topics = esLambdaQuery().between(Topic::getId, 2, 3).query();
|
||||
return topics;
|
||||
}
|
||||
|
||||
public List<Topic> match() {
|
||||
return esLambdaQuery().fuzzyAll(Topic::getContent, Fuzziness.TWO, "abc").query();
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
esLambdaQuery().eq(Topic::getId,1).delete();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,352 @@
|
||||
package com.ruoyi.biemo.elasticsearch.util;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.ruoyi.biemo.elasticsearch.annotation.EsId;
|
||||
import com.ruoyi.biemo.elasticsearch.function.GFunction;
|
||||
import lombok.Data;
|
||||
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
|
||||
import org.elasticsearch.action.bulk.BulkRequest;
|
||||
import org.elasticsearch.action.delete.DeleteRequest;
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.client.RequestOptions;
|
||||
import org.elasticsearch.client.RestHighLevelClient;
|
||||
import org.elasticsearch.client.indices.GetIndexRequest;
|
||||
import org.elasticsearch.common.unit.Fuzziness;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.index.query.BoolQueryBuilder;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
import org.elasticsearch.search.sort.SortOrder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author zcw
|
||||
* @version 1.0
|
||||
* @date 2021/1/14 10:47
|
||||
*/
|
||||
public abstract class EsService<T> {
|
||||
|
||||
@Autowired
|
||||
private RestHighLevelClient client;
|
||||
|
||||
private String index;
|
||||
|
||||
private Class<T> entity;
|
||||
|
||||
private Method getId;
|
||||
|
||||
protected EsService() {
|
||||
Type type = this.getClass().getGenericSuperclass();
|
||||
ParameterizedType parameterizedType = (ParameterizedType) type;
|
||||
Type[] t = parameterizedType.getActualTypeArguments();
|
||||
this.entity = (Class<T>) t[0];
|
||||
if (this.entity.isAnnotationPresent(Document.class)) {
|
||||
this.index = this.entity.getAnnotation(Document.class).indexName();
|
||||
}
|
||||
Field[] fields = this.entity.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
if (field.isAnnotationPresent(EsId.class)) {
|
||||
String fieldName = field.getName();
|
||||
String FieldName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
|
||||
String methodName = GFunction.get + FieldName;
|
||||
Method getMethod = null;
|
||||
try {
|
||||
getMethod = this.entity.getMethod(methodName, null);
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
this.getId = getMethod;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean indexExist() throws Exception {
|
||||
GetIndexRequest request = new GetIndexRequest(index);
|
||||
request.local(false);
|
||||
request.humanReadable(true);
|
||||
request.includeDefaults(false);
|
||||
return client.indices().exists(request, RequestOptions.DEFAULT);
|
||||
}
|
||||
|
||||
public void insertOrUpdateOne(T entity) {
|
||||
IndexRequest request = new IndexRequest(index);
|
||||
try {
|
||||
request.id(this.getId.invoke(entity, null).toString());
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
request.source(JSON.toJSONString(entity), XContentType.JSON);
|
||||
try {
|
||||
client.index(request, RequestOptions.DEFAULT);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void insertBatch(List<T> list) {
|
||||
BulkRequest request = new BulkRequest();
|
||||
list.forEach(item -> {
|
||||
try {
|
||||
request.add(new IndexRequest(index).id(getId.invoke(item, null).toString())
|
||||
.source(JSON.toJSONString(item), XContentType.JSON));
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
try {
|
||||
client.bulk(request, RequestOptions.DEFAULT);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteBatch(List<Long> idList) {
|
||||
BulkRequest request = new BulkRequest();
|
||||
idList.forEach(item -> request.add(new DeleteRequest(index, item.toString())));
|
||||
try {
|
||||
client.bulk(request, RequestOptions.DEFAULT);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public List<T> search(SearchSourceBuilder builder) {
|
||||
SearchRequest request = new SearchRequest(index);
|
||||
request.source(builder);
|
||||
try {
|
||||
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
|
||||
SearchHit[] hits = response.getHits().getHits();
|
||||
List<T> res = new ArrayList<>(hits.length);
|
||||
for (SearchHit hit : hits) {
|
||||
res.add(JSON.parseObject(hit.getSourceAsString(), entity));
|
||||
}
|
||||
return res;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteIndex() {
|
||||
try {
|
||||
client.indices().delete(new DeleteIndexRequest(index), RequestOptions.DEFAULT);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteByQuery(QueryBuilder builder) {
|
||||
DeleteByQueryRequest request = new DeleteByQueryRequest(index);
|
||||
request.setQuery(builder);
|
||||
request.setBatchSize(10000);
|
||||
request.setConflicts("proceed");
|
||||
try {
|
||||
client.deleteByQuery(request, RequestOptions.DEFAULT);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected EsQueryBuilder esLambdaQuery() {
|
||||
return new EsQueryBuilder();
|
||||
}
|
||||
|
||||
@Data
|
||||
protected class EsQueryBuilder {
|
||||
|
||||
private SearchSourceBuilder searchSourceBuilder;
|
||||
|
||||
private BoolQueryBuilder boolQueryBuilder;
|
||||
|
||||
private EsQueryBuilder() {
|
||||
if (this.searchSourceBuilder == null) {
|
||||
this.searchSourceBuilder = new SearchSourceBuilder();
|
||||
}
|
||||
if (this.boolQueryBuilder == null) {
|
||||
this.boolQueryBuilder = QueryBuilders.boolQuery();
|
||||
}
|
||||
}
|
||||
|
||||
public EsQueryBuilder eq(GFunction<? extends T, Object> gFunction, Object value) {
|
||||
String field = gFunction.field();
|
||||
this.searchSourceBuilder.postFilter(QueryBuilders.termQuery(field, value));
|
||||
this.boolQueryBuilder.filter(QueryBuilders.termQuery(field, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EsQueryBuilder notEq(GFunction<? extends T, Object> gFunction, Object value) {
|
||||
String field = gFunction.field();
|
||||
this.searchSourceBuilder.postFilter(QueryBuilders.boolQuery().mustNot(QueryBuilders.termQuery(field, value)));
|
||||
this.boolQueryBuilder.mustNot(QueryBuilders.termQuery(field, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EsQueryBuilder in(GFunction<? extends T, Object> gFunction, Object... values) {
|
||||
String field = gFunction.field();
|
||||
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
|
||||
for (Object value : values) {
|
||||
boolQueryBuilder.should(QueryBuilders.termQuery(field, value));
|
||||
this.boolQueryBuilder.should(QueryBuilders.termQuery(field, value));
|
||||
}
|
||||
this.searchSourceBuilder.postFilter(boolQueryBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EsQueryBuilder notIn(GFunction<? extends T, Object> gFunction, Object... values) {
|
||||
String field = gFunction.field();
|
||||
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
|
||||
for (Object value : values) {
|
||||
boolQueryBuilder.mustNot(QueryBuilders.termQuery(field, value));
|
||||
this.boolQueryBuilder.mustNot(QueryBuilders.termQuery(field, value));
|
||||
}
|
||||
this.searchSourceBuilder.postFilter(boolQueryBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EsQueryBuilder between(GFunction<? extends T, Object> gFunction, Object begin, Object end) {
|
||||
String field = gFunction.field();
|
||||
this.searchSourceBuilder.postFilter(QueryBuilders.boolQuery().filter(QueryBuilders.rangeQuery(field).from(begin).to(end)));
|
||||
this.boolQueryBuilder.filter(QueryBuilders.rangeQuery(field).from(begin).to(end));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EsQueryBuilder notBetween(GFunction<? extends T, Object> gFunction, Object begin, Object end) {
|
||||
String field = gFunction.field();
|
||||
this.searchSourceBuilder.postFilter(QueryBuilders.boolQuery().mustNot(QueryBuilders.rangeQuery(field).from(begin).to(end)));
|
||||
this.boolQueryBuilder.mustNot(QueryBuilders.rangeQuery(field).from(begin).to(end));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EsQueryBuilder gt(GFunction<? extends T, Object> gFunction, Object value) {
|
||||
String field = gFunction.field();
|
||||
this.searchSourceBuilder.postFilter(QueryBuilders.rangeQuery(field).gt(value));
|
||||
this.boolQueryBuilder.filter(QueryBuilders.rangeQuery(field).gt(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EsQueryBuilder lt(GFunction<? extends T, Object> gFunction, Object value) {
|
||||
String field = gFunction.field();
|
||||
this.searchSourceBuilder.postFilter(QueryBuilders.rangeQuery(field).lt(value));
|
||||
this.boolQueryBuilder.filter(QueryBuilders.rangeQuery(field).lt(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EsQueryBuilder ge(GFunction<? extends T, Object> gFunction, Object value) {
|
||||
String field = gFunction.field();
|
||||
this.searchSourceBuilder.postFilter(QueryBuilders.rangeQuery(field).gte(value));
|
||||
this.boolQueryBuilder.filter(QueryBuilders.rangeQuery(field).gte(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EsQueryBuilder le(GFunction<? extends T, Object> gFunction, Object value) {
|
||||
String field = gFunction.field();
|
||||
this.searchSourceBuilder.postFilter(QueryBuilders.rangeQuery(field).lte(value));
|
||||
this.boolQueryBuilder.filter(QueryBuilders.rangeQuery(field).lte(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EsQueryBuilder sort(GFunction<? extends T, Object> gFunction, SortOrder value) {
|
||||
this.searchSourceBuilder.sort(gFunction.field(), value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EsQueryBuilder page(Integer pageNo, Integer pageSize) {
|
||||
pageNo = (pageNo - 1) * pageSize;
|
||||
this.searchSourceBuilder.from(pageNo).size(pageSize);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EsQueryBuilder matchAll(GFunction<? extends T, Object> gFunction, Object... values) {
|
||||
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
|
||||
String field = gFunction.field();
|
||||
for (Object value : values) {
|
||||
boolQueryBuilder.filter(QueryBuilders.matchPhrasePrefixQuery(field, value));
|
||||
this.boolQueryBuilder.filter(QueryBuilders.matchPhrasePrefixQuery(field, value));
|
||||
}
|
||||
this.searchSourceBuilder.postFilter(boolQueryBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EsQueryBuilder matchOne(GFunction<? extends T, Object> gFunction, Object... values) {
|
||||
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
|
||||
String field = gFunction.field();
|
||||
for (Object value : values) {
|
||||
boolQueryBuilder.should(QueryBuilders.matchPhrasePrefixQuery(field, value));
|
||||
this.boolQueryBuilder.should(QueryBuilders.matchPhrasePrefixQuery(field, value));
|
||||
}
|
||||
this.searchSourceBuilder.postFilter(boolQueryBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EsQueryBuilder notMatch(GFunction<? extends T, Object> gFunction, Object value) {
|
||||
String field = gFunction.field();
|
||||
this.boolQueryBuilder.mustNot(QueryBuilders.matchPhrasePrefixQuery(field, value));
|
||||
this.searchSourceBuilder.postFilter(QueryBuilders.boolQuery().mustNot(QueryBuilders.matchPhrasePrefixQuery(field, value)));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EsQueryBuilder fuzzyAll(GFunction<? extends T, Object> gFunction, Fuzziness fuzziness, Object... values) {
|
||||
String field = gFunction.field();
|
||||
for (Object value : values) {
|
||||
this.searchSourceBuilder.postFilter(QueryBuilders.fuzzyQuery(field, value).fuzziness(fuzziness));
|
||||
this.boolQueryBuilder.filter(QueryBuilders.fuzzyQuery(field, value).fuzziness(fuzziness));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public EsQueryBuilder fuzzyOne(GFunction<? extends T, Object> gFunction, Fuzziness fuzziness, Object... values) {
|
||||
String field = gFunction.field();
|
||||
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
|
||||
for (Object value : values) {
|
||||
boolQueryBuilder.should(QueryBuilders.fuzzyQuery(field, value).fuzziness(fuzziness));
|
||||
this.boolQueryBuilder.should(QueryBuilders.fuzzyQuery(field, value).fuzziness(fuzziness));
|
||||
}
|
||||
this.searchSourceBuilder.postFilter(boolQueryBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EsQueryBuilder notFuzzy(GFunction<? extends T, Object> gFunction, Fuzziness fuzziness, Object value) {
|
||||
String field = gFunction.field();
|
||||
this.boolQueryBuilder.mustNot(QueryBuilders.fuzzyQuery(field, value).fuzziness(fuzziness));
|
||||
this.searchSourceBuilder.postFilter(QueryBuilders.boolQuery().mustNot(QueryBuilders.fuzzyQuery(field, value).fuzziness(fuzziness)));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EsQueryBuilder isNull(GFunction<? extends T, Object> gFunction) {
|
||||
String field = gFunction.field();
|
||||
this.boolQueryBuilder.filter(QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery(field)));
|
||||
this.searchSourceBuilder.postFilter(QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery(field)));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EsQueryBuilder isNotNull(GFunction<? extends T, Object> gFunction) {
|
||||
String field = gFunction.field();
|
||||
this.boolQueryBuilder.filter(QueryBuilders.existsQuery(field));
|
||||
this.searchSourceBuilder.postFilter(QueryBuilders.existsQuery(field));
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<T> query() {
|
||||
return search(this.searchSourceBuilder);
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
deleteByQuery(this.boolQueryBuilder);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,36 +1,36 @@
|
||||
package com.ruoyi.biemo.mongodb.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.mongodb.MongoDatabaseFactory;
|
||||
import org.springframework.data.mongodb.core.convert.DbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper;
|
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
|
||||
/**
|
||||
* 配置去掉_class字段
|
||||
*
|
||||
* @author 陈钇蒙
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
public class MongoConverterConfig {
|
||||
@Autowired
|
||||
private MongoDatabaseFactory mongoDatabaseFactory;
|
||||
|
||||
@Autowired
|
||||
private MongoMappingContext mongoMappingContext;
|
||||
|
||||
@Bean
|
||||
public MappingMongoConverter mappingMongoConverter() {
|
||||
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDatabaseFactory);
|
||||
MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mongoMappingContext);
|
||||
// 此处是去除插入数据库的 _class 字段
|
||||
converter.setTypeMapper(new DefaultMongoTypeMapper(null));
|
||||
|
||||
return converter;
|
||||
}
|
||||
}
|
||||
//package com.ruoyi.biemo.mongodb.config;
|
||||
//
|
||||
//import org.springframework.beans.factory.annotation.Autowired;
|
||||
//import org.springframework.context.annotation.Bean;
|
||||
//import org.springframework.context.annotation.Configuration;
|
||||
//import org.springframework.data.mongodb.MongoDatabaseFactory;
|
||||
//import org.springframework.data.mongodb.core.convert.DbRefResolver;
|
||||
//import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
|
||||
//import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper;
|
||||
//import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||
//import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
//
|
||||
///**
|
||||
// * 配置去掉_class字段
|
||||
// *
|
||||
// * @author 陈钇蒙
|
||||
// *
|
||||
// */
|
||||
//@Configuration
|
||||
//public class MongoConverterConfig {
|
||||
// @Autowired
|
||||
// private MongoDatabaseFactory mongoDatabaseFactory;
|
||||
//
|
||||
// @Autowired
|
||||
// private MongoMappingContext mongoMappingContext;
|
||||
//
|
||||
// @Bean
|
||||
// public MappingMongoConverter mappingMongoConverter() {
|
||||
// DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDatabaseFactory);
|
||||
// MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mongoMappingContext);
|
||||
// // 此处是去除插入数据库的 _class 字段
|
||||
// converter.setTypeMapper(new DefaultMongoTypeMapper(null));
|
||||
//
|
||||
// return converter;
|
||||
// }
|
||||
//}
|
||||
|
@ -1,120 +1,120 @@
|
||||
package com.ruoyi.biemo.mongodb.config;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import com.ruoyi.biemo.mongodb.bean.IgnoreDocument;
|
||||
import com.ruoyi.biemo.mongodb.bean.InitValue;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.index.IndexOperations;
|
||||
import org.springframework.data.mongodb.core.index.IndexResolver;
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolver;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.Update;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.mongodb.client.result.UpdateResult;
|
||||
|
||||
import com.ruoyi.biemo.mongodb.utils.PackageUtil;
|
||||
import cn.hutool.core.util.ClassUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
|
||||
/**
|
||||
* 启动时将表初始化
|
||||
*
|
||||
*/
|
||||
@Service
|
||||
public class ScanNewField {
|
||||
@Autowired
|
||||
PackageUtil packageUtil;
|
||||
// 写链接(写到主库,可使用事务)
|
||||
@Autowired
|
||||
private MongoTemplate mongoTemplate;
|
||||
|
||||
@Autowired
|
||||
MongoMappingContext mongoMappingContext;
|
||||
|
||||
@PostConstruct
|
||||
public void scan() {
|
||||
// 找到主程序包
|
||||
Set<Class<?>> set = ClassUtil.scanPackage(packageUtil.getMainPackage());
|
||||
for (Class<?> clazz : set) {
|
||||
IgnoreDocument ignoreDocument = clazz.getAnnotation(IgnoreDocument.class);
|
||||
if (ignoreDocument != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Document document = clazz.getAnnotation(Document.class);
|
||||
if (document == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 创建表
|
||||
if (!mongoTemplate.collectionExists(clazz)) {
|
||||
mongoTemplate.createCollection(clazz);
|
||||
System.out.println("创建了" + clazz.getSimpleName() + "表");
|
||||
}
|
||||
|
||||
// 创建索引
|
||||
IndexOperations indexOps = mongoTemplate.indexOps(clazz);
|
||||
IndexResolver resolver = new MongoPersistentEntityIndexResolver(mongoMappingContext);
|
||||
resolver.resolveIndexFor(clazz).forEach(indexOps::ensureIndex);
|
||||
|
||||
Field[] fields = ReflectUtil.getFields(clazz);
|
||||
for (Field field : fields) {
|
||||
// 获取注解
|
||||
if (field.isAnnotationPresent(InitValue.class)) {
|
||||
InitValue initValue = field.getAnnotation(InitValue.class);
|
||||
if (initValue.value() != null) {
|
||||
|
||||
// 更新表默认值
|
||||
Query query = new Query();
|
||||
query.addCriteria(Criteria.where(field.getName()).is(null));
|
||||
|
||||
Long count = mongoTemplate.count(query, clazz);
|
||||
if (count > 0) {
|
||||
Object value = null;
|
||||
Class<?> type = field.getType();
|
||||
|
||||
if (type.equals(String.class)) {
|
||||
value = initValue.value();
|
||||
}
|
||||
if (type.equals(Short.class)) {
|
||||
value = Short.parseShort(initValue.value());
|
||||
}
|
||||
if (type.equals(Integer.class)) {
|
||||
value = Integer.parseInt(initValue.value());
|
||||
}
|
||||
if (type.equals(Long.class)) {
|
||||
value = Long.parseLong(initValue.value());
|
||||
}
|
||||
if (type.equals(Float.class)) {
|
||||
value = Float.parseFloat(initValue.value());
|
||||
}
|
||||
if (type.equals(Double.class)) {
|
||||
value = Double.parseDouble(initValue.value());
|
||||
}
|
||||
if (type.equals(Boolean.class)) {
|
||||
value = Boolean.parseBoolean(initValue.value());
|
||||
}
|
||||
|
||||
Update update = new Update().set(field.getName(), value);
|
||||
UpdateResult updateResult = mongoTemplate.updateMulti(query, update, clazz);
|
||||
|
||||
System.out.println(clazz.getSimpleName() + "表更新了" + updateResult.getModifiedCount() + "条默认值");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//package com.ruoyi.biemo.mongodb.config;
|
||||
//
|
||||
//import java.lang.reflect.Field;
|
||||
//import java.util.Set;
|
||||
//
|
||||
//import javax.annotation.PostConstruct;
|
||||
//
|
||||
//import com.ruoyi.biemo.mongodb.bean.IgnoreDocument;
|
||||
//import com.ruoyi.biemo.mongodb.bean.InitValue;
|
||||
//import org.springframework.beans.factory.annotation.Autowired;
|
||||
//import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
//import org.springframework.data.mongodb.core.index.IndexOperations;
|
||||
//import org.springframework.data.mongodb.core.index.IndexResolver;
|
||||
//import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolver;
|
||||
//import org.springframework.data.mongodb.core.mapping.Document;
|
||||
//import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
//import org.springframework.data.mongodb.core.query.Criteria;
|
||||
//import org.springframework.data.mongodb.core.query.Query;
|
||||
//import org.springframework.data.mongodb.core.query.Update;
|
||||
//import org.springframework.stereotype.Service;
|
||||
//
|
||||
//import com.mongodb.client.result.UpdateResult;
|
||||
//
|
||||
//import com.ruoyi.biemo.mongodb.utils.PackageUtil;
|
||||
//import cn.hutool.core.util.ClassUtil;
|
||||
//import cn.hutool.core.util.ReflectUtil;
|
||||
//
|
||||
///**
|
||||
// * 启动时将表初始化
|
||||
// *
|
||||
// */
|
||||
//@Service
|
||||
//public class ScanNewField {
|
||||
// @Autowired
|
||||
// PackageUtil packageUtil;
|
||||
// // 写链接(写到主库,可使用事务)
|
||||
// @Autowired
|
||||
// private MongoTemplate mongoTemplate;
|
||||
//
|
||||
// @Autowired
|
||||
// MongoMappingContext mongoMappingContext;
|
||||
//
|
||||
// @PostConstruct
|
||||
// public void scan() {
|
||||
// // 找到主程序包
|
||||
// Set<Class<?>> set = ClassUtil.scanPackage(packageUtil.getMainPackage());
|
||||
// for (Class<?> clazz : set) {
|
||||
// IgnoreDocument ignoreDocument = clazz.getAnnotation(IgnoreDocument.class);
|
||||
// if (ignoreDocument != null) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// Document document = clazz.getAnnotation(Document.class);
|
||||
// if (document == null) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// // 创建表
|
||||
// if (!mongoTemplate.collectionExists(clazz)) {
|
||||
// mongoTemplate.createCollection(clazz);
|
||||
// System.out.println("创建了" + clazz.getSimpleName() + "表");
|
||||
// }
|
||||
//
|
||||
// // 创建索引
|
||||
// IndexOperations indexOps = mongoTemplate.indexOps(clazz);
|
||||
// IndexResolver resolver = new MongoPersistentEntityIndexResolver(mongoMappingContext);
|
||||
// resolver.resolveIndexFor(clazz).forEach(indexOps::ensureIndex);
|
||||
//
|
||||
// Field[] fields = ReflectUtil.getFields(clazz);
|
||||
// for (Field field : fields) {
|
||||
// // 获取注解
|
||||
// if (field.isAnnotationPresent(InitValue.class)) {
|
||||
// InitValue initValue = field.getAnnotation(InitValue.class);
|
||||
// if (initValue.value() != null) {
|
||||
//
|
||||
// // 更新表默认值
|
||||
// Query query = new Query();
|
||||
// query.addCriteria(Criteria.where(field.getName()).is(null));
|
||||
//
|
||||
// Long count = mongoTemplate.count(query, clazz);
|
||||
// if (count > 0) {
|
||||
// Object value = null;
|
||||
// Class<?> type = field.getType();
|
||||
//
|
||||
// if (type.equals(String.class)) {
|
||||
// value = initValue.value();
|
||||
// }
|
||||
// if (type.equals(Short.class)) {
|
||||
// value = Short.parseShort(initValue.value());
|
||||
// }
|
||||
// if (type.equals(Integer.class)) {
|
||||
// value = Integer.parseInt(initValue.value());
|
||||
// }
|
||||
// if (type.equals(Long.class)) {
|
||||
// value = Long.parseLong(initValue.value());
|
||||
// }
|
||||
// if (type.equals(Float.class)) {
|
||||
// value = Float.parseFloat(initValue.value());
|
||||
// }
|
||||
// if (type.equals(Double.class)) {
|
||||
// value = Double.parseDouble(initValue.value());
|
||||
// }
|
||||
// if (type.equals(Boolean.class)) {
|
||||
// value = Boolean.parseBoolean(initValue.value());
|
||||
// }
|
||||
//
|
||||
// Update update = new Update().set(field.getName(), value);
|
||||
// UpdateResult updateResult = mongoTemplate.updateMulti(query, update, clazz);
|
||||
//
|
||||
// System.out.println(clazz.getSimpleName() + "表更新了" + updateResult.getModifiedCount() + "条默认值");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//}
|
||||
|
@ -1,137 +1,137 @@
|
||||
package com.ruoyi.biemo.mongodb.utils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.ruoyi.biemo.mongodb.bean.Page;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.ClassUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.ZipUtil;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
|
||||
/**
|
||||
* 数据库导入导出工具
|
||||
*
|
||||
*/
|
||||
@Service
|
||||
public class ImportExportUtil {
|
||||
// 写链接(写到主库,可使用事务)
|
||||
@Autowired
|
||||
private MongoTemplate mongoTemplate;
|
||||
|
||||
@Autowired
|
||||
private MongoHelper mongoHelper;
|
||||
|
||||
@Autowired
|
||||
PackageUtil packageUtil;
|
||||
public void exportDb(String path) {
|
||||
path = path.replace(".zip", "");
|
||||
FileUtil.del(path);
|
||||
FileUtil.del(path + ".zip");
|
||||
try {
|
||||
|
||||
// 找到主程序包
|
||||
Set<Class<?>> set = ClassUtil.scanPackage(packageUtil.getMainPackage());
|
||||
Page page = new Page();
|
||||
page.setLimit(1000);
|
||||
|
||||
for (Class<?> clazz : set) {
|
||||
Document document = clazz.getAnnotation(Document.class);
|
||||
if (document == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
page.setCurr(1);
|
||||
while (true) {
|
||||
page = mongoHelper.findPage(page, clazz);
|
||||
if (page.getList().size() == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
List<String> lines = new ArrayList<String>();
|
||||
for (Object object : page.getList()) {
|
||||
lines.add(JSONUtil.toJsonStr(object));
|
||||
}
|
||||
FileUtil.appendLines(lines, path + File.separator + clazz.getSimpleName() + ".json", "UTF-8");
|
||||
System.out.println(clazz.getSimpleName() + "表导出了" + page.getList().size() + "条数据");
|
||||
page.setCurr(page.getCurr() + 1);
|
||||
}
|
||||
}
|
||||
ZipUtil.zip(path);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
FileUtil.del(path + ".zip");
|
||||
}
|
||||
|
||||
FileUtil.del(path);
|
||||
}
|
||||
|
||||
public void importDb(String path) {
|
||||
if (!FileUtil.exist(path)) {
|
||||
System.out.println(path + "文件不存在");
|
||||
return;
|
||||
}
|
||||
BufferedReader reader = null;
|
||||
|
||||
path = path.replace(".zip", "");
|
||||
FileUtil.del(path);
|
||||
ZipUtil.unzip(path + ".zip");
|
||||
try {
|
||||
|
||||
// 找到主程序包
|
||||
Set<Class<?>> set = ClassUtil.scanPackage(packageUtil.getMainPackage());
|
||||
for (Class<?> clazz : set) {
|
||||
Document document = clazz.getAnnotation(Document.class);
|
||||
if (document == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
File file = new File(path + File.separator + clazz.getSimpleName() + ".json");
|
||||
if (file.exists()) {
|
||||
mongoTemplate.dropCollection(clazz);
|
||||
|
||||
reader = FileUtil.getReader(file, "UTF-8");
|
||||
List<Object> list = new ArrayList<Object>();
|
||||
while (true) {
|
||||
String json = reader.readLine();
|
||||
if (StrUtil.isEmpty(json)) {
|
||||
mongoTemplate.insertAll(list);
|
||||
System.out.println(clazz.getSimpleName() + "表导入了" + list.size() + "条数据");
|
||||
list.clear();
|
||||
break;
|
||||
}
|
||||
list.add(JSONUtil.toBean(json, clazz));
|
||||
if (list.size() == 1000) {
|
||||
mongoTemplate.insertAll(list);
|
||||
System.out.println(clazz.getSimpleName() + "表导入了" + list.size() + "条数据");
|
||||
list.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
IoUtil.close(reader);
|
||||
}
|
||||
FileUtil.del(path);
|
||||
}
|
||||
}
|
||||
//package com.ruoyi.biemo.mongodb.utils;
|
||||
//
|
||||
//import java.io.BufferedReader;
|
||||
//import java.io.File;
|
||||
//import java.util.ArrayList;
|
||||
//import java.util.List;
|
||||
//import java.util.Map;
|
||||
//import java.util.Set;
|
||||
//
|
||||
//import com.ruoyi.biemo.mongodb.bean.Page;
|
||||
//import org.springframework.beans.factory.annotation.Autowired;
|
||||
//import org.springframework.beans.factory.annotation.Value;
|
||||
//import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
//import org.springframework.context.ApplicationContext;
|
||||
//import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
//import org.springframework.data.mongodb.core.mapping.Document;
|
||||
//import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
//import org.springframework.stereotype.Service;
|
||||
//
|
||||
//import cn.hutool.core.io.FileUtil;
|
||||
//import cn.hutool.core.io.IoUtil;
|
||||
//import cn.hutool.core.util.ClassUtil;
|
||||
//import cn.hutool.core.util.StrUtil;
|
||||
//import cn.hutool.core.util.ZipUtil;
|
||||
//import cn.hutool.json.JSONArray;
|
||||
//import cn.hutool.json.JSONObject;
|
||||
//import cn.hutool.json.JSONUtil;
|
||||
//
|
||||
///**
|
||||
// * 数据库导入导出工具
|
||||
// *
|
||||
// */
|
||||
//@Service
|
||||
//public class ImportExportUtil {
|
||||
// // 写链接(写到主库,可使用事务)
|
||||
// @Autowired
|
||||
// private MongoTemplate mongoTemplate;
|
||||
//
|
||||
// @Autowired
|
||||
// private MongoHelper mongoHelper;
|
||||
//
|
||||
// @Autowired
|
||||
// PackageUtil packageUtil;
|
||||
// public void exportDb(String path) {
|
||||
// path = path.replace(".zip", "");
|
||||
// FileUtil.del(path);
|
||||
// FileUtil.del(path + ".zip");
|
||||
// try {
|
||||
//
|
||||
// // 找到主程序包
|
||||
// Set<Class<?>> set = ClassUtil.scanPackage(packageUtil.getMainPackage());
|
||||
// Page page = new Page();
|
||||
// page.setLimit(1000);
|
||||
//
|
||||
// for (Class<?> clazz : set) {
|
||||
// Document document = clazz.getAnnotation(Document.class);
|
||||
// if (document == null) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// page.setCurr(1);
|
||||
// while (true) {
|
||||
// page = mongoHelper.findPage(page, clazz);
|
||||
// if (page.getList().size() == 0) {
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// List<String> lines = new ArrayList<String>();
|
||||
// for (Object object : page.getList()) {
|
||||
// lines.add(JSONUtil.toJsonStr(object));
|
||||
// }
|
||||
// FileUtil.appendLines(lines, path + File.separator + clazz.getSimpleName() + ".json", "UTF-8");
|
||||
// System.out.println(clazz.getSimpleName() + "表导出了" + page.getList().size() + "条数据");
|
||||
// page.setCurr(page.getCurr() + 1);
|
||||
// }
|
||||
// }
|
||||
// ZipUtil.zip(path);
|
||||
//
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// FileUtil.del(path + ".zip");
|
||||
// }
|
||||
//
|
||||
// FileUtil.del(path);
|
||||
// }
|
||||
//
|
||||
// public void importDb(String path) {
|
||||
// if (!FileUtil.exist(path)) {
|
||||
// System.out.println(path + "文件不存在");
|
||||
// return;
|
||||
// }
|
||||
// BufferedReader reader = null;
|
||||
//
|
||||
// path = path.replace(".zip", "");
|
||||
// FileUtil.del(path);
|
||||
// ZipUtil.unzip(path + ".zip");
|
||||
// try {
|
||||
//
|
||||
// // 找到主程序包
|
||||
// Set<Class<?>> set = ClassUtil.scanPackage(packageUtil.getMainPackage());
|
||||
// for (Class<?> clazz : set) {
|
||||
// Document document = clazz.getAnnotation(Document.class);
|
||||
// if (document == null) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// File file = new File(path + File.separator + clazz.getSimpleName() + ".json");
|
||||
// if (file.exists()) {
|
||||
// mongoTemplate.dropCollection(clazz);
|
||||
//
|
||||
// reader = FileUtil.getReader(file, "UTF-8");
|
||||
// List<Object> list = new ArrayList<Object>();
|
||||
// while (true) {
|
||||
// String json = reader.readLine();
|
||||
// if (StrUtil.isEmpty(json)) {
|
||||
// mongoTemplate.insertAll(list);
|
||||
// System.out.println(clazz.getSimpleName() + "表导入了" + list.size() + "条数据");
|
||||
// list.clear();
|
||||
// break;
|
||||
// }
|
||||
// list.add(JSONUtil.toBean(json, clazz));
|
||||
// if (list.size() == 1000) {
|
||||
// mongoTemplate.insertAll(list);
|
||||
// System.out.println(clazz.getSimpleName() + "表导入了" + list.size() + "条数据");
|
||||
// list.clear();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// } finally {
|
||||
// IoUtil.close(reader);
|
||||
// }
|
||||
// FileUtil.del(path);
|
||||
// }
|
||||
//}
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue