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