001package com.avaje.ebeanservice.docstore.api.support; 002 003import com.avaje.ebean.FetchPath; 004import com.avaje.ebean.Query; 005import com.avaje.ebean.annotation.DocStore; 006import com.avaje.ebean.annotation.DocStoreMode; 007import com.avaje.ebean.plugin.BeanType; 008import com.avaje.ebean.text.PathProperties; 009import com.avaje.ebeaninternal.api.SpiEbeanServer; 010import com.avaje.ebeaninternal.server.core.PersistRequest; 011import com.avaje.ebeaninternal.server.core.PersistRequestBean; 012import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; 013import com.avaje.ebeaninternal.server.deploy.BeanProperty; 014import com.avaje.ebeaninternal.server.deploy.InheritInfo; 015import com.avaje.ebeaninternal.server.deploy.InheritInfoVisitor; 016import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; 017import com.avaje.ebeanservice.docstore.api.DocStoreBeanAdapter; 018import com.avaje.ebeanservice.docstore.api.DocStoreUpdateContext; 019import com.avaje.ebeanservice.docstore.api.DocStoreUpdates; 020import com.avaje.ebeanservice.docstore.api.mapping.DocMappingBuilder; 021import com.avaje.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<DocStoreEmbeddedInvalidation>(); 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 String idName = targetDesc.getIdProperty().getName(); 179 String fullPath = path + "." + idName; 180 targetDesc.docStoreAdapter().registerInvalidationPath(desc.getDocStoreQueueId(), fullPath, pathProp.getProperties()); 181 } 182 } 183 registerPaths = true; 184 } 185 } 186 187 /** 188 * Register a doc store invalidation listener for the given bean type, path and properties. 189 */ 190 @Override 191 public void registerInvalidationPath(String queueId, String path, Set<String> properties) { 192 193 if (!mapped) { 194 if (update == DocStoreMode.IGNORE) { 195 // bean type not mapped but is included as nested document 196 // in a doc store index so we need to update 197 update = DocStoreMode.UPDATE; 198 } 199 } 200 embeddedInvalidation.add(getEmbeddedInvalidation(queueId, path, properties)); 201 } 202 203 /** 204 * Return the DsInvalidationListener based on the properties, path. 205 */ 206 protected DocStoreEmbeddedInvalidation getEmbeddedInvalidation(String queueId, String path, Set<String> properties) { 207 208 if (properties.contains("*")) { 209 return new DocStoreEmbeddedInvalidation(queueId, path); 210 } else { 211 return new DocStoreEmbeddedInvalidationProperties(queueId, path, getPropertyPositions(properties)); 212 } 213 } 214 215 /** 216 * Return the property names as property index positions. 217 */ 218 protected int[] getPropertyPositions(Set<String> properties) { 219 List<Integer> posList = new ArrayList<Integer>(); 220 for (String property : properties) { 221 BeanProperty prop = desc.getBeanProperty(property); 222 if (prop != null) { 223 posList.add(prop.getPropertyIndex()); 224 } 225 } 226 int[] pos = new int[posList.size()]; 227 for (int i = 0; i <pos.length; i++) { 228 pos[i] = posList.get(i); 229 } 230 return pos; 231 } 232 233 @Override 234 public void updateEmbedded(PersistRequestBean<T> request, DocStoreUpdates docStoreUpdates) { 235 for (int i = 0; i < embeddedInvalidation.size(); i++) { 236 embeddedInvalidation.get(i).embeddedInvalidate(request, docStoreUpdates); 237 } 238 } 239 240 /** 241 * Return the pathProperties which defines the JSON document to index. 242 * This can add derived/embedded/nested parts to the document. 243 */ 244 protected DocStructure derivePathProperties(PathProperties pathProps) { 245 246 boolean includeByDefault = (pathProps == null); 247 if (pathProps == null) { 248 pathProps = new PathProperties(); 249 } 250 251 return getDocStructure(pathProps, includeByDefault); 252 } 253 254 protected DocStructure getDocStructure(PathProperties pathProps, final boolean includeByDefault) { 255 256 final DocStructure docStructure = new DocStructure(pathProps); 257 258 BeanProperty[] properties = desc.propertiesNonTransient(); 259 for (int i = 0; i < properties.length; i++) { 260 properties[i].docStoreInclude(includeByDefault, docStructure); 261 } 262 263 InheritInfo inheritInfo = desc.getInheritInfo(); 264 if (inheritInfo != null) { 265 inheritInfo.visitChildren(new InheritInfoVisitor() { 266 @Override 267 public void visit(InheritInfo inheritInfo) { 268 for (BeanProperty localProperty : inheritInfo.localProperties()) { 269 localProperty.docStoreInclude(includeByDefault, docStructure); 270 } 271 } 272 }); 273 } 274 275 return docStructure; 276 } 277 278 public FetchPath getEmbedded(String path) { 279 return docStructure.getEmbedded(path); 280 } 281 282 public FetchPath getEmbeddedManyRoot(String path) { 283 return docStructure.getEmbeddedManyRoot(path); 284 } 285 286 @Override 287 public boolean isMapped() { 288 return mapped; 289 } 290 291 @Override 292 public String getQueueId() { 293 return queueId; 294 } 295 296 @Override 297 public DocStoreMode getMode(PersistRequest.Type persistType, DocStoreMode txnMode) { 298 299 if (txnMode == null) { 300 return getMode(persistType); 301 } else if (txnMode == DocStoreMode.IGNORE) { 302 return DocStoreMode.IGNORE; 303 } 304 return mapped ? txnMode : getMode(persistType); 305 } 306 307 private DocStoreMode getMode(PersistRequest.Type persistType) { 308 switch (persistType) { 309 case INSERT: 310 return insert; 311 case UPDATE: 312 return update; 313 case DELETE: 314 return delete; 315 default: 316 return DocStoreMode.IGNORE; 317 } 318 } 319 320 /** 321 * Return the supplied value or default to the bean name lower case. 322 */ 323 protected String derive(BeanType<?> desc, String suppliedValue) { 324 return (suppliedValue != null && !suppliedValue.isEmpty()) ? suppliedValue : desc.getName().toLowerCase(); 325 } 326 327 @Override 328 public abstract void deleteById(Object idValue, DocStoreUpdateContext txn) throws IOException; 329 330 @Override 331 public abstract void index(Object idValue, T entityBean, DocStoreUpdateContext txn) throws IOException; 332 333 @Override 334 public abstract void insert(Object idValue, PersistRequestBean<T> persistRequest, DocStoreUpdateContext txn) throws IOException; 335 336 @Override 337 public abstract void update(Object idValue, PersistRequestBean<T> persistRequest, DocStoreUpdateContext txn) throws IOException; 338 339 @Override 340 public abstract void updateEmbedded(Object idValue, String embeddedProperty, String embeddedRawContent, DocStoreUpdateContext txn) throws IOException; 341 342}