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