package io.ebeanservice.docstore.api.support;
import io.ebean.FetchPath;
import io.ebean.Query;
import io.ebean.annotation.DocStore;
import io.ebean.annotation.DocStoreMode;
import io.ebean.plugin.BeanType;
import io.ebean.text.PathProperties;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.server.core.PersistRequest;
import io.ebeaninternal.server.core.PersistRequestBean;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import io.ebeanservice.docstore.api.DocStoreBeanAdapter;
import io.ebeanservice.docstore.api.DocStoreUpdateContext;
import io.ebeanservice.docstore.api.DocStoreUpdates;
import io.ebeanservice.docstore.api.mapping.DocMappingBuilder;
import io.ebeanservice.docstore.api.mapping.DocumentMapping;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
public abstract class DocStoreBeanBaseAdapter<T> implements DocStoreBeanAdapter<T> {
protected final SpiEbeanServer server;
protected final BeanDescriptor<T> desc;
protected final boolean mapped;
protected final String queueId;
protected final String indexType;
protected final String indexName;
private final DocStore docStore;
protected final DocStoreMode insert;
protected DocStoreMode update;
protected final DocStoreMode delete;
protected final List<DocStoreEmbeddedInvalidation> embeddedInvalidation = new ArrayList<>();
protected final PathProperties pathProps;
protected Map<String, String> sortableMap;
protected DocStructure docStructure;
protected DocumentMapping documentMapping;
private boolean registerPaths;
public DocStoreBeanBaseAdapter(BeanDescriptor<T> desc, DeployBeanDescriptor<T> deploy) {
this.desc = desc;
this.server = desc.getEbeanServer();
this.mapped = deploy.isDocStoreMapped();
this.pathProps = deploy.getDocStorePathProperties();
this.docStore = deploy.getDocStore();
this.queueId = derive(desc, deploy.getDocStoreQueueId());
this.indexName = derive(desc, deploy.getDocStoreIndexName());
this.indexType = derive(desc, deploy.getDocStoreIndexType());
this.insert = deploy.getDocStoreInsertEvent();
this.update = deploy.getDocStoreUpdateEvent();
this.delete = deploy.getDocStoreDeleteEvent();
}
@Override
public boolean hasEmbeddedInvalidation() {
return !embeddedInvalidation.isEmpty();
}
@Override
public DocumentMapping createDocMapping() {
if (documentMapping != null) {
return documentMapping;
}
if (!mapped) return null;
this.docStructure = derivePathProperties(pathProps);
DocMappingBuilder mappingBuilder = new DocMappingBuilder(docStructure.doc(), docStore);
desc.docStoreMapping(mappingBuilder, null);
mappingBuilder.applyMapping();
sortableMap = mappingBuilder.collectSortable();
docStructure.prepareMany(desc);
documentMapping = mappingBuilder.create(queueId, indexName, indexType);
return documentMapping;
}
@Override
public String getIndexType() {
return indexType;
}
@Override
public String getIndexName() {
return indexName;
}
@Override
public void applyPath(Query<T> query) {
query.apply(docStructure.doc());
}
@Override
public String rawProperty(String property) {
String rawProperty = sortableMap.get(property);
return rawProperty == null ? property : rawProperty;
}
@Override
public void registerPaths() {
if (mapped && !registerPaths) {
Collection<PathProperties.Props> pathProps = docStructure.doc().getPathProps();
for (PathProperties.Props pathProp : pathProps) {
String path = pathProp.getPath();
if (path != null) {
BeanDescriptor<?> targetDesc = desc.getBeanDescriptor(path);
BeanProperty idProperty = targetDesc.getIdProperty();
if (idProperty != null) {
String fullPath = path + "." + idProperty.getName();
targetDesc.docStoreAdapter().registerInvalidationPath(desc.getDocStoreQueueId(), fullPath, pathProp.getProperties());
}
}
}
registerPaths = true;
}
}
@Override
public void registerInvalidationPath(String queueId, String path, Set<String> properties) {
if (!mapped) {
if (update == DocStoreMode.IGNORE) {
update = DocStoreMode.UPDATE;
}
}
embeddedInvalidation.add(getEmbeddedInvalidation(queueId, path, properties));
}
protected DocStoreEmbeddedInvalidation getEmbeddedInvalidation(String queueId, String path, Set<String> properties) {
if (properties.contains("*")) {
return new DocStoreEmbeddedInvalidation(queueId, path);
} else {
return new DocStoreEmbeddedInvalidationProperties(queueId, path, getPropertyPositions(properties));
}
}
protected int[] getPropertyPositions(Set<String> properties) {
List<Integer> posList = new ArrayList<>();
for (String property : properties) {
BeanProperty prop = desc.getBeanProperty(property);
if (prop != null) {
posList.add(prop.getPropertyIndex());
}
}
int[] pos = new int[posList.size()];
for (int i = 0; i < pos.length; i++) {
pos[i] = posList.get(i);
}
return pos;
}
@Override
public void updateEmbedded(PersistRequestBean<T> request, DocStoreUpdates docStoreUpdates) {
for (DocStoreEmbeddedInvalidation anEmbeddedInvalidation : embeddedInvalidation) {
anEmbeddedInvalidation.embeddedInvalidate(request, docStoreUpdates);
}
}
protected DocStructure derivePathProperties(PathProperties pathProps) {
boolean includeByDefault = (pathProps == null);
if (pathProps == null) {
pathProps = new PathProperties();
}
return getDocStructure(pathProps, includeByDefault);
}
protected DocStructure getDocStructure(PathProperties pathProps, final boolean includeByDefault) {
final DocStructure docStructure = new DocStructure(pathProps);
BeanProperty[] properties = desc.propertiesNonTransient();
for (BeanProperty property : properties) {
property.docStoreInclude(includeByDefault, docStructure);
}
InheritInfo inheritInfo = desc.getInheritInfo();
if (inheritInfo != null) {
inheritInfo.visitChildren(inheritInfo1 -> {
for (BeanProperty localProperty : inheritInfo1.localProperties()) {
localProperty.docStoreInclude(includeByDefault, docStructure);
}
});
}
return docStructure;
}
@Override
public FetchPath getEmbedded(String path) {
return docStructure.getEmbedded(path);
}
@Override
public FetchPath getEmbeddedManyRoot(String path) {
return docStructure.getEmbeddedManyRoot(path);
}
@Override
public boolean isMapped() {
return mapped;
}
@Override
public String getQueueId() {
return queueId;
}
@Override
public DocStoreMode getMode(PersistRequest.Type persistType, DocStoreMode txnMode) {
if (txnMode == null) {
return getMode(persistType);
} else if (txnMode == DocStoreMode.IGNORE) {
return DocStoreMode.IGNORE;
}
return mapped ? txnMode : getMode(persistType);
}
private DocStoreMode getMode(PersistRequest.Type persistType) {
switch (persistType) {
case INSERT:
return insert;
case UPDATE:
return update;
case DELETE:
return delete;
default:
return DocStoreMode.IGNORE;
}
}
protected String derive(BeanType<?> desc, String suppliedValue) {
return (suppliedValue != null && !suppliedValue.isEmpty()) ? suppliedValue : desc.getName().toLowerCase();
}
@Override
public abstract void deleteById(Object idValue, DocStoreUpdateContext txn) throws IOException;
@Override
public abstract void index(Object idValue, T entityBean, DocStoreUpdateContext txn) throws IOException;
@Override
public abstract void insert(Object idValue, PersistRequestBean<T> persistRequest, DocStoreUpdateContext txn) throws IOException;
@Override
public abstract void update(Object idValue, PersistRequestBean<T> persistRequest, DocStoreUpdateContext txn) throws IOException;
@Override
public abstract void updateEmbedded(Object idValue, String embeddedProperty, String embeddedRawContent, DocStoreUpdateContext txn) throws IOException;
}