org.redkalex.source.mongo.MongodbDriverDataSource Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of redkale-plugins Show documentation
Show all versions of redkale-plugins Show documentation
Redkale-Plugins -- java framework
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkalex.source.mongo;
import com.mongodb.*;
import com.mongodb.bulk.BulkWriteResult;
import com.mongodb.client.model.*;
import com.mongodb.client.result.*;
import com.mongodb.reactivestreams.client.*;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.logging.*;
import java.util.stream.Stream;
import org.bson.*;
import org.bson.codecs.configuration.*;
import org.bson.codecs.pojo.ClassModelBuilder;
import org.bson.codecs.pojo.Convention;
import org.bson.codecs.pojo.PojoCodecProvider;
import org.bson.conversions.Bson;
import org.reactivestreams.*;
import org.redkale.annotation.AutoLoad;
import org.redkale.annotation.ResourceChanged;
import org.redkale.annotation.ResourceType;
import org.redkale.inject.ResourceEvent;
import org.redkale.service.Local;
import org.redkale.source.*;
import static org.redkale.source.DataSources.*;
import org.redkale.util.*;
/**
* Mongodb版的DataSource实现
* 注意: datasource.url 需要指定为 mongodb:, 例如:mongodb://127.0.0.1:5005
*
* @author zhangjx
*/
@Local
@AutoLoad(false)
@SuppressWarnings("unchecked")
@ResourceType(DataSource.class)
@SourceType(MongodbDataSource.class)
public class MongodbDriverDataSource extends MongodbDataSource
implements java.util.function.Function {
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
protected String readdb;
protected String writedb;
protected boolean cacheForbidden;
protected Properties readConfProps;
protected Properties writeConfProps;
protected MongoClient readMongoClient;
protected MongoDatabase readMongoDatabase;
protected MongoClient writeMongoClient;
protected MongoDatabase writeMongoDatabase;
@Override
public void init(AnyValue conf) {
super.init(conf);
this.name = conf.getValue("name", "");
if (conf.getAnyValue("read") == null) { // 没有读写分离
Properties rwConf = new Properties();
conf.forEach((k, v) -> rwConf.put(k, decryptProperty(k, v)));
initProperties(rwConf);
this.readConfProps = rwConf;
this.writeConfProps = rwConf;
} else { // 读写分离
Properties readConf = new Properties();
Properties writeConf = new Properties();
conf.getAnyValue("read").forEach((k, v) -> readConf.put(k, decryptProperty(k, v)));
conf.getAnyValue("write").forEach((k, v) -> writeConf.put(k, decryptProperty(k, v)));
initProperties(readConf);
initProperties(writeConf);
this.readConfProps = readConf;
this.writeConfProps = writeConf;
}
this.cacheForbidden = "NONE".equalsIgnoreCase(readConfProps.getProperty(DATA_SOURCE_CACHEMODE));
this.readMongoClient = createMongoClient(true, this.readConfProps);
if (this.readConfProps == this.writeConfProps) {
this.writeMongoClient = this.readMongoClient;
this.writedb = this.readdb;
} else {
this.writeMongoClient = createMongoClient(false, this.writeConfProps);
}
}
@Override
@ResourceChanged
public void onResourceChange(ResourceEvent[] events) {
if (Utility.isEmpty(events)) {
return;
}
// 不支持读写分离模式的动态切换
if (readConfProps == writeConfProps
&& (events[0].name().startsWith("read.") || events[0].name().startsWith("write."))) {
throw new SourceException(
"DataSource(name=" + resourceName() + ") not support to change to read/write separation mode");
}
if (readConfProps != writeConfProps
&& (!events[0].name().startsWith("read.") && !events[0].name().startsWith("write."))) {
throw new SourceException(
"DataSource(name=" + resourceName() + ") not support to change to non read/write separation mode");
}
StringBuilder sb = new StringBuilder();
if (readConfProps == writeConfProps) {
List allEvents = new ArrayList<>();
Properties newProps = new Properties();
newProps.putAll(this.readConfProps);
for (ResourceEvent event : events) { // 可能需要解密
String newValue = decryptProperty(event.name(), event.newValue().toString());
allEvents.add(ResourceEvent.create(event.name(), newValue, event.oldValue()));
newProps.put(event.name(), newValue);
sb.append("DataSource(name=")
.append(resourceName())
.append(") change '")
.append(event.name())
.append("' to '")
.append(event.coverNewValue())
.append("'\r\n");
}
{ // 更新MongoClient
MongoClient oldClient = this.readMongoClient;
this.readMongoClient = createMongoClient(true, newProps);
this.writeMongoClient = this.readMongoClient;
this.writedb = this.readdb;
if (oldClient != null) {
oldClient.close();
}
}
for (ResourceEvent event : allEvents) {
this.readConfProps.put(event.name(), event.newValue());
}
} else {
List readEvents = new ArrayList<>();
List writeEvents = new ArrayList<>();
Properties newReadProps = new Properties();
newReadProps.putAll(this.readConfProps);
Properties newWriteProps = new Properties();
newWriteProps.putAll(this.writeConfProps);
for (ResourceEvent event : events) {
if (event.name().startsWith("read.")) {
String newName = event.name().substring("read.".length());
String newValue =
decryptProperty(event.name(), event.newValue().toString());
readEvents.add(ResourceEvent.create(newName, newValue, event.oldValue()));
newReadProps.put(event.name(), newValue);
} else {
String newName = event.name().substring("write.".length());
String newValue =
decryptProperty(event.name(), event.newValue().toString());
writeEvents.add(ResourceEvent.create(newName, newValue, event.oldValue()));
newWriteProps.put(event.name(), newValue);
}
sb.append("DataSource(name=")
.append(resourceName())
.append(") change '")
.append(event.name())
.append("' to '")
.append(event.coverNewValue())
.append("'\r\n");
}
if (!readEvents.isEmpty()) { // 更新Read MongoClient
MongoClient oldClient = this.readMongoClient;
this.readMongoClient = createMongoClient(true, newReadProps);
if (oldClient != null) {
oldClient.close();
}
}
if (!writeEvents.isEmpty()) { // 更新Write MongoClient
MongoClient oldClient = this.writeMongoClient;
this.writeMongoClient = createMongoClient(false, newReadProps);
if (oldClient != null) {
oldClient.close();
}
}
// 更新Properties
if (!readEvents.isEmpty()) {
for (ResourceEvent event : readEvents) {
this.readConfProps.put(event.name(), event.newValue());
}
}
if (!writeEvents.isEmpty()) {
for (ResourceEvent event : writeEvents) {
this.writeConfProps.put(event.name(), event.newValue());
}
}
}
if (sb.length() > 0) {
logger.log(Level.INFO, sb.toString());
}
}
protected MongoClient createMongoClient(boolean read, Properties prop) {
int maxconns = Math.max(1, Integer.decode(prop.getProperty(DATA_SOURCE_MAXCONNS, "" + Utility.cpus())));
// 实体类自动映射
MongoClientSettings.Builder settingBuilder = MongoClientSettings.builder();
String url = prop.getProperty(DATA_SOURCE_URL);
if (url.indexOf('?') < 0) {
url += "?maxpoolsize=" + maxconns;
} else if (!url.contains("maxpoolsize=")) {
url += "&maxpoolsize=" + maxconns;
}
ConnectionString cc = new ConnectionString(url);
if (read && readdb == null) {
this.readdb = cc.getDatabase();
}
if (!read && writedb == null) {
this.writedb = cc.getDatabase();
}
settingBuilder.applyConnectionString(cc);
CodecRegistry registry = CodecRegistries.fromRegistries(
MongoClientSettings.getDefaultCodecRegistry(),
CodecRegistries.fromProviders(PojoCodecProvider.builder()
.automatic(true)
.conventions(List.of(createConvention()))
.build()));
settingBuilder.codecRegistry(registry);
return MongoClients.create(settingBuilder.build());
}
protected Convention createConvention() {
return new MongoConvention();
}
protected class MongoConvention implements Convention {
@Override
public void apply(ClassModelBuilder> builder) {
EntityInfo info = getEntityInfo(builder.getType());
if (info != null) {
Set validCols = new HashSet<>();
for (EntityColumn col : info.getInsertColumns()) {
validCols.add(col.getField());
}
for (EntityColumn col : info.getUpdateColumns()) {
validCols.add(col.getField());
}
Set fields = new HashSet<>();
builder.getPropertyModelBuilders().forEach(p -> {
fields.add(p.getName());
});
for (String field : fields) {
if (!validCols.contains(field)) {
builder.removeProperty(field);
}
}
}
}
}
// 解密可能存在的加密字段, 可重载
protected String decryptProperty(String key, String value) {
return value;
}
// 可重载
protected void initProperties(Properties props) {
// do nothing
}
@Override
public void destroy(AnyValue config) {
super.destroy(config);
if (this.readMongoClient != null) {
this.readMongoClient.close();
}
if (this.writeMongoClient != null && this.readMongoClient != this.writeMongoClient) {
this.writeMongoClient.close();
}
}
@Override
public String toString() {
if (readConfProps == null) {
return getClass().getSimpleName() + "{}"; // compileMode模式下会为null
}
return getClass().getSimpleName() + "{url=" + readConfProps.getProperty(DATA_SOURCE_URL) + "}";
}
@Local
@Override
public EntityInfo apply(Class t) {
return loadEntityInfo(t);
}
@Local
protected EntityInfo loadEntityInfo(Class clazz) {
return loadEntityInfo(clazz, this.cacheForbidden, readConfProps, null);
}
/**
* 加载指定类的EntityInfo
*
* @param 泛型
* @param clazz 类
* @return EntityInfo
*/
@Local
@Override
protected EntityInfo getEntityInfo(Class clazz) {
return super.getEntityInfo(clazz);
}
@Override
@Local
public void compile(Class clazz) {
EntityInfo.compile(clazz, this);
}
@Local
public MongoClient getReadMongoClient() {
return this.readMongoClient;
}
@Local
@Override
public MongoDatabase getReadMongoDatabase() {
if (this.readMongoDatabase != null) {
return this.readMongoDatabase;
}
this.readMongoDatabase = this.readMongoClient.getDatabase(readdb);
return this.readMongoDatabase;
}
@Local
@Override
public MongoCollection getReadMongoCollection(EntityInfo info) {
return this.getReadMongoDatabase().getCollection(info.getTable((T) null), info.getType());
}
@Local
@Override
public MongoCollection getReadMongoDocumentCollection(EntityInfo info) {
return this.getReadMongoDatabase().getCollection(info.getTable((T) null));
}
@Local
@Override
public MongoClient getWriteMongoClient() {
return this.writeMongoClient;
}
@Local
@Override
public MongoDatabase getWriteMongoDatabase() {
if (this.writeMongoDatabase != null) {
return this.writeMongoDatabase;
}
this.writeMongoDatabase = this.readMongoClient.getDatabase(writedb);
return this.writeMongoDatabase;
}
@Local
@Override
public MongoCollection getWriteMongoCollection(EntityInfo info) {
return this.getWriteMongoDatabase().getCollection(info.getTable((T) null), info.getType());
}
@Local
@Override
public MongoCollection getWriteMongoDocumentCollection(EntityInfo info) {
return this.getWriteMongoDatabase().getCollection(info.getTable((T) null));
}
// 可重载此方法以支持特殊数据类型, 例如:Date、Time
protected Object formatFilterValue(EntityInfo info, Serializable val) {
return val;
}
@Local
@Override
public Bson createSortBson(Flipper flipper) {
if (flipper == null) {
return null;
}
if (flipper.getSort() == null || flipper.getSort().trim().isEmpty()) {
return null;
}
List sorts = new ArrayList<>();
for (String item : flipper.getSort().trim().split(",")) {
item = item.trim();
if (item.isEmpty()) {
continue;
}
String[] sub = item.split("\\s+");
if (sub.length < 2 || sub[1].equalsIgnoreCase("ASC")) {
sorts.add(Sorts.ascending(sub[0]));
} else {
sorts.add(Sorts.descending(sub[0]));
}
}
if (sorts.isEmpty()) {
return null;
}
if (sorts.size() == 1) {
return sorts.get(0);
}
return Sorts.orderBy(sorts);
}
@Local
@Override
public List createUpdateBson(EntityInfo info, ColumnValue... values) {
List items = new ArrayList<>(values.length);
for (ColumnValue colval : values) {
Bson bson = createUpdateBson(info, colval);
if (bson != null) {
items.add(bson);
}
}
return items;
}
@Local
@Override
public Bson createUpdateBson(EntityInfo info, ColumnValue colval) {
String key = colval.getColumn();
ColumnNode val = colval.getValue();
switch (colval.getExpress()) {
case SET: // col = val
return new BsonDocument("$set", new BsonDocument(key, formatToBsonValue(val)));
case INC: // col = col + val
return new BsonDocument("$inc", new BsonDocument(key, formatToBsonValue(val)));
case DEC: // col = col - val
return new BsonDocument("$inc", new BsonDocument(key, formatToBsonValue(val, true)));
case MUL: // col = col * val
return new BsonDocument("$mul", new BsonDocument(key, formatToBsonValue(val)));
case DIV: // col = col / val
return new BsonDocument(
"$set",
new BsonDocument(
key,
new BsonDocument(
"$divide",
new BsonArray(List.of(new BsonString("$" + key), formatToBsonValue(val))))));
case MOD: // col = col % val
return new BsonDocument(
"$set",
new BsonDocument(
key,
new BsonDocument(
"$mod",
new BsonArray(List.of(new BsonString("$" + key), formatToBsonValue(val))))));
case AND: // col = col & val
return new BsonDocument("$bit", new BsonDocument(key, new BsonDocument("and", formatToBsonValue(val))));
case ORR: // col = col | val
return new BsonDocument("$bit", new BsonDocument(key, new BsonDocument("or", formatToBsonValue(val))));
}
return null;
}
@Local
@Override
public BsonField createBsonField(FilterFunc func, String fieldName, Serializable column) {
BsonField bf = null;
if (column == null || FilterFuncColumn.COLUMN_NULL.equals(column)) {
column = "_id";
}
if (fieldName == null) {
fieldName = column.toString();
}
if (func == FilterFunc.COUNT) {
bf = Accumulators.sum(fieldName, 1);
} else if (func == FilterFunc.AVG) {
bf = Accumulators.avg(fieldName, "$" + column);
} else if (func == FilterFunc.MAX) {
bf = Accumulators.max(fieldName, "$" + column);
} else if (func == FilterFunc.MIN) {
bf = Accumulators.min(fieldName, "$" + column);
} else if (func == FilterFunc.SUM) {
bf = Accumulators.sum(fieldName, "$" + column);
} else {
throw new UnsupportedOperationException(
FilterFunc.class.getSimpleName() + " " + func + " not supported yet.");
}
return bf;
}
@Local
@Override
public Bson createFilterBson(EntityInfo info, FilterNode node) {
return createFilter(info, node, false);
}
protected BsonValue formatToBsonValue(ColumnNode node) {
return formatToBsonValue(node, false);
}
protected BsonValue formatToBsonValue(ColumnNode node, boolean dec) {
if (node == null) {
return null;
} else if (node instanceof ColumnNumberNode) {
Number val = ((ColumnNumberNode) node).getValue();
BsonNumber bn;
if (val instanceof Number) {
if (val instanceof Float || val instanceof Double) {
double d = val.doubleValue();
if (dec) {
d = -d;
}
bn = new BsonDouble(d);
} else if (val instanceof Long) {
long d = val.longValue();
if (dec) {
d = -d;
}
bn = new BsonInt64(d);
} else {
int d = val.intValue();
if (dec) {
d = -d;
}
bn = new BsonInt32(d);
}
return bn;
}
} else if (node instanceof ColumnNameNode) {
return new BsonString("$" + ((ColumnNameNode) node).getColumn());
} else if (node instanceof ColumnStringNode) {
return new BsonString(((ColumnStringNode) node).getValue());
} else if (node instanceof ColumnBytesNode) {
return new BsonBinary(((ColumnBytesNode) node).getValue());
}
throw new IllegalArgumentException("Not supported ColumnValue " + node);
}
private Bson createFilter(EntityInfo info, FilterNode node, boolean sub) {
if (node == null) {
return null;
}
Bson bson = createFilterElement(info, node);
if (bson == null && node.getNodes() == null) {
return null;
}
List items = new ArrayList<>();
if (bson != null) {
items.add(bson);
}
if (node.getNodes() != null) {
for (FilterNode item : node.getNodes()) {
Bson s = createFilter(info, item, true);
if (s == null) {
continue;
}
items.add(s);
}
}
if (items.isEmpty()) {
return null;
}
if (items.size() == 1) {
return items.get(0);
}
return node.isOr()
? Filters.or(items.toArray(new Bson[items.size()]))
: Filters.and(items.toArray(new Bson[items.size()]));
}
private Bson createFilterElement(EntityInfo info, FilterNode node) {
if (node == null || node.getColumn() == null || node.getColumn().charAt(0) == '#') {
return null;
}
if (node instanceof FilterJoinNode) {
throw new IllegalArgumentException("Not supported " + FilterJoinNode.class.getSimpleName());
}
switch (node.getExpress()) {
case EQ: {
return Filters.eq(node.getColumn(), formatFilterValue(info, node.getValue()));
}
case IG_EQ: {
return Filters.regex(node.getColumn(), "^" + node.getValue() + "$", "i");
}
case NE:
case IG_NE: {
return Filters.not(Filters.eq(node.getColumn(), node.getValue()));
}
case GT: {
return Filters.gt(node.getColumn(), formatFilterValue(info, node.getValue()));
}
case LT: {
return Filters.lt(node.getColumn(), formatFilterValue(info, node.getValue()));
}
case GE: {
return Filters.gte(node.getColumn(), formatFilterValue(info, node.getValue()));
}
case LE: {
return Filters.lte(node.getColumn(), formatFilterValue(info, node.getValue()));
}
case LIKE: {
return Filters.regex(node.getColumn(), "" + node.getValue());
}
case IG_LIKE: {
return Filters.regex(node.getColumn(), "" + node.getValue(), "i");
}
case NOT_LIKE: {
return Filters.not(Filters.regex(node.getColumn(), "" + node.getValue()));
}
case IG_NOT_LIKE: {
return Filters.not(Filters.regex(node.getColumn(), "" + node.getValue(), "i"));
}
case IN: {
return Filters.in(
node.getColumn(),
node.getValue() instanceof Collection
? (Collection) node.getValue()
: List.of((Object[]) node.getValue()));
}
case NOT_IN: {
return Filters.not(Filters.in(
node.getColumn(),
node.getValue() instanceof Collection
? (Collection) node.getValue()
: (Object[]) node.getValue()));
}
case BETWEEN: {
Range range = (Range) node.getValue();
if (range.getMax() != null && range.getMax().compareTo(range.getMin()) > 0) {
return Filters.and(
Filters.gte(node.getColumn(), range.getMin()),
Filters.lte(node.getColumn(), range.getMax()));
} else {
return Filters.gte(node.getColumn(), range.getMin());
}
}
case NOT_BETWEEN: {
Range range = (Range) node.getValue();
Bson bson;
if (range.getMax() != null && range.getMax().compareTo(range.getMin()) > 0) {
bson = Filters.and(
Filters.gte(node.getColumn(), range.getMin()),
Filters.lte(node.getColumn(), range.getMax()));
} else {
bson = Filters.gte(node.getColumn(), range.getMin());
}
return Filters.not(bson);
}
default:
throw new IllegalArgumentException("Not supported FilterNode " + node);
}
}
@Override
public String getType() {
return "mongodb";
}
@Override
public int insert(T... entitys) {
if (entitys.length == 0) {
return 0;
}
checkEntity("insert", entitys);
return insertAsync(entitys).join();
}
@Override
public CompletableFuture insertAsync(T... entitys) {
if (entitys.length == 0) {
return CompletableFuture.completedFuture(0);
}
checkEntity("insert", entitys);
EntityInfo info = loadEntityInfo((Class) entitys[0].getClass());
MongoCollection collection = getWriteMongoCollection(info);
ReatorFuture future = new ReatorFuture<>();
collection.insertMany(List.of(entitys)).subscribe(future);
return future.thenApply(v -> v.getInsertedIds().size());
}
@Override
public int delete(T... entitys) {
if (entitys.length == 0) {
return 0;
}
checkEntity("delete", entitys);
return deleteAsync(entitys).join();
}
@Override
public CompletableFuture deleteAsync(T... entitys) {
if (entitys.length == 0) {
return CompletableFuture.completedFuture(0);
}
checkEntity("delete", entitys);
EntityInfo info = loadEntityInfo((Class) entitys[0].getClass());
MongoCollection collection = getWriteMongoCollection(info);
Attribute primary = info.getPrimary();
Object[] ids = new Object[entitys.length];
for (int i = 0; i < entitys.length; i++) {
ids[i] = primary.get(entitys[i]);
}
ReatorFuture future = new ReatorFuture<>();
collection.deleteMany(Filters.in(primary.field(), ids)).subscribe(future);
return future.thenApply(v -> (int) v.getDeletedCount());
}
@Override
public int delete(Class clazz, Serializable... pks) {
if (pks.length == 0) {
return 0;
}
return deleteAsync(clazz, pks).join();
}
@Override
public CompletableFuture deleteAsync(Class clazz, Serializable... pks) {
if (pks.length == 0) {
return CompletableFuture.completedFuture(0);
}
EntityInfo info = loadEntityInfo(clazz);
MongoCollection collection = getWriteMongoCollection(info);
ReatorFuture future = new ReatorFuture<>();
collection.deleteMany(Filters.in(info.getPrimaryField(), pks)).subscribe(future); // deleteOne bug?
return future.thenApply(v -> (int) v.getDeletedCount());
}
@Override
public int delete(Class clazz, Flipper flipper, FilterNode node) {
return deleteAsync(clazz, flipper, node).join();
}
@Override
public CompletableFuture deleteAsync(Class clazz, Flipper flipper, FilterNode node) {
if (flipper != null) {
return CompletableFuture.failedFuture(new RuntimeException("delete on Flipper not supported yet."));
}
EntityInfo info = loadEntityInfo(clazz);
MongoCollection collection = getWriteMongoCollection(info);
Bson filter = createFilterBson(info, node);
ReatorFuture future = new ReatorFuture<>();
collection.deleteMany(filter).subscribe(future);
return future.thenApply(v -> (int) v.getDeletedCount());
}
@Override
public int clearTable(Class clazz, FilterNode node) {
return clearTableAsync(clazz, node).join();
}
@Override
public CompletableFuture clearTableAsync(Class clazz, FilterNode node) {
EntityInfo info = loadEntityInfo(clazz);
MongoCollection collection = getWriteMongoCollection(info);
ReatorFuture future = new ReatorFuture<>();
collection.deleteMany(new BsonDocument()).subscribe(future);
return future.thenApply(v -> (int) v.getDeletedCount());
}
@Override
public int createTable(Class clazz, Serializable pk) {
return createTableAsync(clazz, pk).join();
}
@Override
public CompletableFuture createTableAsync(Class clazz, Serializable pk) {
return CompletableFuture.completedFuture(0);
}
@Override
public int dropTable(Class clazz, FilterNode node) {
return dropTableAsync(clazz, node).join();
}
@Override
public CompletableFuture dropTableAsync(Class clazz, FilterNode node) {
EntityInfo info = loadEntityInfo(clazz);
MongoCollection collection = getWriteMongoCollection(info);
ReatorFuture future = new ReatorFuture<>();
collection.drop().subscribe(future);
return future.thenApply(v -> 1);
}
@Override
public int update(T... entitys) {
return updateAsync(entitys).join();
}
@Override
public CompletableFuture updateAsync(T... entitys) {
if (entitys.length == 0) {
return CompletableFuture.completedFuture(0);
}
checkEntity("update", entitys);
EntityInfo info = loadEntityInfo((Class) entitys[0].getClass());
MongoCollection collection = getWriteMongoCollection(info);
Attribute primary = info.getPrimary();
List> list = new ArrayList<>();
for (T entity : entitys) {
Serializable pk = primary.get(entity);
List items = new ArrayList<>();
for (Attribute attr : info.getUpdateAttributes()) {
items.add(Updates.set(attr.field(), attr.get(entity)));
}
list.add(new UpdateOneModel(Filters.eq(info.getPrimaryField(), pk), Updates.combine(items)));
}
ReatorFuture future = new ReatorFuture<>();
collection.bulkWrite(list).subscribe(future);
return future.thenApply(v -> (int) v.getModifiedCount());
}
@Override
public int updateColumn(Class clazz, Serializable pk, String column, Serializable value) {
return updateColumnAsync(clazz, pk, column, value).join();
}
@Override
public CompletableFuture updateColumnAsync(
Class clazz, Serializable pk, String column, Serializable value) {
EntityInfo info = loadEntityInfo(clazz);
MongoCollection collection = getWriteMongoCollection(info);
ReatorFuture future = new ReatorFuture<>();
collection
.updateOne(Filters.eq(info.getPrimaryField(), pk), Updates.set(column, value))
.subscribe(future);
return future.thenApply(v -> (int) v.getModifiedCount());
}
@Override
public int updateColumn(Class clazz, String column, Serializable value, FilterNode node) {
return updateColumnAsync(clazz, column, value, node).join();
}
@Override
public CompletableFuture updateColumnAsync(
Class clazz, String column, Serializable value, FilterNode node) {
EntityInfo info = loadEntityInfo(clazz);
MongoCollection collection = getWriteMongoCollection(info);
ReatorFuture future = new ReatorFuture<>();
UpdateOptions options = null;
collection
.updateMany(createFilterBson(info, node), Updates.set(column, value), options)
.subscribe(future);
return future.thenApply(v -> (int) v.getModifiedCount());
}
@Override
public int updateColumn(Class clazz, Serializable pk, ColumnValue... values) {
return updateColumnAsync(clazz, pk, values).join();
}
@Override
public CompletableFuture updateColumnAsync(Class clazz, Serializable pk, ColumnValue... values) {
if (values.length == 0) {
return CompletableFuture.completedFuture(0);
}
EntityInfo info = loadEntityInfo(clazz);
List items = createUpdateBson(info, values);
MongoCollection collection = getWriteMongoCollection(info);
ReatorFuture future = new ReatorFuture<>();
collection
.updateOne(Filters.eq(info.getPrimaryField(), pk), Updates.combine(items))
.subscribe(future);
return future.thenApply(v -> (int) v.getModifiedCount());
}
@Override
public int updateColumn(Class clazz, FilterNode node, Flipper flipper, ColumnValue... values) {
return updateColumnAsync(clazz, node, flipper, values).join();
}
@Override
public CompletableFuture updateColumnAsync(
Class clazz, FilterNode node, Flipper flipper, ColumnValue... values) {
if (values.length == 0) {
return CompletableFuture.completedFuture(0);
}
if (flipper != null) {
return CompletableFuture.failedFuture(new RuntimeException("updateColumn on Flipper not supported yet."));
}
EntityInfo info = loadEntityInfo(clazz);
List items = createUpdateBson(info, values);
if (items == null || items.isEmpty()) {
return CompletableFuture.completedFuture(0);
}
MongoCollection collection = getWriteMongoCollection(info);
ReatorFuture future = new ReatorFuture<>();
collection
.updateMany(createFilterBson(info, node), Updates.combine(items))
.subscribe(future);
return future.thenApply(v -> (int) v.getModifiedCount());
}
@Override
public int updateColumn(T entity, FilterNode node, SelectColumn selects) {
return updateColumnAsync(entity, node, selects).join();
}
@Override
public CompletableFuture updateColumnAsync(T entity, FilterNode node, SelectColumn selects) {
EntityInfo info = loadEntityInfo((Class) entity.getClass());
List items = new ArrayList<>();
for (Attribute attr : info.getUpdateAttributes()) {
if (selects == null || selects.test(attr.field())) {
items.add(Updates.set(attr.field(), attr.get(entity)));
}
}
if (items.isEmpty()) {
return CompletableFuture.completedFuture(0);
}
MongoCollection collection = getWriteMongoCollection(info);
ReatorFuture future = new ReatorFuture<>();
Bson filter = node == null
? Filters.eq(info.getPrimaryField(), info.getPrimary().get(entity))
: createFilterBson(info, node);
collection.updateMany(filter, Updates.combine(items)).subscribe(future);
return future.thenApply(v -> (int) v.getModifiedCount());
}
@Override
public Number getNumberResult(Class entityClass, FilterFunc func, Number defVal, String column, FilterNode node) {
return getNumberResultAsync(entityClass, func, defVal, column, node).join();
}
@Override
public CompletableFuture getNumberResultAsync(
Class entityClass, FilterFunc func, Number defVal, String column, FilterNode node) {
EntityInfo info = loadEntityInfo(entityClass);
MongoCollection collection = getReadMongoDocumentCollection(info);
Bson filter = createFilterBson(info, node);
if (func == FilterFunc.COUNT) {
Publisher publisher =
filter == null ? collection.countDocuments() : collection.countDocuments(filter);
ReatorFuture future = new ReatorFuture<>();
publisher.subscribe(future);
return future.thenApply(v -> v.intValue());
} else if (func == FilterFunc.DISTINCTCOUNT) {
String key = column == null ? info.getPrimaryField() : column;
ReatorFuture future = new ReatorFuture<>();
List items = new ArrayList<>();
if (filter != null) {
items.add(Aggregates.match(filter));
}
// [{$group:{_id:"$fieldName"}}, {$group:{_id:1, count:{$sum:1}}}]
items.add(Aggregates.group("$" + key));
items.add(Aggregates.group(1, Accumulators.sum("count", 1)));
collection.aggregate(items).subscribe(future);
return future.thenApply(v -> v == null ? defVal : (Number) v.get("count"));
} else {
ReatorFuture future = new ReatorFuture<>();
List items = new ArrayList<>();
if (filter != null) {
items.add(Aggregates.match(filter));
}
BsonField bf = createBsonField(func, column, column);
items.add(Aggregates.group(null, bf));
// System.println(items.get(0).toBsonDocument().toJson(JsonWriterSettings.builder().indent(true).build()));
collection.aggregate(items).subscribe(future);
return future.thenApply(v -> v == null ? defVal : (Number) v.get(column));
}
}
@Override
public Map getNumberMap(
Class entityClass, FilterNode node, FilterFuncColumn... columns) {
return (Map) getNumberMapAsync(entityClass, node, columns).join();
}
@Override // 等价SQL: SELECT FUNC1{column1}, FUNC2{column2}, ... FROM {table}
public CompletableFuture