001package io.ebeanservice.docstore.api.support; 002 003import io.ebean.FetchPath; 004import io.ebean.Query; 005import io.ebean.annotation.DocStore; 006import io.ebean.annotation.DocStoreMode; 007import io.ebean.docstore.DocUpdateContext; 008import io.ebean.plugin.BeanType; 009import io.ebean.text.PathProperties; 010import io.ebeaninternal.api.SpiEbeanServer; 011import io.ebeaninternal.server.core.PersistRequest; 012import io.ebeaninternal.server.core.PersistRequestBean; 013import io.ebeaninternal.server.deploy.BeanDescriptor; 014import io.ebeaninternal.server.deploy.BeanProperty; 015import io.ebeaninternal.server.deploy.InheritInfo; 016import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; 017import io.ebeanservice.docstore.api.DocStoreBeanAdapter; 018import io.ebeanservice.docstore.api.DocStoreUpdateContext; 019import io.ebeanservice.docstore.api.DocStoreUpdates; 020import io.ebeanservice.docstore.api.mapping.DocMappingBuilder; 021import io.ebeanservice.docstore.api.mapping.DocumentMapping; 022 023import java.io.IOException; 024import java.util.ArrayList; 025import java.util.Collection; 026import java.util.List; 027import java.util.Map; 028import java.util.Set; 029 030/** 031 * Base implementation for much of DocStoreBeanAdapter. 032 */ 033public abstract class DocStoreBeanBaseAdapter<T> implements DocStoreBeanAdapter<T> { 034 035 protected final SpiEbeanServer server; 036 037 /** 038 * The associated BeanDescriptor. 039 */ 040 protected final BeanDescriptor<T> desc; 041 042 /** 043 * The type of index. 044 */ 045 protected final boolean mapped; 046 047 /** 048 * Identifier used in the queue system to identify the index. 049 */ 050 protected final String queueId; 051 052 /** 053 * ElasticSearch index type. 054 */ 055 protected final String indexType; 056 057 /** 058 * ElasticSearch index name. 059 */ 060 protected final String indexName; 061 062 /** 063 * Doc store deployment annotation. 064 */ 065 private final DocStore docStore; 066 067 /** 068 * Behavior on insert. 069 */ 070 protected final DocStoreMode insert; 071 072 /** 073 * Behavior on update. 074 */ 075 protected DocStoreMode update; 076 077 /** 078 * Behavior on delete. 079 */ 080 protected final DocStoreMode delete; 081 082 /** 083 * List of embedded paths from other documents that include this document type. 084 * As such an update to this doc type means that those embedded documents need to be updated. 085 */ 086 protected final List<DocStoreEmbeddedInvalidation> embeddedInvalidation = new ArrayList<>(); 087 088 protected final PathProperties pathProps; 089 090 /** 091 * Map of properties to 'raw' properties. 092 */ 093 protected Map<String, String> sortableMap; 094 095 /** 096 * Nested path properties defining the doc structure for indexing. 097 */ 098 protected DocStructure docStructure; 099 100 protected DocumentMapping documentMapping; 101 102 private boolean registerPaths; 103 104 public DocStoreBeanBaseAdapter(BeanDescriptor<T> desc, DeployBeanDescriptor<T> deploy) { 105 106 this.desc = desc; 107 this.server = desc.getEbeanServer(); 108 this.mapped = deploy.isDocStoreMapped(); 109 this.pathProps = deploy.getDocStorePathProperties(); 110 this.docStore = deploy.getDocStore(); 111 this.queueId = derive(desc, deploy.getDocStoreQueueId()); 112 this.indexName = derive(desc, deploy.getDocStoreIndexName()); 113 this.indexType = derive(desc, deploy.getDocStoreIndexType()); 114 this.insert = deploy.getDocStoreInsertEvent(); 115 this.update = deploy.getDocStoreUpdateEvent(); 116 this.delete = deploy.getDocStoreDeleteEvent(); 117 } 118 119 @Override 120 public boolean hasEmbeddedInvalidation() { 121 return !embeddedInvalidation.isEmpty(); 122 } 123 124 @Override 125 public DocumentMapping createDocMapping() { 126 127 if (documentMapping != null) { 128 return documentMapping; 129 } 130 131 if (!mapped) return null; 132 133 this.docStructure = derivePathProperties(pathProps); 134 135 DocMappingBuilder mappingBuilder = new DocMappingBuilder(docStructure.doc(), docStore); 136 desc.docStoreMapping(mappingBuilder, null); 137 mappingBuilder.applyMapping(); 138 139 sortableMap = mappingBuilder.collectSortable(); 140 docStructure.prepareMany(desc); 141 documentMapping = mappingBuilder.create(queueId, indexName, indexType); 142 return documentMapping; 143 } 144 145 @Override 146 public String getIndexType() { 147 return indexType; 148 } 149 150 @Override 151 public String getIndexName() { 152 return indexName; 153 } 154 155 @Override 156 public void applyPath(Query<T> query) { 157 query.apply(docStructure.doc()); 158 } 159 160 @Override 161 public String rawProperty(String property) { 162 163 String rawProperty = sortableMap.get(property); 164 return rawProperty == null ? property : rawProperty; 165 } 166 167 /** 168 * Register invalidation paths for embedded documents. 169 */ 170 @Override 171 public void registerPaths() { 172 if (mapped && !registerPaths) { 173 Collection<PathProperties.Props> pathProps = docStructure.doc().getPathProps(); 174 for (PathProperties.Props pathProp : pathProps) { 175 String path = pathProp.getPath(); 176 if (path != null) { 177 BeanDescriptor<?> targetDesc = desc.getBeanDescriptor(path); 178 BeanProperty idProperty = targetDesc.getIdProperty(); 179 if (idProperty != null) { 180 // embedded beans don't have id property 181 String fullPath = path + "." + idProperty.getName(); 182 targetDesc.docStoreAdapter().registerInvalidationPath(desc.getDocStoreQueueId(), fullPath, pathProp.getProperties()); 183 } 184 } 185 } 186 registerPaths = true; 187 } 188 } 189 190 /** 191 * Register a doc store invalidation listener for the given bean type, path and properties. 192 */ 193 @Override 194 public void registerInvalidationPath(String queueId, String path, Set<String> properties) { 195 196 if (!mapped) { 197 if (update == DocStoreMode.IGNORE) { 198 // bean type not mapped but is included as nested document 199 // in a doc store index so we need to update 200 update = DocStoreMode.UPDATE; 201 } 202 } 203 embeddedInvalidation.add(getEmbeddedInvalidation(queueId, path, properties)); 204 } 205 206 /** 207 * Return the DsInvalidationListener based on the properties, path. 208 */ 209 protected DocStoreEmbeddedInvalidation getEmbeddedInvalidation(String queueId, String path, Set<String> properties) { 210 211 if (properties.contains("*")) { 212 return new DocStoreEmbeddedInvalidation(queueId, path); 213 } else { 214 return new DocStoreEmbeddedInvalidationProperties(queueId, path, getPropertyPositions(properties)); 215 } 216 } 217 218 /** 219 * Return the property names as property index positions. 220 */ 221 protected int[] getPropertyPositions(Set<String> properties) { 222 List<Integer> posList = new ArrayList<>(); 223 for (String property : properties) { 224 BeanProperty prop = desc.getBeanProperty(property); 225 if (prop != null) { 226 posList.add(prop.getPropertyIndex()); 227 } 228 } 229 int[] pos = new int[posList.size()]; 230 for (int i = 0; i < pos.length; i++) { 231 pos[i] = posList.get(i); 232 } 233 return pos; 234 } 235 236 @Override 237 public void updateEmbedded(PersistRequestBean<T> request, DocStoreUpdates docStoreUpdates) { 238 for (DocStoreEmbeddedInvalidation anEmbeddedInvalidation : embeddedInvalidation) { 239 anEmbeddedInvalidation.embeddedInvalidate(request, docStoreUpdates); 240 } 241 } 242 243 /** 244 * Return the pathProperties which defines the JSON document to index. 245 * This can add derived/embedded/nested parts to the document. 246 */ 247 protected DocStructure derivePathProperties(PathProperties pathProps) { 248 249 boolean includeByDefault = (pathProps == null); 250 if (pathProps == null) { 251 pathProps = new PathProperties(); 252 } 253 254 return getDocStructure(pathProps, includeByDefault); 255 } 256 257 protected DocStructure getDocStructure(PathProperties pathProps, final boolean includeByDefault) { 258 259 final DocStructure docStructure = new DocStructure(pathProps); 260 261 BeanProperty[] properties = desc.propertiesNonTransient(); 262 for (BeanProperty property : properties) { 263 property.docStoreInclude(includeByDefault, docStructure); 264 } 265 266 InheritInfo inheritInfo = desc.getInheritInfo(); 267 if (inheritInfo != null) { 268 inheritInfo.visitChildren(inheritInfo1 -> { 269 for (BeanProperty localProperty : inheritInfo1.localProperties()) { 270 localProperty.docStoreInclude(includeByDefault, docStructure); 271 } 272 }); 273 } 274 275 return docStructure; 276 } 277 278 @Override 279 public FetchPath getEmbedded(String path) { 280 return docStructure.getEmbedded(path); 281 } 282 283 @Override 284 public FetchPath getEmbeddedManyRoot(String path) { 285 return docStructure.getEmbeddedManyRoot(path); 286 } 287 288 @Override 289 public boolean isMapped() { 290 return mapped; 291 } 292 293 @Override 294 public String getQueueId() { 295 return queueId; 296 } 297 298 @Override 299 public DocStoreMode getMode(PersistRequest.Type persistType, DocStoreMode txnMode) { 300 301 if (txnMode == null) { 302 return getMode(persistType); 303 } else if (txnMode == DocStoreMode.IGNORE) { 304 return DocStoreMode.IGNORE; 305 } 306 return mapped ? txnMode : getMode(persistType); 307 } 308 309 private DocStoreMode getMode(PersistRequest.Type persistType) { 310 switch (persistType) { 311 case INSERT: 312 return insert; 313 case UPDATE: 314 return update; 315 case DELETE: 316 return delete; 317 default: 318 return DocStoreMode.IGNORE; 319 } 320 } 321 322 /** 323 * Return the supplied value or default to the bean name lower case. 324 */ 325 protected String derive(BeanType<?> desc, String suppliedValue) { 326 return (suppliedValue != null && !suppliedValue.isEmpty()) ? suppliedValue : desc.getName().toLowerCase(); 327 } 328 329 @Override 330 public abstract void deleteById(Object idValue, DocUpdateContext txn) throws IOException; 331 332 @Override 333 public abstract void index(Object idValue, T entityBean, DocUpdateContext txn) throws IOException; 334 335 @Override 336 public abstract void insert(Object idValue, PersistRequestBean<T> persistRequest, DocStoreUpdateContext txn) throws IOException; 337 338 @Override 339 public abstract void update(Object idValue, PersistRequestBean<T> persistRequest, DocStoreUpdateContext txn) throws IOException; 340 341 @Override 342 public abstract void updateEmbedded(Object idValue, String embeddedProperty, String embeddedRawContent, DocUpdateContext txn) throws IOException; 343 344}