001// -------------------------------------------------------------------------------- 002// Copyright 2002-2024 Echo Three, LLC 003// 004// Licensed under the Apache License, Version 2.0 (the "License"); 005// you may not use this file except in compliance with the License. 006// You may obtain a copy of the License at 007// 008// http://www.apache.org/licenses/LICENSE-2.0 009// 010// Unless required by applicable law or agreed to in writing, software 011// distributed under the License is distributed on an "AS IS" BASIS, 012// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013// See the License for the specific language governing permissions and 014// limitations under the License. 015// -------------------------------------------------------------------------------- 016 017package com.echothree.model.control.index.server.indexer; 018 019import com.echothree.model.control.core.common.EntityAttributeTypes; 020import com.echothree.model.control.core.server.control.CoreControl; 021import com.echothree.model.control.index.common.IndexConstants; 022import com.echothree.model.control.index.common.IndexFields; 023import com.echothree.model.control.index.common.IndexSubfields; 024import com.echothree.model.control.index.common.exception.IndexIOErrorException; 025import com.echothree.model.control.index.server.analysis.BasicAnalyzer; 026import com.echothree.model.control.index.server.control.IndexControl; 027import com.echothree.model.control.tag.server.control.TagControl; 028import com.echothree.model.control.workflow.server.control.WorkflowControl; 029import com.echothree.model.data.core.server.entity.EntityAliasType; 030import com.echothree.model.data.core.server.entity.EntityAttribute; 031import com.echothree.model.data.core.server.entity.EntityAttributeDetail; 032import com.echothree.model.data.core.server.entity.EntityClobAttribute; 033import com.echothree.model.data.core.server.entity.EntityDateAttribute; 034import com.echothree.model.data.core.server.entity.EntityGeoPointAttribute; 035import com.echothree.model.data.core.server.entity.EntityInstance; 036import com.echothree.model.data.core.server.entity.EntityIntegerAttribute; 037import com.echothree.model.data.core.server.entity.EntityListItemAttribute; 038import com.echothree.model.data.core.server.entity.EntityLongAttribute; 039import com.echothree.model.data.core.server.entity.EntityMultipleListItemAttribute; 040import com.echothree.model.data.core.server.entity.EntityNameAttribute; 041import com.echothree.model.data.core.server.entity.EntityStringAttribute; 042import com.echothree.model.data.core.server.entity.EntityTime; 043import com.echothree.model.data.core.server.entity.EntityTimeAttribute; 044import com.echothree.model.data.core.server.entity.EntityType; 045import com.echothree.model.data.index.server.entity.Index; 046import com.echothree.model.data.index.server.entity.IndexDetail; 047import com.echothree.model.data.index.server.entity.IndexStatus; 048import com.echothree.model.data.party.server.entity.Language; 049import com.echothree.model.data.tag.server.entity.EntityTag; 050import com.echothree.model.data.tag.server.entity.TagScope; 051import com.echothree.util.common.message.ExecutionErrors; 052import com.echothree.util.common.persistence.BasePK; 053import com.echothree.util.server.control.BaseLogic; 054import com.echothree.util.server.message.ExecutionErrorAccumulator; 055import com.echothree.util.server.persistence.BaseEntity; 056import com.echothree.util.server.persistence.Session; 057import java.io.Closeable; 058import java.io.File; 059import java.io.IOException; 060import java.nio.file.Paths; 061import java.util.List; 062import org.apache.commons.logging.Log; 063import org.apache.commons.logging.LogFactory; 064import org.apache.lucene.analysis.Analyzer; 065import org.apache.lucene.document.Document; 066import org.apache.lucene.document.Field; 067import org.apache.lucene.document.IntPoint; 068import org.apache.lucene.document.LongPoint; 069import org.apache.lucene.index.IndexWriter; 070import org.apache.lucene.index.IndexWriterConfig; 071import org.apache.lucene.index.SerialMergeScheduler; 072import org.apache.lucene.index.Term; 073import org.apache.lucene.store.Directory; 074import org.apache.lucene.store.FSDirectory; 075 076public abstract class BaseIndexer<BE extends BaseEntity> 077 extends BaseLogic 078 implements Closeable { 079 080 protected CoreControl coreControl = Session.getModelController(CoreControl.class); 081 protected IndexControl indexControl = Session.getModelController(IndexControl.class); 082 protected TagControl tagControl = Session.getModelController(TagControl.class); 083 protected WorkflowControl workflowControl = Session.getModelController(WorkflowControl.class); 084 protected Log log = LogFactory.getLog(this.getClass()); 085 086 protected ExecutionErrorAccumulator eea; 087 protected Index index; 088 089 protected Language language; 090 protected EntityType entityType; 091 protected IndexStatus indexStatus; 092 protected List<EntityAliasType> entityAliasTypes; 093 protected List<EntityAttribute> entityAttributes; 094 protected List<TagScope> tagScopes; 095 096 protected IndexWriter indexWriter; 097 098 protected BaseIndexer(final ExecutionErrorAccumulator eea, final Index index) { 099 this.eea = eea; 100 this.index = index; 101 } 102 103 public Index getIndex() { 104 return index; 105 } 106 107 public void open() { 108 if(indexWriter == null) { 109 IndexDetail indexDetail = index.getLastDetail(); 110 111 this.language = indexDetail.getLanguage(); 112 this.entityType = indexDetail.getIndexType().getLastDetail().getEntityType(); 113 this.indexStatus = indexControl.getIndexStatusForUpdate(index); 114 this.entityAliasTypes = coreControl.getEntityAliasTypesByEntityType(entityType); 115 this.entityAttributes = coreControl.getEntityAttributesByEntityType(entityType); 116 this.tagScopes = tagControl.getTagScopesByEntityType(entityType); 117 118 openIndexWriter(getAnalyzer()); 119 } 120 } 121 122 public EntityType getEntityType() { 123 return entityType; 124 } 125 126 @Override 127 public void close() { 128 if(indexWriter != null) { 129 closeIndexWriter(); 130 } 131 } 132 133 /** Index an EntityInstance in all of its Workflows. */ 134 private void indexWorkflowEntityStatuses(final Document document, final EntityInstance entityInstance) { 135 workflowControl.getWorkflowsByEntityType(entityInstance.getEntityType()).stream().forEach((workflow) -> { 136 var workflowEntityStatuses = workflowControl.getWorkflowEntityStatusesByEntityInstance(workflow, entityInstance); 137 138 if (!workflowEntityStatuses.isEmpty()) { 139 var workflowStepNamesBuilder = new StringBuilder(); 140 141 workflowEntityStatuses.forEach((workflowEntityStatus) -> { 142 if(workflowStepNamesBuilder.length() != 0) { 143 workflowStepNamesBuilder.append(' '); 144 } 145 146 workflowStepNamesBuilder.append(workflowEntityStatus.getWorkflowStep().getLastDetail().getWorkflowStepName()); 147 }); 148 149 document.add(new Field(workflow.getLastDetail().getWorkflowName(), workflowStepNamesBuilder.toString(), FieldTypes.NOT_STORED_TOKENIZED)); 150 } 151 }); 152 } 153 154 private void indexEntityTimes(final Document document, final EntityInstance entityInstance) { 155 EntityTime entityTime = coreControl.getEntityTime(entityInstance); 156 157 if(entityTime != null) { 158 Long createdTime = entityTime.getCreatedTime(); 159 Long modifiedTime = entityTime.getModifiedTime(); 160 Long deletedTime = entityTime.getDeletedTime(); 161 162 if(createdTime != null) { 163 document.add(new LongPoint(IndexFields.createdTime.name(), createdTime)); 164 } 165 166 if(modifiedTime != null) { 167 document.add(new LongPoint(IndexFields.modifiedTime.name(), modifiedTime)); 168 } 169 170 if(deletedTime != null) { 171 document.add(new LongPoint(IndexFields.deletedTime.name(), deletedTime)); 172 } 173 } 174 } 175 176 private void indexEntityAliases(final Document document, final EntityInstance entityInstance) { 177 var entityAliases = coreControl.getEntityAliasesByEntityInstance(entityInstance); 178 179 for(var entityAlias : entityAliases) { 180 var fieldName = entityAlias.getEntityAliasType().getLastDetail().getEntityAliasTypeName(); 181 var alias = entityAlias.getAlias(); 182 183 document.add(new Field(fieldName, alias, FieldTypes.NOT_STORED_NOT_TOKENIZED)); 184 } 185 186 } 187 188 private void indexEntityAttributes(final Document document, final EntityInstance entityInstance) { 189 entityAttributes.forEach((entityAttribute) -> { 190 EntityAttributeDetail entityAttributeDetail = entityAttribute.getLastDetail(); 191 String fieldName = entityAttributeDetail.getEntityAttributeName(); 192 String entityAttributeTypeName = entityAttributeDetail.getEntityAttributeType().getEntityAttributeTypeName(); 193 194 if (entityAttributeTypeName.equals(EntityAttributeTypes.BOOLEAN.name())) { 195 var entityBooleanAttribute = coreControl.getEntityBooleanAttribute(entityAttribute, entityInstance); 196 197 if(entityBooleanAttribute != null) { 198 var booleanAttribute = entityBooleanAttribute.getBooleanAttribute(); 199 if(IndexerDebugFlags.LogBaseIndexing) { 200 log.info("--- fieldName =\"" + fieldName + ", \"booleanAttribute = \"" + booleanAttribute + "\""); 201 } 202 document.add(new Field(fieldName, booleanAttribute.toString(), FieldTypes.NOT_STORED_NOT_TOKENIZED)); 203 } 204 } else if (entityAttributeTypeName.equals(EntityAttributeTypes.NAME.name())) { 205 EntityNameAttribute entityNameAttribute = coreControl.getEntityNameAttribute(entityAttribute, entityInstance); 206 207 if(entityNameAttribute != null) { 208 String nameAttribute = entityNameAttribute.getNameAttribute(); 209 if(IndexerDebugFlags.LogBaseIndexing) { 210 log.info("--- fieldName =\"" + fieldName + ", \"nameAttribute = \"" + nameAttribute + "\""); 211 } 212 document.add(new Field(fieldName, nameAttribute, FieldTypes.NOT_STORED_NOT_TOKENIZED)); 213 } 214 } else if (entityAttributeTypeName.equals(EntityAttributeTypes.INTEGER.name())) { 215 EntityIntegerAttribute entityIntegerAttribute = coreControl.getEntityIntegerAttribute(entityAttribute, entityInstance); 216 217 if(entityIntegerAttribute != null) { 218 Integer integerAttribute = entityIntegerAttribute.getIntegerAttribute(); 219 if(IndexerDebugFlags.LogBaseIndexing) { 220 log.info("--- fieldName =\"" + fieldName + ",\" integerAttribute = \"" + integerAttribute + "\""); 221 } 222 document.add(new IntPoint(fieldName, integerAttribute)); 223 } 224 } else if (entityAttributeTypeName.equals(EntityAttributeTypes.LONG.name())) { 225 EntityLongAttribute entityLongAttribute = coreControl.getEntityLongAttribute(entityAttribute, entityInstance); 226 227 if(entityLongAttribute != null) { 228 Long longAttribute = entityLongAttribute.getLongAttribute(); 229 if(IndexerDebugFlags.LogBaseIndexing) { 230 log.info("--- fieldName =\"" + fieldName + ",\" longAttribute = \"" + longAttribute + "\""); 231 } 232 document.add(new LongPoint(fieldName, longAttribute)); 233 } 234 } else if (language != null && entityAttributeTypeName.equals(EntityAttributeTypes.STRING.name())) { 235 EntityStringAttribute entityStringAttribute = coreControl.getEntityStringAttribute(entityAttribute, entityInstance, language); 236 237 if(entityStringAttribute != null) { 238 String stringAttribute = entityStringAttribute.getStringAttribute(); 239 if(IndexerDebugFlags.LogBaseIndexing) { 240 log.info("--- fieldName = \"" + fieldName + ",\" stringAttribute = \"" + stringAttribute + "\""); 241 } 242 document.add(new Field(fieldName, stringAttribute, FieldTypes.NOT_STORED_TOKENIZED)); 243 } 244 } else if (entityAttributeTypeName.equals(EntityAttributeTypes.GEOPOINT.name())) { 245 EntityGeoPointAttribute entityGeoPointAttribute = coreControl.getEntityGeoPointAttribute(entityAttribute, entityInstance); 246 247 if(entityGeoPointAttribute != null) { 248 Integer latitude = entityGeoPointAttribute.getLatitude(); 249 Integer longitude = entityGeoPointAttribute.getLongitude(); 250 Long elevation = entityGeoPointAttribute.getElevation(); 251 Long altitude = entityGeoPointAttribute.getAltitude(); 252 253 if(IndexerDebugFlags.LogBaseIndexing) { 254 log.info("--- fieldName = \"" + fieldName + ",\" latitude = \"" + latitude + ",\" longitude = \"" + longitude + ",\" elevation = \"" + elevation + ",\" altitude = \"" + altitude + "\""); 255 } 256 257 document.add(new IntPoint(fieldName + IndexConstants.INDEX_SUBFIELD_SEPARATOR + IndexSubfields.latitude.name(), latitude)); 258 document.add(new IntPoint(fieldName + IndexConstants.INDEX_SUBFIELD_SEPARATOR + IndexSubfields.longitude.name(), longitude)); 259 260 if(elevation != null) { 261 document.add(new LongPoint(fieldName + IndexConstants.INDEX_SUBFIELD_SEPARATOR + IndexSubfields.elevation.name(), elevation)); 262 } 263 264 if(altitude != null) { 265 document.add(new LongPoint(fieldName + IndexConstants.INDEX_SUBFIELD_SEPARATOR + IndexSubfields.altitude.name(), altitude)); 266 } 267 } 268 } else if (language != null && entityAttributeTypeName.equals(EntityAttributeTypes.CLOB.name())) { 269 // TODO: MIME type should be taken into account 270 EntityClobAttribute entityClobAttribute = coreControl.getEntityClobAttribute(entityAttribute, entityInstance, language); 271 272 if(entityClobAttribute != null) { 273 String clobAttribute = entityClobAttribute.getClobAttribute(); 274 if(IndexerDebugFlags.LogBaseIndexing) { 275 log.info("--- fieldName =\"" + fieldName + ",\" clobAttribute = \"" + clobAttribute + "\""); 276 } 277 document.add(new Field(fieldName, clobAttribute, FieldTypes.NOT_STORED_TOKENIZED)); 278 } 279 } else if (entityAttributeTypeName.equals(EntityAttributeTypes.DATE.name())) { 280 EntityDateAttribute entityDateAttribute = coreControl.getEntityDateAttribute(entityAttribute, entityInstance); 281 282 if(entityDateAttribute != null) { 283 Integer dateAttribute = entityDateAttribute.getDateAttribute(); 284 if(IndexerDebugFlags.LogBaseIndexing) { 285 log.info("--- fieldName =\"" + fieldName + ",\" dateAttribute = \"" + dateAttribute + "\""); 286 } 287 document.add(new IntPoint(fieldName, dateAttribute)); 288 } 289 } else if (entityAttributeTypeName.equals(EntityAttributeTypes.TIME.name())) { 290 EntityTimeAttribute entityTimeAttribute = coreControl.getEntityTimeAttribute(entityAttribute, entityInstance); 291 292 if(entityTimeAttribute != null) { 293 Long timeAttribute = entityTimeAttribute.getTimeAttribute(); 294 if(IndexerDebugFlags.LogBaseIndexing) { 295 log.info("--- fieldName =\"" + fieldName + ",\" dateAttribute = \"" + timeAttribute + "\""); 296 } 297 document.add(new LongPoint(fieldName, timeAttribute)); 298 } 299 } else if (entityAttributeTypeName.equals(EntityAttributeTypes.LISTITEM.name())) { 300 EntityListItemAttribute entityListItemAttribute = coreControl.getEntityListItemAttribute(entityAttribute, entityInstance); 301 302 if(entityListItemAttribute != null) { 303 String entityListItemName = entityListItemAttribute.getEntityListItem().getLastDetail().getEntityListItemName(); 304 if(IndexerDebugFlags.LogBaseIndexing) { 305 log.info("--- fieldName =\"" + fieldName + ",\" entityListItemName = \"" + entityListItemName + "\""); 306 } 307 document.add(new Field(fieldName, entityListItemName, FieldTypes.NOT_STORED_TOKENIZED)); 308 } 309 } else if (entityAttributeTypeName.equals(EntityAttributeTypes.MULTIPLELISTITEM.name())) { 310 List<EntityMultipleListItemAttribute> entityMultipleListItemAttributes = coreControl.getEntityMultipleListItemAttributes(entityAttribute, entityInstance); 311 if (entityMultipleListItemAttributes != null && !entityMultipleListItemAttributes.isEmpty()) { 312 StringBuilder entityListItemNamesBuilder = new StringBuilder(); 313 entityMultipleListItemAttributes.forEach((entityMultipleListItemAttribute) -> { 314 if(entityListItemNamesBuilder.length() != 0) { 315 entityListItemNamesBuilder.append(' '); 316 } 317 318 entityListItemNamesBuilder.append(entityMultipleListItemAttribute.getEntityListItem().getLastDetail().getEntityListItemName()); 319 }); 320 String entityListItemNames = entityListItemNamesBuilder.toString(); 321 if(IndexerDebugFlags.LogBaseIndexing) { 322 log.info("--- fieldName =\"" + fieldName + ",\" entityListItemNames = \"" + entityListItemNames + "\""); 323 } 324 document.add(new Field(fieldName, entityListItemNames, FieldTypes.NOT_STORED_TOKENIZED)); 325 } 326 } 327 }); 328 } 329 330 private void indexEntityTags(final Document document, final EntityInstance entityInstance) { 331 List<EntityTag> entityTags = tagControl.getEntityTagsByTaggedEntityInstance(entityInstance); 332 333 entityTags.stream().map((entityTag) -> entityTag.getTag().getLastDetail()).forEach((tagDetail) -> { 334 String tagScopeName = tagDetail.getTagScope().getLastDetail().getTagScopeName(); 335 String tagName = tagDetail.getTagName(); 336 337 document.add(new Field(tagScopeName, tagName, FieldTypes.NOT_STORED_TOKENIZED)); 338 }); 339 } 340 341 private void indexEntityAppearance(final Document document, final EntityInstance entityInstance) { 342 var entityAppearance = coreControl.getEntityAppearance(entityInstance); 343 344 if(entityAppearance != null) { 345 var entityAppearanceName = entityAppearance.getAppearance().getLastDetail().getAppearanceName(); 346 347 document.add(new Field(IndexFields.appearance.name(), entityAppearanceName, FieldTypes.NOT_STORED_TOKENIZED)); 348 } 349 } 350 351 protected Document newDocumentWithEntityInstanceFields(final EntityInstance entityInstance, final BasePK basePK) { 352 var document = new Document(); 353 354 document.add(new Field(IndexFields.entityRef.name(), basePK.getEntityRef(), FieldTypes.STORED_NOT_TOKENIZED)); 355 document.add(new Field(IndexFields.entityInstanceId.name(), entityInstance.getPrimaryKey().getEntityId().toString(), FieldTypes.STORED_NOT_TOKENIZED)); 356 357 indexWorkflowEntityStatuses(document, entityInstance); 358 indexEntityTimes(document, entityInstance); 359 indexEntityAliases(document, entityInstance); 360 indexEntityAttributes(document, entityInstance); 361 indexEntityTags(document, entityInstance); 362 indexEntityAppearance(document, entityInstance); 363 364 return document; 365 } 366 367 /** 368 * Create a Lucene IndexWriter. 369 */ 370 protected void openIndexWriter(final Analyzer analyzer) { 371 if(IndexerDebugFlags.LogBaseIndexing) { 372 log.info(">>> getIndexWriter"); 373 } 374 375 boolean createIndex = indexStatus.getCreatedTime() != null ? false : true; 376 377 if(createIndex) { 378 checkIndexDirectory(eea, indexStatus); 379 } 380 381 // indexCreatedTime is rechecked because if checkIndexDirectory was called, 382 // it will be set to the time the directory was created, vs. remaining null. 383 if(!hasExecutionErrors(eea)) { 384 try { 385 Directory fsDir = FSDirectory.open(Paths.get(index.getLastDetail().getDirectory())); 386 IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer); 387 388 indexWriterConfig.setMergeScheduler(new SerialMergeScheduler()); 389 indexWriterConfig.setOpenMode(createIndex ? IndexWriterConfig.OpenMode.CREATE : IndexWriterConfig.OpenMode.APPEND); 390 391 indexWriter = new IndexWriter(fsDir, indexWriterConfig); 392 } catch (IOException ioe1) { 393 // indexWriter will remain null, signaling that index was not able to be opened 394 if(IndexerDebugFlags.LogBaseIndexing) { 395 log.info("--- new IndexWriter failed"); 396 } 397 398 try { 399 if(indexWriter != null) { 400 indexWriter.close(); 401 } 402 } catch (IOException ioe2) { 403 // ioe2 discarded. 404 } finally { 405 indexWriter = null; 406 } 407 408 handleExecutionError(IndexIOErrorException.class, eea, ExecutionErrors.IndexIOError.name(), ioe1.getMessage()); 409 } 410 } 411 412 if(IndexerDebugFlags.LogBaseIndexing) { 413 log.info("<<< getIndexWriter"); 414 } 415 } 416 417 protected void closeIndexWriter() { 418 if(IndexerDebugFlags.LogBaseIndexing) { 419 log.info(">>> closeIndexWriter"); 420 } 421 422 try { 423 indexWriter.commit(); 424 indexWriter.close(); 425 } catch(IOException ioe) { 426 // unrecoverable error 427 // TODO: Index should be marked as possibly invalid 428 if(IndexerDebugFlags.LogBaseIndexing) { 429 log.info("--- indexWriter.close failed"); 430 } 431 432 handleExecutionError(IndexIOErrorException.class, eea, ExecutionErrors.IndexIOError.name(), ioe.getMessage()); 433 } 434 435 if(IndexerDebugFlags.LogBaseIndexing) { 436 log.info("<<< closeIndexWriter"); 437 } 438 } 439 440 private void checkIndexDirectory(final ExecutionErrorAccumulator eea, final IndexStatus indexStatus) { 441 if(IndexerDebugFlags.LogBaseIndexing) { 442 log.info("--- checkIndexDirectory, index = " + index); 443 } 444 Long indexCreatedTime = indexStatus.getCreatedTime(); 445 446 if(indexCreatedTime == null) { 447 indexCreatedTime = createIndexDirectory(eea); 448 449 if(!hasExecutionErrors(eea)) { 450 indexStatus.setCreatedTime(indexCreatedTime); 451 } 452 } 453 } 454 455 private Long createIndexDirectory(final ExecutionErrorAccumulator eea) { 456 if(IndexerDebugFlags.LogBaseIndexing) { 457 log.info(">>> createIndexDirectory, index = " + index); 458 } 459 Long indexCreatedTime = null; 460 String strDirectory = index.getLastDetail().getDirectory(); 461 File directory = new File(strDirectory); 462 463 if(directory.exists()) { 464 if(directory.isDirectory()) { 465 File[] files = directory.listFiles(); 466 467 if(files == null) { 468 handleExecutionError(IndexIOErrorException.class, eea, ExecutionErrors.IndexIOError.name(), "listFiles failed for " + strDirectory); 469 } else { 470 int numFiles = files.length; 471 472 for(int i = 0 ; i < numFiles ; i++) { 473 File indivFile = files[i]; 474 475 if(indivFile.isFile()) { 476 if(!indivFile.delete()) { 477 handleExecutionError(IndexIOErrorException.class, eea, ExecutionErrors.IndexIOError.name(), "delete failed for " + indivFile.getPath()); 478 } 479 } 480 } 481 } 482 } else { 483 handleExecutionError(IndexIOErrorException.class, eea, ExecutionErrors.IndexIOError.name(), "directory isn't a directory: " + directory.getPath()); 484 } 485 } else { 486 if(!directory.mkdirs()) { 487 handleExecutionError(IndexIOErrorException.class, eea, ExecutionErrors.IndexIOError.name(), "mkdirs failed for " + directory.getPath()); 488 } 489 } 490 491 if(!hasExecutionErrors(eea)) { 492 indexCreatedTime = System.currentTimeMillis(); 493 } 494 495 if(IndexerDebugFlags.LogBaseIndexing) { 496 log.info("<<< createIndexDirectory, indexCreatedTime = " + indexCreatedTime); 497 } 498 return indexCreatedTime; 499 } 500 501 public void forceReindex() { 502 indexStatus.setCreatedTime(null); 503 } 504 505 protected Analyzer getAnalyzer() { 506 return new BasicAnalyzer(eea, language, entityType, entityAliasTypes, entityAttributes, tagScopes); 507 } 508 509 protected abstract BE getEntity(final EntityInstance entityInstance); 510 511 protected abstract Document convertToDocument(final EntityInstance entityInstance, final BE item); 512 513 protected void addEntityToIndex(final EntityInstance entityInstance, final BE baseEntity) 514 throws IOException { 515 Document document = convertToDocument(entityInstance, baseEntity); 516 517 if(document != null) { 518 indexWriter.addDocument(document); 519 } 520 } 521 522 protected void removeEntityFromIndex(final BE baseEntity) 523 throws IOException { 524 indexWriter.deleteDocuments(new Term(IndexFields.entityRef.name(), baseEntity.getPrimaryKey().getEntityRef())); 525 } 526 527 public void updateIndex(final EntityInstance entityInstance) { 528 BE baseEntity = getEntity(entityInstance); 529 530 if(baseEntity != null) { 531 EntityTime entityTime = coreControl.getEntityTime(entityInstance); 532 533 if(entityTime != null) { 534 Long modifiedTime = entityTime.getModifiedTime(); 535 Long deletedTime = entityTime.getDeletedTime(); 536 537 try { 538 if(modifiedTime != null || deletedTime != null) { 539 removeEntityFromIndex(baseEntity); 540 } 541 542 if(deletedTime == null) { 543 addEntityToIndex(entityInstance, baseEntity); 544 } 545 } catch(IOException ioe) { 546 handleExecutionError(IndexIOErrorException.class, eea, ExecutionErrors.IndexIOError.name(), ioe.getMessage()); 547 } 548 } 549 } 550 } 551 552}