001/* 002 * Copyright 2015 Anyware Services 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 */ 016package org.ametys.cms.content.indexing.solr; 017 018import java.io.IOException; 019import java.nio.ByteBuffer; 020import java.nio.CharBuffer; 021import java.nio.charset.CharsetDecoder; 022import java.nio.charset.CodingErrorAction; 023import java.nio.charset.StandardCharsets; 024import java.text.SimpleDateFormat; 025import java.util.ArrayList; 026import java.util.Arrays; 027import java.util.Collection; 028import java.util.HashMap; 029import java.util.HashSet; 030import java.util.List; 031import java.util.Map; 032import java.util.Set; 033 034import javax.jcr.RepositoryException; 035 036import org.apache.avalon.framework.activity.Initializable; 037import org.apache.avalon.framework.component.Component; 038import org.apache.avalon.framework.context.Context; 039import org.apache.avalon.framework.context.ContextException; 040import org.apache.avalon.framework.context.Contextualizable; 041import org.apache.avalon.framework.service.ServiceException; 042import org.apache.avalon.framework.service.ServiceManager; 043import org.apache.avalon.framework.service.Serviceable; 044import org.apache.cocoon.components.ContextHelper; 045import org.apache.cocoon.environment.Request; 046import org.apache.commons.lang3.ObjectUtils; 047import org.apache.commons.lang3.StringUtils; 048import org.apache.solr.client.solrj.SolrClient; 049import org.apache.solr.client.solrj.SolrQuery; 050import org.apache.solr.client.solrj.SolrServerException; 051import org.apache.solr.client.solrj.request.CoreAdminRequest; 052import org.apache.solr.client.solrj.request.schema.FieldTypeDefinition; 053import org.apache.solr.client.solrj.request.schema.SchemaRequest; 054import org.apache.solr.client.solrj.request.schema.SchemaRequest.Update; 055import org.apache.solr.client.solrj.response.CoreAdminResponse; 056import org.apache.solr.client.solrj.response.QueryResponse; 057import org.apache.solr.client.solrj.response.SolrResponseBase; 058import org.apache.solr.client.solrj.response.UpdateResponse; 059import org.apache.solr.client.solrj.response.schema.SchemaRepresentation; 060import org.apache.solr.client.solrj.response.schema.SchemaResponse; 061import org.apache.solr.client.solrj.util.ClientUtils; 062import org.apache.solr.common.SolrInputDocument; 063import org.apache.solr.common.util.NamedList; 064import org.slf4j.Logger; 065 066import org.ametys.cms.indexing.IndexingException; 067import org.ametys.cms.indexing.solr.ReloadAclCacheRequest; 068import org.ametys.cms.indexing.solr.UpdateCorePropertyRequest; 069import org.ametys.cms.repository.Content; 070import org.ametys.cms.repository.ContentQueryHelper; 071import org.ametys.cms.repository.WorkflowAwareContent; 072import org.ametys.cms.search.query.ContentAttachmentQuery; 073import org.ametys.cms.search.query.DocumentTypeQuery; 074import org.ametys.cms.search.query.OrQuery; 075import org.ametys.cms.search.query.Query; 076import org.ametys.cms.search.query.ResourceLocationQuery; 077import org.ametys.cms.search.solr.SolrClientProvider; 078import org.ametys.cms.search.solr.schema.CopyFieldDefinition; 079import org.ametys.cms.search.solr.schema.FieldDefinition; 080import org.ametys.cms.search.solr.schema.SchemaDefinition; 081import org.ametys.cms.search.solr.schema.SchemaDefinitionProvider; 082import org.ametys.cms.search.solr.schema.SchemaDefinitionProviderExtensionPoint; 083import org.ametys.cms.search.solr.schema.SchemaFields; 084import org.ametys.cms.search.solr.schema.SchemaHelper; 085import org.ametys.plugins.explorer.resources.Resource; 086import org.ametys.plugins.explorer.resources.ResourceCollection; 087import org.ametys.plugins.repository.AmetysObject; 088import org.ametys.plugins.repository.AmetysObjectIterable; 089import org.ametys.plugins.repository.AmetysObjectResolver; 090import org.ametys.plugins.repository.RepositoryConstants; 091import org.ametys.plugins.repository.TraversableAmetysObject; 092import org.ametys.plugins.repository.UnknownAmetysObjectException; 093import org.ametys.plugins.repository.collection.AmetysObjectCollection; 094import org.ametys.plugins.repository.provider.RequestAttributeWorkspaceSelector; 095import org.ametys.plugins.repository.provider.WorkspaceSelector; 096import org.ametys.runtime.config.Config; 097import org.ametys.runtime.plugin.component.AbstractLogEnabled; 098 099/** 100 * Solr indexer. 101 */ 102public class SolrIndexer extends AbstractLogEnabled implements Component, Serviceable, Initializable, Contextualizable 103{ 104 /** The component role. */ 105 public static final String ROLE = SolrIndexer.class.getName(); 106 107 private static final ThreadLocal<SimpleDateFormat> __DATE_FORMAT = new ThreadLocal<>(); 108 109 private static final String _CONFIGSET_NAME_PREFIX = "configset-"; 110 111 private static final List<String> _READ_ONLY_FIELDS = Arrays.asList("id", "_root_", "_version_", "text"); 112 private static final List<String> _READ_ONLY_FIELDTYPES = Arrays.asList("string", "long", "text_general"); 113 114 private static final int __SOLR_STRING_NB_BYTES_LIMIT = 32766; 115 116 /** The ametys object resolver. */ 117 protected AmetysObjectResolver _resolver; 118 /** The schema definition provider extension point. */ 119 protected SchemaDefinitionProviderExtensionPoint _schemaDefProviderEP; 120 /** The schema helper. */ 121 protected SchemaHelper _schemaHelper; 122 /** Solr Ametys contents indexer */ 123 protected SolrContentIndexer _solrContentIndexer; 124 /** Solr workflow indexer. */ 125 protected SolrWorkflowIndexer _solrWorkflowIndexer; 126 /** Solr resource indexer. */ 127 protected SolrResourceIndexer _solrResourceIndexer; 128 129 /** The Solr client provider */ 130 protected SolrClientProvider _solrClientProvider; 131 132 /** The solr core prefix. */ 133 protected String _solrCorePrefix; 134 /** The Ametys internal URL used by Solr to query Ametys */ 135 protected String _ametysInternalUrl; 136 137 /** The workspace selector. */ 138 protected WorkspaceSelector _workspaceSelector; 139 140 /** The avalon context */ 141 protected Context _context; 142 143 /** 144 * Returns the formatter for indexing dates. This is used for adding a dates as formatted strings (and not with date object directly) to prevent indexing of the wrong value because of time zone 145 * @return The date format for indexing dates 146 */ 147 public static SimpleDateFormat dateFormat() 148 { 149 if (__DATE_FORMAT.get() == null) 150 { 151 __DATE_FORMAT.set(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")); 152 } 153 return __DATE_FORMAT.get(); 154 } 155 156 /** 157 * Truncates (if needed) the given string in order to be indexed without <i>immense term</i> error by Solr. 158 * Only the {@value #__SOLR_STRING_NB_BYTES_LIMIT} first bytes of the String will be kept. 159 * @param value The string value to index 160 * @param logger The logger for logging in WARN level in case the given string is too long and will be truncated. Can be null if you do not want to log. 161 * @param documentId The id of the document being indexed. Can be null if you do not want to log. 162 * @param fieldName The name of the field being indexed. Can be null if you do not want to log. 163 * @return The given string value, or its truncation if it is too long (greater than {@value #__SOLR_STRING_NB_BYTES_LIMIT} bytes) 164 */ 165 public static String truncateUtf8StringValue(String value, Logger logger, String documentId , String fieldName) 166 { 167 if (value.length() * 4 <= __SOLR_STRING_NB_BYTES_LIMIT) 168 { 169 // With UTF-8, a character is encoded using 1, 2, 3 or 4 bytes, so (value.length() <= value.getBytes().length <= 4 * value.length()) 170 // As a result, value.getBytes().length <= limit 171 return value; 172 } 173 174 // There is a doubt, the string may need to be truncated (or not) 175 byte[] valueBytes = value.getBytes(StandardCharsets.UTF_8); 176 int bytesLength = valueBytes.length; 177 if (bytesLength <= __SOLR_STRING_NB_BYTES_LIMIT) 178 { 179 return value; 180 } 181 182 if (ObjectUtils.allNotNull(logger, documentId, fieldName)) 183 { 184 logger.warn("The string value for document '{}' and field name '{}' is longer ({}) than the max bytes length {}. It will be truncated to prevent Solr error, but you should consider verifying why this string is so long.", documentId, fieldName, bytesLength, __SOLR_STRING_NB_BYTES_LIMIT); 185 } 186 187 // Need a truncation (inspired by https://stackoverflow.com/questions/119328/how-do-i-truncate-a-java-string-to-fit-in-a-given-number-of-bytes-once-utf-8-en#answer-35148974) 188 CharBuffer charBuffer = CharBuffer.allocate(__SOLR_STRING_NB_BYTES_LIMIT); 189 CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder() 190 .onMalformedInput(CodingErrorAction.IGNORE); 191 decoder.decode(ByteBuffer.wrap(valueBytes, 0, __SOLR_STRING_NB_BYTES_LIMIT), charBuffer, true); 192 decoder.flush(charBuffer); 193 return new String(charBuffer.array(), 0, charBuffer.position()); 194 } 195 196 @Override 197 public void service(ServiceManager serviceManager) throws ServiceException 198 { 199 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 200 _schemaDefProviderEP = (SchemaDefinitionProviderExtensionPoint) serviceManager.lookup(SchemaDefinitionProviderExtensionPoint.ROLE); 201 _schemaHelper = (SchemaHelper) serviceManager.lookup(SchemaHelper.ROLE); 202 _solrContentIndexer = (SolrContentIndexer) serviceManager.lookup(SolrContentIndexer.ROLE); 203 _solrWorkflowIndexer = (SolrWorkflowIndexer) serviceManager.lookup(SolrWorkflowIndexer.ROLE); 204 _solrResourceIndexer = (SolrResourceIndexer) serviceManager.lookup(SolrResourceIndexer.ROLE); 205 _solrClientProvider = (SolrClientProvider) serviceManager.lookup(SolrClientProvider.ROLE); 206 _workspaceSelector = (WorkspaceSelector) serviceManager.lookup(WorkspaceSelector.ROLE); 207 } 208 209 @Override 210 public void initialize() throws Exception 211 { 212 Config config = Config.getInstance(); 213 _solrCorePrefix = config.getValueAsString("cms.solr.core.prefix"); 214 215 _ametysInternalUrl = config.getValueAsString("cms.solr.core.ametys.internal.url"); 216 if (StringUtils.isBlank(_ametysInternalUrl)) 217 { 218 // fallback to CMS URL as internal URL is an optional parameter 219 _ametysInternalUrl = config.getValueAsString("cms.url"); 220 } 221 } 222 223 @Override 224 public void contextualize(Context context) throws ContextException 225 { 226 _context = context; 227 } 228 229 /** 230 * Gets the Solr client 231 * @param workspaceName The name of the workspace 232 * @return the Solr client 233 */ 234 protected SolrClient _getSolrClient(String workspaceName) 235 { 236 return _solrClientProvider.getUpdateClient(workspaceName); 237 } 238 239 // for admin operations 240 private SolrClient _defaultSolrClient() 241 { 242 return _solrClientProvider.getUpdateClient(RepositoryConstants.DEFAULT_WORKSPACE); 243 } 244 245 /** 246 * Get the names of the Solr cores. 247 * @return The names of the Solr cores. 248 * @throws IOException If an I/O error occurs. 249 * @throws SolrServerException If a Solr error occurs. 250 */ 251 @SuppressWarnings("unchecked") 252 public Set<String> getCoreNames() throws IOException, SolrServerException 253 { 254 Set<String> coreNames = new HashSet<>(); 255 256 getLogger().debug("Getting core list."); 257 258 SolrQuery query = new SolrQuery(); 259 query.setRequestHandler("/admin/cores"); 260 query.setParam("action", "STATUS"); 261 262 QueryResponse response = _defaultSolrClient().query(query); 263 264 NamedList<NamedList<?>> status = (NamedList<NamedList<?>>) response.getResponse().get("status"); 265 for (Map.Entry<String, NamedList<?>> core : status) 266 { 267 String fullName = (String) core.getValue().get("name"); 268 if (fullName.startsWith(_solrCorePrefix)) 269 { 270 coreNames.add(fullName.substring(_solrCorePrefix.length())); 271 } 272 } 273 274 return coreNames; 275 } 276 277 /** 278 * Get the names of the Solr cores. 279 * @return The names of the Solr cores. 280 * @throws IOException If an I/O error occurs. 281 * @throws SolrServerException If a Solr error occurs. 282 */ 283 protected Set<String> getRealCoreNames() throws IOException, SolrServerException 284 { 285 Set<String> coreNames = new HashSet<>(); 286 287 getLogger().debug("Getting core list."); 288 289 CoreAdminResponse response = new CoreAdminRequest().process(_defaultSolrClient()); 290 291 NamedList<NamedList<Object>> status = response.getCoreStatus(); 292 for (Map.Entry<String, NamedList<Object>> core : status) 293 { 294 String fullName = (String) core.getValue().get("name"); 295 if (fullName.startsWith(_solrCorePrefix)) 296 { 297 coreNames.add(fullName.substring(_solrCorePrefix.length())); 298 } 299 } 300 301 return coreNames; 302 } 303 304 /** 305 * Create a Solr core. 306 * @param name The name of the core to create. 307 * @throws IOException If an I/O error occurs. 308 * @throws SolrServerException If a Solr error occurs. 309 */ 310 public void createCore(String name) throws IOException, SolrServerException 311 { 312 String fullName = _solrCorePrefix + name; 313 String configsetName = _CONFIGSET_NAME_PREFIX + (_solrCorePrefix.endsWith("-") ? _solrCorePrefix.substring(0, _solrCorePrefix.length() - 1) : _solrCorePrefix); 314 _createConfigset(configsetName); 315 316 getLogger().info("Creating core '{}' (full name: '{}').", name, fullName); 317 318 SolrQuery query = new SolrQuery(); 319 query.setRequestHandler("/admin/cores"); 320 query.setParam("action", "CREATE"); 321 query.setParam("name", fullName); 322 query.setParam("configSet", configsetName); 323 query.setParam("property.ametys.url", _ametysInternalUrl); 324 325 QueryResponse response = _defaultSolrClient().query(query); 326 NamedList<?> results = response.getResponse(); 327 328 NamedList<?> error = (NamedList<?>) results.get("error"); 329 if (error != null) 330 { 331 throw new IOException("Error creating the core: " + error.get("msg")); 332 } 333 } 334 335 /** 336 * Updates the ametys.url property of the Solr cores. 337 */ 338 public void updateAmetysUrlCoreProperty() 339 { 340 Set<String> coreNames; 341 try 342 { 343 coreNames = getCoreNames(); 344 } 345 catch (SolrServerException | IOException e) 346 { 347 getLogger().error("Cannot get Solr core names. As a result, the internal Ametys URL could not be updated on Solr server.", e); 348 return; 349 } 350 351 for (String coreName : coreNames) 352 { 353 String collection = _solrClientProvider.getCollectionName(coreName); 354 SolrResponseBase response; 355 try 356 { 357 response = new UpdateCorePropertyRequest("ametys.url", _ametysInternalUrl).process(_getSolrClient(coreName), collection); 358 } 359 catch (SolrServerException | IOException e) 360 { 361 getLogger().error("'core.properties' file updating for workspace '{}' did not succeed as expected.", coreName, e); 362 continue; 363 } 364 365 NamedList<Object> responseParams = response.getResponse(); 366 if ("ok".equals(responseParams.get("result"))) 367 { 368 Boolean valueChanged = responseParams.getBooleanArg("valueChanged"); 369 if (valueChanged) 370 { 371 getLogger().info("'core.properties' file updated with the up-to-date Ametys URL for workspace '{}'", coreName); 372 } 373 else 374 { 375 getLogger().info("'core.properties' file already has the up-to-date Ametys URL for workspace '{}', it was not modified.", coreName); 376 } 377 } 378 else 379 { 380 getLogger().error("'core.properties' file updating for workspace '{}' did not succeed as expected.", coreName); 381 } 382 } 383 } 384 385 private void _createConfigset(String name) throws IOException, SolrServerException 386 { 387 getLogger().info("Creating (if necessary) configset '{}'", name); 388 389 // This request handler will check if configset exists. If not it will be created. 390 SolrQuery query = new SolrQuery(); 391 query.setRequestHandler("/admin/cores"); 392 query.setParam("action", "createConfigset"); 393 query.setParam("name", name); 394 395 QueryResponse response = _defaultSolrClient().query(query); 396 NamedList<?> results = response.getResponse(); 397 398 NamedList<?> error = (NamedList<?>) results.get("error"); 399 if (error != null) 400 { 401 throw new IOException("Error creating the core: " + error.get("msg")); 402 } 403 } 404 405 /** 406 * Delete a Solr core. 407 * @param name The name of the core to delete. 408 * @throws IOException If an I/O error occurs. 409 * @throws SolrServerException If a Solr error occurs. 410 */ 411 public void deleteCore(String name) throws IOException, SolrServerException 412 { 413 String fullName = _solrCorePrefix + name; 414 415 getLogger().info("Deleting core '{}' (full name: '{}').", name, fullName); 416 417 SolrQuery query = new SolrQuery(); 418 query.setRequestHandler("/admin/cores"); 419 query.setParam("action", "UNLOAD"); 420 query.setParam("core", fullName); 421 query.setParam("deleteInstanceDir", "true"); 422 423 QueryResponse response = _defaultSolrClient().query(query); 424 NamedList<?> results = response.getResponse(); 425 426 NamedList<?> error = (NamedList<?>) results.get("error"); 427 if (error != null) 428 { 429 throw new IOException("Error deleting core" + name + ": " + error.get("msg")); 430 } 431 } 432 433 /** 434 * Send the schema. 435 * @throws IOException If a communication error occurs. 436 * @throws SolrServerException If a solr error occurs. 437 */ 438 public void sendSchema() throws IOException, SolrServerException 439 { 440 getLogger().info("Computing and sending the schema to the solr server."); 441 442 String workspaceName = _workspaceSelector.getWorkspace(); 443 String collection = _solrClientProvider.getCollectionName(workspaceName); 444 SolrClient solrClient = _getSolrClient(workspaceName); 445 446// SchemaRepresentation staticSchema = _schemaHelper.getStaticSchema(); 447 SchemaRepresentation staticSchema = _schemaHelper.getSchema("resource://org/ametys/cms/search/solr/schema/schema.xml"); 448 449 // TODO Clear the schema except fields marked ametysReadOnly="true". 450 451 // Clear the current schema. 452 clearSchema(solrClient, collection); 453 454 SchemaRequest schemaRequest = new SchemaRequest(); 455 SchemaResponse schemaResponse = schemaRequest.process(solrClient, collection); 456 457 // The cleared schema contains only the basic fields which can't be deleted. 458 SchemaRepresentation clearedSchema = schemaResponse.getSchemaRepresentation(); 459 SchemaFields schemaFields = new SchemaFields(clearedSchema); 460 461 getLogger().debug("Schema after clear: \n{}", schemaFields.toString()); 462 463 // Add the static schema types and fields. 464 List<SchemaRequest.Update> updates = new ArrayList<>(); 465 466 // Set "add field" definitions from the static schema to the update list. 467 addStaticSchemaUpdates(updates, staticSchema, schemaFields); 468 469 getLogger().debug("Temporary schema after static add: \n{}", schemaFields.toString()); 470 471 // Set "add field" definitions from the static schema to the update list. 472 addCustomUpdates(updates, schemaFields); 473 474 SchemaRequest.MultiUpdate multiUpdate = new SchemaRequest.MultiUpdate(updates); 475 SchemaResponse.UpdateResponse updateResponse = multiUpdate.process(solrClient, collection); 476 477 getLogger().debug("Send schema response: {}", updateResponse.toString()); 478 Object errors = updateResponse.getResponse().get("errors"); 479 if (errors != null && errors instanceof List && !((List) errors).isEmpty()) 480 { 481 String msg = "An error occured with the sent schema to Solr, it contains errors:\n" + errors.toString(); 482 throw new SolrServerException(msg); 483 } 484 485 getLogger().info("Schema sent to the solr server."); 486 487 reloadCores(); 488 } 489 490 /** 491 * Compute the list of {@link Update} directives from the static schema. 492 * @param updates The list of {@link Update} directives to fill. 493 * @param staticSchema The static schema representation. 494 * @param schemaFields The current schema fields, used to track the existing fields (to be filled). 495 */ 496 protected void addStaticSchemaUpdates(List<SchemaRequest.Update> updates, SchemaRepresentation staticSchema, SchemaFields schemaFields) 497 { 498 List<FieldTypeDefinition> fieldTypes = staticSchema.getFieldTypes(); 499 for (FieldTypeDefinition fieldType : fieldTypes) 500 { 501 String name = (String) fieldType.getAttributes().get("name"); 502 if (!schemaFields.hasFieldType(name)) 503 { 504 updates.add(new SchemaRequest.AddFieldType(fieldType)); 505 schemaFields.addFieldType(name); 506 } 507 } 508 for (Map<String, Object> field : staticSchema.getFields()) 509 { 510 String name = (String) field.get("name"); 511 if (!schemaFields.hasField(name)) 512 { 513 updates.add(new SchemaRequest.AddField(field)); 514 schemaFields.addField(name); 515 } 516 } 517 for (Map<String, Object> field : staticSchema.getDynamicFields()) 518 { 519 String name = (String) field.get("name"); 520 if (!schemaFields.hasDynamicField(name)) 521 { 522 updates.add(new SchemaRequest.AddDynamicField(field)); 523 schemaFields.addDynamicField(name); 524 } 525 } 526 for (Map<String, Object> field : staticSchema.getCopyFields()) 527 { 528 String source = (String) field.get("source"); 529 String dest = (String) field.get("dest"); 530 if (!schemaFields.hasCopyField(source, dest)) 531 { 532 updates.add(new SchemaRequest.AddCopyField(source, Arrays.asList(dest))); 533 schemaFields.addCopyField(source, dest); 534 } 535 } 536 } 537 538 /** 539 * Compute the list of custom {@link Update} directives. 540 * @param updates The list of {@link Update} directives to fill. 541 * @param schemaFields The current schema fields, used to track the existing fields (to be filled). 542 */ 543 protected void addCustomUpdates(List<SchemaRequest.Update> updates, SchemaFields schemaFields) 544 { 545 // Add all our property-managed fields. 546 for (String providerId : _schemaDefProviderEP.getExtensionsIds()) 547 { 548 SchemaDefinitionProvider definitionProvider = _schemaDefProviderEP.getExtension(providerId); 549 550 for (SchemaDefinition definition : definitionProvider.getDefinitions()) 551 { 552 if (!definitionExists(definition, schemaFields)) 553 { 554 SchemaRequest.Update update = getSchemaUpdate(definition); 555 if (update != null) 556 { 557 updates.add(update); 558 } 559 } 560 } 561 } 562 563// for (String propId : _sysPropEP.getExtensionsIds()) 564// { 565// Collection<SchemaDefinition> definitions = _sysPropEP.getExtension(propId).getSchemaDefinitions(); 566// 567// for (SchemaDefinition definition : definitions) 568// { 569// if (!definitionExists(definition, schemaFields)) 570// { 571// SchemaRequest.Update update = getSchemaUpdate(definition); 572// if (update != null) 573// { 574// updates.add(update); 575// } 576// } 577// } 578// } 579 } 580 581 /** 582 * Test if the given schema definition exists in the given {@link SchemaFields} reference. 583 * @param definition The schema definition to test. 584 * @param schemaFields The current schema fields. 585 * @return true if the SchemaFields contain the schema definition. 586 */ 587 protected boolean definitionExists(SchemaDefinition definition, SchemaFields schemaFields) 588 { 589 if (definition instanceof FieldDefinition) 590 { 591 FieldDefinition fieldDef = (FieldDefinition) definition; 592 if (fieldDef.isDynamic()) 593 { 594 return schemaFields.hasField(fieldDef.getName()); 595 } 596 else 597 { 598 return schemaFields.hasDynamicField(fieldDef.getName()); 599 } 600 } 601 else if (definition instanceof CopyFieldDefinition) 602 { 603 CopyFieldDefinition fieldDef = (CopyFieldDefinition) definition; 604 return schemaFields.hasCopyField(fieldDef.getSource(), fieldDef.getDestination()); 605 } 606 607 return false; 608 } 609 610 /** 611 * Delete all the fields of the existing schema in the given collection. 612 * @param solrClient The Solr client 613 * @param collection The collection. 614 * @throws IOException If a communication error occurs. 615 * @throws SolrServerException If a solr error occurs. 616 */ 617 protected void clearSchema(SolrClient solrClient, String collection) throws IOException, SolrServerException 618 { 619 try 620 { 621 getLogger().info("Clearing the existing schema on the solr server."); 622 623 SchemaRequest schemaRequest = new SchemaRequest(); 624 SchemaResponse schemaResponse = schemaRequest.process(solrClient, collection); 625 626 SchemaRepresentation schema = schemaResponse.getSchemaRepresentation(); 627 628 List<SchemaRequest.Update> deletions = new ArrayList<>(); 629 630 // First the copy fields, then dynamic and simple fields, and field types in the end. 631 for (Map<String, Object> field : schema.getCopyFields()) 632 { 633 String source = (String) field.get("source"); 634 String dest = (String) field.get("dest"); 635 deletions.add(new SchemaRequest.DeleteCopyField(source, Arrays.asList(dest))); 636 } 637 for (Map<String, Object> field : schema.getDynamicFields()) 638 { 639 String name = (String) field.get("name"); 640 deletions.add(new SchemaRequest.DeleteDynamicField(name)); 641 } 642 for (Map<String, Object> field : schema.getFields()) 643 { 644 String name = (String) field.get("name"); 645 if (!_READ_ONLY_FIELDS.contains(name)) 646 { 647 deletions.add(new SchemaRequest.DeleteField(name)); 648 } 649 } 650 for (FieldTypeDefinition fieldType : schema.getFieldTypes()) 651 { 652 String name = (String) fieldType.getAttributes().get("name"); 653 if (!_READ_ONLY_FIELDTYPES.contains(name)) 654 { 655 deletions.add(new SchemaRequest.DeleteFieldType(name)); 656 } 657 } 658 659 SchemaRequest.MultiUpdate multiUpdate = new SchemaRequest.MultiUpdate(deletions); 660 SchemaResponse.UpdateResponse updateResponse = multiUpdate.process(solrClient, collection); 661 662 getLogger().debug("Clear schema response: {}", updateResponse.toString()); 663 664 getLogger().info("Solr schema cleared."); 665 } 666 catch (SolrServerException | IOException e) 667 { 668 getLogger().error("Error clearing schema in collection " + collection, e); 669 throw e; 670 } 671 } 672 673 /** 674 * Get the schema {@link Update} directive from the given schema definition. 675 * @param definition The schema definition to add. 676 * @return The add {@link Update} directive. 677 */ 678 protected SchemaRequest.Update getSchemaUpdate(SchemaDefinition definition) 679 { 680 SchemaRequest.Update update = null; 681 682 if (definition instanceof FieldDefinition) 683 { 684 FieldDefinition fieldDef = (FieldDefinition) definition; 685 686 // Force indexed and stored attributes to true. 687 Map<String, Object> attributes = new HashMap<>(); 688 attributes.put("name", fieldDef.getName()); 689 attributes.put("type", fieldDef.getType()); 690 attributes.put("multiValued", fieldDef.isMultiValued()); 691 attributes.put("docValues", fieldDef.isDocValues()); 692 attributes.put("indexed", Boolean.TRUE); 693 attributes.put("stored", Boolean.TRUE); 694 695 update = new SchemaRequest.AddField(attributes); 696 } 697 else if (definition instanceof CopyFieldDefinition) 698 { 699 CopyFieldDefinition fieldDef = (CopyFieldDefinition) definition; 700 701 String source = fieldDef.getSource(); 702 String dest = fieldDef.getDestination(); 703 704 update = new SchemaRequest.AddCopyField(source, Arrays.asList(dest)); 705 } 706 707 return update; 708 } 709 710 /** 711 * Reload the solr cores. 712 * @throws IOException If a communication error occurs. 713 * @throws SolrServerException If a solr error occurs. 714 */ 715 protected void reloadCores() throws IOException, SolrServerException 716 { 717 getLogger().info("Reloading solr cores."); 718 719 for (String coreName : getCoreNames()) 720 { 721 String fullName = _solrCorePrefix + coreName; 722 723 CoreAdminResponse reloadResponse = CoreAdminRequest.reloadCore(fullName, _defaultSolrClient()); 724 725 getLogger().debug("Reload core response: {}", reloadResponse.toString()); 726 } 727 728 getLogger().info("All cores reloaded."); 729 } 730 731 /** 732 * Reloads the ACL Solr cache for all users 733 * @throws IOException If an I/O error occurs. 734 * @throws SolrServerException If a Solr error occurs. 735 * @throws RepositoryException If a repository exception occurs when retrieving workspaces. 736 */ 737 public void reloadAclCache() throws IOException, SolrServerException, RepositoryException 738 { 739 String[] workspaceNames = _workspaceSelector.getWorkspaces(); 740 for (String workspaceName : workspaceNames) 741 { 742 reloadAclCache(workspaceName); 743 } 744 } 745 746 /** 747 * Reloads the ACL Solr cache for all users 748 * @param workspaceName The workspace name 749 * @throws IOException If an I/O error occurs. 750 * @throws SolrServerException If a Solr error occurs. 751 */ 752 public void reloadAclCache(String workspaceName) throws IOException, SolrServerException 753 { 754 reloadAclCache(workspaceName, false); 755 } 756 757 /** 758 * Reloads the ACL Solr cache for all users 759 * @param workspaceName The workspace name 760 * @param checkIfNecessary true to check if the reload is necessary for each segment (i.e. reload only the segments not already in cache) 761 * @throws IOException If an I/O error occurs. 762 * @throws SolrServerException If a Solr error occurs. 763 */ 764 public void reloadAclCache(String workspaceName, boolean checkIfNecessary) throws IOException, SolrServerException 765 { 766 getLogger().info("Reloading read ACL Solr cache for workspace '{}'", workspaceName); 767 768 String collection = _solrClientProvider.getCollectionName(workspaceName); 769 SolrResponseBase responseBase = new ReloadAclCacheRequest(checkIfNecessary).process(_getSolrClient(workspaceName), collection); 770 NamedList<Object> responseObj = responseBase.getResponse(); 771 772 if ("ok".equals(responseObj.get("result"))) 773 { 774 getLogger().info("Read-ACL Solr cache reloaded for workspace '{}'", workspaceName); 775 } 776 else 777 { 778 Object error = responseObj.get("error"); 779 getLogger().error("The reloading of Read-ACL Solr Cache for workspace '{}' did not succeed as expected.\n Error code is the following: {}", workspaceName, error); 780 } 781 } 782 783 /** 784 * Index all the contents in a given workspace. 785 * @param workspaceName the workspace where to index 786 * @param indexAttachments to index content attachments 787 * @param commit true to commit 788 * @return The indexation result as a Map. 789 * @throws Exception if an error occurs while indexing. 790 */ 791 public Map<String, Object> indexAllContents(String workspaceName, boolean indexAttachments, boolean commit) throws Exception 792 { 793 Request request = ContextHelper.getRequest(_context); 794 795 // Retrieve the current workspace. 796 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 797 798 try 799 { 800 // Force the workspace. 801 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 802 803 getLogger().info("Starting the indexation of all contents for workspace {}", workspaceName); 804 805 long start = System.currentTimeMillis(); 806 807 // Delete all contents 808 unindexAllContents(workspaceName, indexAttachments, commit); 809 810 String query = ContentQueryHelper.getContentXPathQuery(null); 811 AmetysObjectIterable<Content> contents = _resolver.query(query); 812 813 IndexationResult result = doIndexContents(contents, workspaceName, indexAttachments, commit); 814 815 long end = System.currentTimeMillis(); 816 817 if (!result.hasErrors()) 818 { 819 getLogger().info("{} contents indexed without error in {} milliseconds.", result.getSuccessCount(), end - start); 820 } 821 else 822 { 823 getLogger().info("Content indexation ended, the process took {} milliseconds. {} contents were not indexed successfully, please review the error logs above for more details.", end - start, result.getErrorCount()); 824 } 825 826 Map<String, Object> results = new HashMap<>(); 827 results.put("successCount", result.getSuccessCount()); 828 if (result.hasErrors()) 829 { 830 results.put("errorCount", result.getErrorCount()); 831 } 832 833 return results; 834 } 835 catch (Exception e) 836 { 837 String error = String.format("Failed to index all contents in workspace %s", workspaceName); 838 getLogger().error(error, e); 839 throw new IndexingException(error, e); 840 } 841 finally 842 { 843 // Restore context 844 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 845 } 846 } 847 848 /** 849 * Unindex all content documents. 850 * @param workspaceName The workspace name 851 * @param unindexAttachments also unindex content attachments 852 * @param commit true to commit 853 * @throws Exception if an error occurs while unindexing. 854 */ 855 protected void unindexAllContents(String workspaceName, boolean unindexAttachments, boolean commit) throws Exception 856 { 857 String collection = _solrClientProvider.getCollectionName(workspaceName); 858 859 Query contents = new DocumentTypeQuery(SolrFieldNames.TYPE_CONTENT); 860 Query query; 861 if (unindexAttachments) 862 { 863 query = new OrQuery(new DocumentTypeQuery(SolrFieldNames.TYPE_CONTENT_RESOURCE), contents); 864 } 865 else 866 { 867 query = contents; 868 } 869 _getSolrClient(workspaceName).deleteByQuery(collection, query.build()); 870 871 if (commit) 872 { 873 commit(workspaceName); 874 } 875 } 876 877 /** 878 * Add or update the child contents of a {@link AmetysObjectCollection} into Solr index, for all workspaces and commit 879 * @param collectionId The id of collection 880 * @param indexAttachments to index content attachments 881 * @throws Exception if an error occurs while indexing. 882 */ 883 public void indexSubcontents(String collectionId, boolean indexAttachments) throws Exception 884 { 885 String[] workspaceNames = _workspaceSelector.getWorkspaces(); 886 for (String workspaceName : workspaceNames) 887 { 888 indexSubcontents(collectionId, workspaceName, indexAttachments, true); 889 } 890 } 891 892 /** 893 * Index the child contents of a {@link AmetysObjectCollection} 894 * @param collectionId The id of collection 895 * @param workspaceName the workspace where to index 896 * @param indexAttachments to index content attachments 897 * @param commit true to commit 898 * @throws Exception if an error occurs while unindexing. 899 */ 900 public void indexSubcontents(String collectionId, String workspaceName, boolean indexAttachments, boolean commit) throws Exception 901 { 902 Request request = ContextHelper.getRequest(_context); 903 904 // Retrieve the current workspace. 905 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 906 907 try 908 { 909 // Force the workspace. 910 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 911 912 if (_resolver.hasAmetysObjectForId(collectionId)) 913 { 914 AmetysObjectCollection collection = _resolver.resolveById(collectionId); 915 AmetysObjectIterable<AmetysObject> children = collection.getChildren(); 916 917 for (AmetysObject child : children) 918 { 919 if (child instanceof Content) 920 { 921 Content content = (Content) child; 922 923 _doIndexContent(content, workspaceName, indexAttachments, false); 924 } 925 } 926 927 if (commit) 928 { 929 commit(workspaceName); 930 } 931 } 932 } 933 finally 934 { 935 // Restore context 936 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 937 } 938 } 939 940 /** 941 * Add or update a content into Solr index on all workspaces and commit 942 * @param contentId The id of the content to index 943 * @param indexAttachments to index content attachments 944 * @throws Exception if an error occurs while indexing. 945 */ 946 public void indexContent(String contentId, boolean indexAttachments) throws Exception 947 { 948 String[] workspaceNames = _workspaceSelector.getWorkspaces(); 949 for (String workspaceName : workspaceNames) 950 { 951 indexContent(contentId, workspaceName, indexAttachments, true); 952 } 953 } 954 955 /** 956 * Add or update a content into Solr index 957 * @param contentId The id of the content to index 958 * @param workspaceName the workspace where to index 959 * @param indexAttachments to index content attachments 960 * @param commit true to commit the indexation 961 * @throws Exception if an error occurs while indexing. 962 */ 963 public void indexContent(String contentId, String workspaceName, boolean indexAttachments, boolean commit) throws Exception 964 { 965 Request request = ContextHelper.getRequest(_context); 966 967 // Retrieve the current workspace. 968 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 969 970 try 971 { 972 // Force the workspace. 973 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 974 975 if (_resolver.hasAmetysObjectForId(contentId)) 976 { 977 Content content = _resolver.resolveById(contentId); 978 _doIndexContent(content, workspaceName, indexAttachments, commit); 979 } 980 } 981 finally 982 { 983 // Restore context 984 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 985 } 986 } 987 988 private void _doIndexContent(Content content, String workspaceName, boolean indexAttachments, boolean commit) throws IndexingException 989 { 990 try 991 { 992 long time_0 = System.currentTimeMillis(); 993 994 getLogger().debug("Indexing content {} into Solr for workspace {}", content.getId(), workspaceName); 995 996 deleteRepeaterDocs(content.getId(), workspaceName); 997 doIndexContent(content, workspaceName); 998 doIndexContentWorkflow(content, workspaceName, false); 999 if (indexAttachments) 1000 { 1001 indexContentAttachments(content.getRootAttachments(), content); 1002 } 1003 1004 if (commit) 1005 { 1006 commit(workspaceName); 1007 } 1008 1009 getLogger().debug("Successfully indexed content {} in Solr in {} ms", content.getId(), System.currentTimeMillis() - time_0); 1010 } 1011 catch (Exception e) 1012 { 1013 String error = String.format("Failed to index content %s in workspace %s", content.getId(), workspaceName); 1014 getLogger().error(error, e); 1015 throw new IndexingException(error, e); 1016 } 1017 } 1018 1019 /** 1020 * Send a collection of contents for indexation in the solr server on all workspaces and commit 1021 * @param contents the collection of contents to index. 1022 * @return the indexation result. 1023 * @throws Exception if an error occurs while indexing. 1024 */ 1025 public IndexationResult indexContents(Iterable<Content> contents) throws Exception 1026 { 1027 IndexationResult result = new IndexationResult(); 1028 1029 String[] workspaceNames = _workspaceSelector.getWorkspaces(); 1030 for (String workspaceName : workspaceNames) 1031 { 1032 IndexationResult wResult = indexContents(contents, workspaceName, true, true); 1033 result = new IndexationResult(result.getSuccessCount() + wResult.getSuccessCount(), result.getErrorCount() + wResult.getErrorCount()); 1034 } 1035 1036 return result; 1037 } 1038 1039 /** 1040 * Send a collection of contents for indexation in the solr server. 1041 * @param contents the collection of contents to index. 1042 * @param workspaceName the workspace where to index 1043 * @param indexAttachments to index content attachments 1044 * @param commit true to commit the indexation 1045 * @return the indexation result. 1046 * @throws Exception if an error occurs while indexing. 1047 */ 1048 public IndexationResult indexContents(Iterable<Content> contents, String workspaceName, boolean indexAttachments, boolean commit) throws Exception 1049 { 1050 Request request = ContextHelper.getRequest(_context); 1051 1052 // Retrieve the current workspace. 1053 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1054 1055 try 1056 { 1057 // Force the workspace. 1058 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 1059 1060 getLogger().info("Starting indexation of several contents for workspace {}", workspaceName); 1061 1062 long start = System.currentTimeMillis(); 1063 1064 IndexationResult result = doIndexContents(contents, workspaceName, indexAttachments, commit); 1065 1066 long end = System.currentTimeMillis(); 1067 1068 if (!result.hasErrors()) 1069 { 1070 getLogger().info("{} contents indexed without error in {} milliseconds.", result.getSuccessCount(), end - start); 1071 } 1072 else 1073 { 1074 getLogger().info("Content indexation ended, the process took {} milliseconds. {} contents were not indexed successfully, please review the error logs above for more details.", end - start, result.getErrorCount()); 1075 } 1076 1077 return result; 1078 } 1079 catch (Exception e) 1080 { 1081 String error = String.format("Failed to index several contents in workspace %s", workspaceName); 1082 getLogger().error(error, e); 1083 throw new IndexingException(error, e); 1084 } 1085 finally 1086 { 1087 // Restore context 1088 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 1089 } 1090 1091 } 1092 1093 /** 1094 * Send some contents for indexation in the solr server. 1095 * @param contents the contents to index. 1096 * @param workspaceName The workspace name 1097 * @param indexAttachments to index content attachments 1098 * @param commit true to commit transaction 1099 * @return the indexation result. 1100 * @throws Exception if an error occurs committing the results. 1101 */ 1102 protected IndexationResult doIndexContents(Iterable<Content> contents, String workspaceName, boolean indexAttachments, boolean commit) throws Exception 1103 { 1104 int successCount = 0; 1105 int errorCount = 0; 1106 for (Content content : contents) 1107 { 1108 try 1109 { 1110 doIndexContent(content, workspaceName); 1111 doIndexContentWorkflow(content, workspaceName, false); 1112 if (indexAttachments) 1113 { 1114 indexContentAttachments(content.getRootAttachments(), content); 1115 } 1116 successCount++; 1117 } 1118 catch (Exception e) 1119 { 1120 getLogger().error("Error indexing content '" + content.getId() + "' in the solr server.", e); 1121 errorCount++; 1122 } 1123 } 1124 1125 if (commit) 1126 { 1127 commit(workspaceName); 1128 } 1129 1130 return new IndexationResult(successCount, errorCount); 1131 } 1132 1133 /** 1134 * Update the value of a specific system property in a content document. 1135 * @param content The content to update. 1136 * @param propertyId The system property ID. 1137 * @param workspaceName The workspace name 1138 * @param commit true to commit update 1139 * @throws Exception if an error occurs while indexing. 1140 */ 1141 public void updateSystemProperty(Content content, String propertyId, String workspaceName, boolean commit) throws Exception 1142 { 1143 getLogger().debug("Updating the property '{}' for content {} into Solr.", propertyId, content); 1144 1145 SolrInputDocument document = new SolrInputDocument(); 1146 boolean hasUpdate = _solrContentIndexer.indexPartialSystemProperty(content, propertyId, document); 1147 1148 if (!hasUpdate) 1149 { 1150 getLogger().debug("Did not index '{}' property for content {} in Solr because no update to apply.", propertyId, content); 1151 return; 1152 } 1153 1154 String collection = _solrClientProvider.getCollectionName(workspaceName); 1155 UpdateResponse solrResponse = _getSolrClient(workspaceName).add(collection, document); 1156 int status = solrResponse.getStatus(); 1157 1158 if (status != 0) 1159 { 1160 throw new IOException("Indexing of property '" + propertyId + "': got status code '" + status + "'."); 1161 } 1162 1163 if (commit) 1164 { 1165 commit(workspaceName); 1166 } 1167 1168 getLogger().debug("Succesfully indexed '{}' property for content {} in Solr.", propertyId, content); 1169 } 1170 1171 /** 1172 * Remove a content from Solr index for all workspaces and commit 1173 * @param contentId The id of content to unindex 1174 * @param unindexAttachments also unindex content attachments 1175 * @throws Exception if an error occurs while indexing. 1176 */ 1177 public void unindexContent(String contentId, boolean unindexAttachments) throws Exception 1178 { 1179 String[] workspaceNames = _workspaceSelector.getWorkspaces(); 1180 for (String workspaceName : workspaceNames) 1181 { 1182 unindexContent(contentId, workspaceName, unindexAttachments, true); 1183 } 1184 } 1185 1186 /** 1187 * Remove a content from Solr index 1188 * @param contentId The id of content to unindex 1189 * @param workspaceName The workspace where to work in 1190 * @param unindexAttachments also unindex content attachments 1191 * @param commit true to commit operation 1192 * @throws Exception if an error occurs while indexing. 1193 */ 1194 public void unindexContent(String contentId, String workspaceName, boolean unindexAttachments, boolean commit) throws Exception 1195 { 1196 Request request = ContextHelper.getRequest(_context); 1197 1198 // Retrieve the current workspace. 1199 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1200 1201 try 1202 { 1203 // Force the workspace. 1204 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 1205 1206 getLogger().debug("Unindexing content {} from Solr for workspace {}", contentId, workspaceName); 1207 1208 deleteRepeaterDocs(contentId, workspaceName); 1209 doUnindexDocument(contentId, workspaceName); 1210 if (unindexAttachments) 1211 { 1212 doUnindexContentAttachments(contentId, workspaceName); 1213 } 1214 _solrWorkflowIndexer.unindexAmetysObjectWorkflow(contentId, workspaceName, false); 1215 1216 if (commit) 1217 { 1218 commit(workspaceName); 1219 } 1220 1221 getLogger().debug("Succesfully deleted content {} from Solr.", contentId); 1222 } 1223 catch (Exception e) 1224 { 1225 String error = String.format("Failed to unindex content %s in workspace %s", contentId, workspaceName); 1226 getLogger().error(error, e); 1227 throw new IndexingException(error, e); 1228 } 1229 finally 1230 { 1231 // Restore workspace 1232 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 1233 } 1234 } 1235 1236 /** 1237 * Remove a content from Solr index for all workspaces and commit 1238 * @param contentIds The id of content to unindex 1239 * @throws Exception if an error occurs while indexing. 1240 */ 1241 public void unindexContents(Collection<String> contentIds) throws Exception 1242 { 1243 String[] workspaceNames = _workspaceSelector.getWorkspaces(); 1244 for (String workspaceName : workspaceNames) 1245 { 1246 unindexContents(contentIds, workspaceName, true); 1247 } 1248 } 1249 1250 /** 1251 * Remove a content from Solr index 1252 * @param contentIds The id of content to unindex 1253 * @param workspaceName The workspace where to work in 1254 * @param commit true to commit 1255 * @throws Exception if an error occurs while indexing. 1256 */ 1257 public void unindexContents(Collection<String> contentIds, String workspaceName, boolean commit) throws Exception 1258 { 1259 Request request = ContextHelper.getRequest(_context); 1260 1261 // Retrieve the current workspace. 1262 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1263 1264 try 1265 { 1266 // Force the workspace. 1267 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 1268 1269 getLogger().debug("Unindexing several contents from Solr."); 1270 1271 for (String contentId : contentIds) 1272 { 1273 deleteRepeaterDocs(contentId, workspaceName); 1274 doUnindexDocument(contentId, workspaceName); 1275 _solrWorkflowIndexer.unindexAmetysObjectWorkflow(contentId, workspaceName, false); 1276 } 1277 1278 if (commit) 1279 { 1280 commit(workspaceName); 1281 } 1282 1283 getLogger().debug("Succesfully unindexed content from Solr."); 1284 } 1285 catch (Exception e) 1286 { 1287 String error = String.format("Failed to unindex several contents in workspace %s", workspaceName); 1288 getLogger().error(error, e); 1289 throw new IndexingException(error, e); 1290 } 1291 finally 1292 { 1293 // Restore workspace 1294 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 1295 } 1296 } 1297 1298 /** 1299 * Add or update a content into Solr index 1300 * @param content The content to index 1301 * @param workspaceName The workspace where to index 1302 * @throws Exception if an error occurs while indexing. 1303 */ 1304 protected void doIndexContent(Content content, String workspaceName) throws Exception 1305 { 1306 long time_0 = System.currentTimeMillis(); 1307 1308 SolrInputDocument document = new SolrInputDocument(); 1309 List<SolrInputDocument> additionalDocuments = new ArrayList<>(); 1310 1311 _solrContentIndexer.indexContent(content, document, additionalDocuments); 1312 1313 long time_1 = System.currentTimeMillis(); 1314 getLogger().debug("Populate indexing fields for content {} for workspace {} in {} ms", content.getId(), workspaceName, time_1 - time_0); 1315 1316 // Indexation of AmetysObject property 1317 document.addField(SolrFieldNames.IS_AMETYS_OBJECT, true); 1318 1319 long time_2 = System.currentTimeMillis(); 1320 getLogger().debug("Populate ACL for content {} for workspace {} in {} ms", content.getId(), workspaceName, time_2 - time_1); 1321 1322 List<SolrInputDocument> documents = new ArrayList<>(additionalDocuments); 1323 documents.add(document); 1324 1325 UpdateResponse solrResponse = _getSolrClient(workspaceName).add(_solrClientProvider.getCollectionName(workspaceName), documents); 1326 int status = solrResponse.getStatus(); 1327 1328 long time_3 = System.currentTimeMillis(); 1329 getLogger().debug("Update document for content {} for workspace {} in {} ms", content.getId(), workspaceName, time_3 - time_2); 1330 1331 if (status != 0) 1332 { 1333 throw new IOException("Content indexation: got status code '" + status + "'."); 1334 } 1335 } 1336 1337 /** 1338 * Index the whole workflow of a content. 1339 * @param content The content. 1340 * @param workspaceName The workspace name 1341 * @param commit true to commit, false otherwise. 1342 * @throws Exception if an error occurs while indexing. 1343 */ 1344 protected void doIndexContentWorkflow(Content content, String workspaceName, boolean commit) throws Exception 1345 { 1346 if (content instanceof WorkflowAwareContent) 1347 { 1348 _solrWorkflowIndexer.indexAmetysObjectWorkflow((WorkflowAwareContent) content, workspaceName, commit); 1349 } 1350 } 1351 1352 /** 1353 * Index content attachments as new entries in the idnex 1354 * @param collection the collection of attachments 1355 * @param content the content whose attachments will be indexed 1356 * @throws Exception if something goes wrong when indexing the attachments of the content 1357 */ 1358 public void indexContentAttachments(ResourceCollection collection, Content content) throws Exception 1359 { 1360 if (collection == null) 1361 { 1362 return; 1363 } 1364 1365 for (AmetysObject object : collection.getChildren()) 1366 { 1367 if (object instanceof ResourceCollection) 1368 { 1369 indexContentAttachments((ResourceCollection) object, content); 1370 } 1371 else if (object instanceof Resource) 1372 { 1373 Resource resource = (Resource) object; 1374 indexContentAttachment(resource, content); 1375 } 1376 } 1377 } 1378 1379 /** 1380 * Index a content attachment 1381 * @param resource the content attachment as a {@link Resource} 1382 * @param content the content whose attachment is going to be indexed 1383 * @throws Exception if something goes wrong when processing the indexation of the content attachment 1384 */ 1385 public void indexContentAttachment(Resource resource, Content content) throws Exception 1386 { 1387 SolrInputDocument document = new SolrInputDocument(); 1388 1389 // Prepare resource doc 1390 _indexContentAttachment(resource, document, content); 1391 1392 // Indexation of the document 1393 _indexResourceDocument(resource, document); 1394 } 1395 1396 private void _indexContentAttachment(Resource resource, SolrInputDocument document, Content content) throws Exception 1397 { 1398 String language = content.getLanguage(); 1399 1400 _solrResourceIndexer.indexResource(resource, document, SolrFieldNames.TYPE_CONTENT_RESOURCE, language); 1401 1402 // Need the id of the content for unindexing attachment during the unindexing of the content 1403 document.addField(SolrFieldNames.ATTACHMENT_CONTENT_ID, content.getId()); 1404 } 1405 1406 /** 1407 * Index a populated solr input document of type Resource. 1408 * @param resource the resource from which the input document is created 1409 * @param document the input document 1410 * @throws SolrServerException if there is an error on the server 1411 * @throws IOException if there is a communication error with the server 1412 */ 1413 protected void _indexResourceDocument(Resource resource, SolrInputDocument document) throws SolrServerException, IOException 1414 { 1415 String resourceId = resource.getId(); 1416 1417 // Retrieve appropriate solr client 1418 Request request = ContextHelper.getRequest(_context); 1419 String workspaceName = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1420 String collectionName = _solrClientProvider.getCollectionName(workspaceName); 1421 SolrClient solrClient = _solrClientProvider.getUpdateClient(workspaceName); 1422 1423 // Add document 1424 UpdateResponse solrResponse = solrClient.add(collectionName, document); 1425 int status = solrResponse.getStatus(); 1426 1427 if (status != 0) 1428 { 1429 throw new IOException("Ametys resource indexing - Expecting status code of '0' in the Solr response but got : '" + status + "'. Resource id : " + resourceId); 1430 } 1431 1432 getLogger().debug("Successful resource indexing. Resource identifier : {}", resourceId); 1433 } 1434 1435 /** 1436 * Delete repeater documents of a specified content. 1437 * @param workspaceName The workspace name 1438 * @param contentId the content ID. 1439 * @throws Exception if an error occurs while indexing. 1440 */ 1441 protected void deleteRepeaterDocs(String contentId, String workspaceName) throws Exception 1442 { 1443 long time_0 = System.currentTimeMillis(); 1444 1445 // _documentType:repeater AND id:content\://xxx/* 1446 StringBuilder query = new StringBuilder(); 1447 query.append(SolrFieldNames.DOCUMENT_TYPE).append(':').append(SolrFieldNames.TYPE_REPEATER) 1448 .append(" AND id:").append(ClientUtils.escapeQueryChars(contentId)).append("/*"); 1449 1450 _getSolrClient(workspaceName).deleteByQuery(_solrClientProvider.getCollectionName(workspaceName), query.toString()); 1451 1452 getLogger().debug("Successfully delete repeaters documents for content {} in {} ms", contentId, System.currentTimeMillis() - time_0); 1453 } 1454 1455 /** 1456 * Index all the resources in a given workspace. 1457 * @param workspaceName The workspace where to index 1458 * @param commit true to commit indexation 1459 * @return The indexation result as a Map. 1460 * @throws Exception if an error occurs while indexing. 1461 */ 1462 public Map<String, Object> indexAllResources(String workspaceName, boolean commit) throws Exception 1463 { 1464 Request request = ContextHelper.getRequest(_context); 1465 1466 // Retrieve the current workspace. 1467 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1468 1469 try 1470 { 1471 // Force the workspace. 1472 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 1473 1474 getLogger().info("Starting the indexation of all resources for workspace {}", workspaceName); 1475 1476 long start = System.currentTimeMillis(); 1477 1478 // Delete all resources 1479 unindexAllResources(workspaceName, commit); 1480 1481 Map<String, Object> results = new HashMap<>(); 1482 1483 try 1484 { 1485 TraversableAmetysObject resourceRoot = _resolver.resolveByPath(RepositoryConstants.NAMESPACE_PREFIX + ":resources"); 1486 AmetysObjectIterable<Resource> resources = resourceRoot.getChildren(); 1487 1488 IndexationResult result = doIndexResources(resources, SolrFieldNames.TYPE_RESOURCE, resourceRoot, workspaceName, commit); 1489 1490 long end = System.currentTimeMillis(); 1491 1492 if (!result.hasErrors()) 1493 { 1494 getLogger().info("{} resources indexed without error in {} milliseconds.", result.getSuccessCount(), end - start); 1495 } 1496 else 1497 { 1498 getLogger().info("Resource indexation ended, the process took {} milliseconds. {} resources were not indexed successfully, please review the error logs above for more details.", end - start, result.getErrorCount()); 1499 } 1500 1501 results.put("successCount", result.getSuccessCount()); 1502 if (result.hasErrors()) 1503 { 1504 results.put("errorCount", result.getErrorCount()); 1505 } 1506 } 1507 catch (UnknownAmetysObjectException e) 1508 { 1509 getLogger().info("There is no root for resources in current workspace."); 1510 } 1511 1512 return results; 1513 } 1514 catch (Exception e) 1515 { 1516 String error = String.format("Failed to index all resources in workspace %s", workspaceName); 1517 getLogger().error(error, e); 1518 throw new IndexingException(error, e); 1519 } 1520 finally 1521 { 1522 // Restore context 1523 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 1524 } 1525 } 1526 1527 /** 1528 * Send a collection of contents for indexation in the solr server. 1529 * @param resources the collection of contents to index. 1530 * @param documentType The document type of the resource 1531 * @param workspaceName The workspace where to index 1532 * @param commit true to commit indexation 1533 * @return the indexation result. 1534 * @throws Exception if an error occurs while indexing. 1535 */ 1536 public IndexationResult indexResources(Iterable<AmetysObject> resources, String documentType, String workspaceName, boolean commit) throws Exception 1537 { 1538 return indexResources(resources, documentType, null, workspaceName, commit); 1539 } 1540 1541 /** 1542 * Send a collection of contents for indexation in the solr server. 1543 * @param resources the collection of contents to index. 1544 * @param documentType The document type of the resource 1545 * @param resourceRoot The resource root, can be null. In that case, it will be computed for each resource. 1546 * @param workspaceName The workspace where to index 1547 * @param commit true to commit indexation 1548 * @return the indexation result. 1549 * @throws Exception if an error occurs while indexing. 1550 */ 1551 public IndexationResult indexResources(Iterable<? extends AmetysObject> resources, String documentType, TraversableAmetysObject resourceRoot, String workspaceName, boolean commit) throws Exception 1552 { 1553 Request request = ContextHelper.getRequest(_context); 1554 1555 // Retrieve the current workspace. 1556 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1557 1558 try 1559 { 1560 // Force the workspace. 1561 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 1562 1563 getLogger().info("Starting indexation of several resources for workspace {}", workspaceName); 1564 1565 long start = System.currentTimeMillis(); 1566 1567 IndexationResult result = doIndexResources(resources, documentType, resourceRoot, workspaceName, commit); 1568 1569 long end = System.currentTimeMillis(); 1570 1571 if (!result.hasErrors()) 1572 { 1573 getLogger().info("{} resources indexed without error in {} milliseconds.", result.getSuccessCount(), end - start); 1574 } 1575 else 1576 { 1577 getLogger().info("Resource indexation ended, the process took {} milliseconds. {} resources were not indexed successfully, please review the error logs above for more details.", end - start, result.getErrorCount()); 1578 } 1579 1580 return result; 1581 } 1582 catch (Exception e) 1583 { 1584 String error = String.format("Failed to index several resources in workspace %s", workspaceName); 1585 getLogger().error(error, e); 1586 throw new IndexingException(error, e); 1587 } 1588 finally 1589 { 1590 // Restore context 1591 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 1592 } 1593 } 1594 1595 /** 1596 * Send some resources for indexation in the solr server. 1597 * @param resources the resources to index. 1598 * @param documentType The document type of the resource 1599 * @param resourceRoot The resource root, can be null. In that case, it will be computed for each resource. 1600 * @param workspaceName The workspace where to index 1601 * @param commit true to commit indexation 1602 * @return the indexation result. 1603 * @throws Exception if an error occurs committing the results. 1604 */ 1605 protected IndexationResult doIndexResources(Iterable<? extends AmetysObject> resources, String documentType, TraversableAmetysObject resourceRoot, String workspaceName, boolean commit) throws Exception 1606 { 1607 int successCount = 0; 1608 int errorCount = 0; 1609 for (AmetysObject resource : resources) 1610 { 1611 try 1612 { 1613 doIndexExplorerItem(resource, documentType, resourceRoot); 1614 successCount++; 1615 } 1616 catch (Exception e) 1617 { 1618 getLogger().error("Error indexing resource '" + resource.getId() + "' in the solr server.", e); 1619 errorCount++; 1620 } 1621 } 1622 1623 if (commit) 1624 { 1625 commit(workspaceName); 1626 } 1627 1628 return new IndexationResult(successCount, errorCount); 1629 } 1630 1631 /** 1632 * Add or update a resource into Solr index 1633 * @param resource The resource to index 1634 * @param documentType The document type of the resource 1635 * @param workspaceName The workspace where to index 1636 * @param commit true to commit indexation 1637 * @throws Exception if an error occurs while indexing. 1638 */ 1639 public void indexResource(Resource resource, String documentType, String workspaceName, boolean commit) throws Exception 1640 { 1641 Request request = ContextHelper.getRequest(_context); 1642 1643 // Retrieve the current workspace. 1644 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1645 1646 try 1647 { 1648 // Force the workspace. 1649 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 1650 1651 getLogger().debug("Indexing resource {} into Solr.", resource.getId()); 1652 1653 doIndexResource(resource, documentType, null); 1654 1655 if (commit) 1656 { 1657 commit(workspaceName); 1658 } 1659 1660 getLogger().debug("Succesfully indexed resource {} in Solr.", resource.getId()); 1661 } 1662 catch (Exception e) 1663 { 1664 String error = String.format("Failed to index resource %s in workspace %s", resource.getId(), workspaceName); 1665 getLogger().error(error, e); 1666 throw new IndexingException(error, e); 1667 } 1668 finally 1669 { 1670 // Restore context 1671 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 1672 } 1673 } 1674 1675 /** 1676 * Delete all resource documents from the solr index. 1677 * @param workspaceName The workspace name 1678 * @param commit true to commit deletion 1679 * @throws Exception If an error occurs while unindexing. 1680 */ 1681 public void unindexAllResources(String workspaceName, boolean commit) throws Exception 1682 { 1683 getLogger().debug("Unindexing all resources from Solr."); 1684 1685 String collection = _solrClientProvider.getCollectionName(workspaceName); 1686 _getSolrClient(workspaceName).deleteByQuery(collection, SolrFieldNames.DOCUMENT_TYPE + ':' + SolrFieldNames.TYPE_RESOURCE); 1687 1688 if (commit) 1689 { 1690 commit(workspaceName); 1691 } 1692 1693 getLogger().debug("Succesfully deleted all resource documents from Solr."); 1694 } 1695 1696 /** 1697 * Delete all resource documents at a given path for all workspaces and commit 1698 * @param rootId The resource root ID, must not be null. 1699 * @param path The resource path relative to the given root, must start with a slash. 1700 * @throws Exception If an error occurs while unindexing. 1701 */ 1702 public void unindexResourcesByPath(String rootId, String path) throws Exception 1703 { 1704 String[] workspaceNames = _workspaceSelector.getWorkspaces(); 1705 for (String workspaceName : workspaceNames) 1706 { 1707 unindexResourcesByPath(rootId, path, workspaceName, true); 1708 } 1709 } 1710 1711 /** 1712 * Delete all resource documents at a given path. 1713 * @param rootId The resource root ID, must not be null. 1714 * @param path The resource path relative to the given root, must start with a slash. 1715 * @param workspaceName The workspace where to work in 1716 * @param commit true to commit operation 1717 * @throws Exception If an error occurs while unindexing. 1718 */ 1719 public void unindexResourcesByPath(String rootId, String path, String workspaceName, boolean commit) throws Exception 1720 { 1721 Request request = ContextHelper.getRequest(_context); 1722 1723 // Retrieve the current workspace. 1724 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1725 1726 try 1727 { 1728 // Force the workspace. 1729 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 1730 1731 getLogger().debug("Unindexing all resources at path {} in root {}", path, rootId); 1732 1733 Query query = new ResourceLocationQuery(rootId, path); 1734 1735 String collection = _solrClientProvider.getCollectionName(workspaceName); 1736 _getSolrClient(workspaceName).deleteByQuery(collection, query.build()); 1737 1738 if (commit) 1739 { 1740 commit(workspaceName); 1741 } 1742 1743 getLogger().debug("Succesfully deleted resource document from Solr."); 1744 } 1745 catch (Exception e) 1746 { 1747 String error = String.format("Failed to unindex resource %s in workspace %s", path, workspaceName); 1748 getLogger().error(error, e); 1749 throw new IndexingException(error, e); 1750 } 1751 finally 1752 { 1753 // Restore workspace 1754 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 1755 } 1756 } 1757 1758 /** 1759 * Remove a resource from Solr index for all workspaces and commit 1760 * @param resourceId The id of resource to unindex 1761 * @throws Exception if an error occurs while indexing. 1762 */ 1763 public void unindexResource(String resourceId) throws Exception 1764 { 1765 String[] workspaceNames = _workspaceSelector.getWorkspaces(); 1766 for (String workspaceName : workspaceNames) 1767 { 1768 unindexResource(resourceId, workspaceName, true); 1769 } 1770 } 1771 1772 /** 1773 * Remove a resource from the Solr index. 1774 * @param resourceId The id of resource to unindex 1775 * @param workspaceName The workspace where to work in 1776 * @param commit true to commit operation 1777 * @throws Exception if an error occurs while unindexing. 1778 */ 1779 public void unindexResource(String resourceId, String workspaceName, boolean commit) throws Exception 1780 { 1781 Request request = ContextHelper.getRequest(_context); 1782 1783 // Retrieve the current workspace. 1784 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1785 1786 try 1787 { 1788 // Force the workspace. 1789 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 1790 1791 getLogger().debug("Unindexing resource {} from Solr.", resourceId); 1792 1793 doUnindexDocument(resourceId, workspaceName); 1794 1795 if (commit) 1796 { 1797 commit(workspaceName); 1798 } 1799 1800 getLogger().debug("Succesfully deleted resource {} from Solr.", resourceId); 1801 } 1802 catch (Exception e) 1803 { 1804 String error = String.format("Failed to unindex resource %s in workspace %s", resourceId, workspaceName); 1805 getLogger().error(error, e); 1806 throw new IndexingException(error, e); 1807 } 1808 finally 1809 { 1810 // Restore workspace 1811 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 1812 } 1813 } 1814 1815 /** 1816 * Add or update a resource into Solr index 1817 * @param node The resource to index 1818 * @param documentType The document type of the resource 1819 * @param resourceRoot The resource root, can be null. In that case, it will be computed for each resource. 1820 * @throws Exception if an error occurs while indexing. 1821 */ 1822 protected void doIndexExplorerItem(AmetysObject node, String documentType, TraversableAmetysObject resourceRoot) throws Exception 1823 { 1824 if (node instanceof ResourceCollection) 1825 { 1826 for (AmetysObject child : ((ResourceCollection) node).getChildren()) 1827 { 1828 doIndexExplorerItem(child, documentType, resourceRoot); 1829 } 1830 } 1831 else if (node instanceof Resource) 1832 { 1833 doIndexResource((Resource) node, documentType, resourceRoot); 1834 } 1835 } 1836 1837 /** 1838 * Add or update a resource into Solr index 1839 * @param resource The resource to index 1840 * @param documentType The document type of the resource 1841 * @param resourceRoot The resource root, can be null. In that case, it will be computed for each resource. 1842 * @throws Exception if an error occurs while indexing. 1843 */ 1844 protected void doIndexResource(Resource resource, String documentType, TraversableAmetysObject resourceRoot) throws Exception 1845 { 1846 SolrInputDocument document = new SolrInputDocument(); 1847 1848 _solrResourceIndexer.indexResource(resource, document, documentType, resourceRoot); 1849 1850 String workspaceName = _workspaceSelector.getWorkspace(); 1851 UpdateResponse solrResponse = _getSolrClient(workspaceName).add(_solrClientProvider.getCollectionName(workspaceName), document); 1852 int status = solrResponse.getStatus(); 1853 1854 if (status != 0) 1855 { 1856 throw new IOException("Resource indexation: got status code '" + status + "'."); 1857 } 1858 } 1859 1860 /** 1861 * Process a Solr commit operation in all workspaces. 1862 * @throws SolrServerException if there is an error on the server 1863 * @throws IOException if there is a communication error with the server 1864 */ 1865 public void commit() throws SolrServerException, IOException 1866 { 1867 String[] workspaceNames; 1868 try 1869 { 1870 workspaceNames = _workspaceSelector.getWorkspaces(); 1871 for (String workspaceName : workspaceNames) 1872 { 1873 commit(workspaceName); 1874 } 1875 } 1876 catch (RepositoryException e) 1877 { 1878 throw new RuntimeException("An exception occured while retrieving JCR workspaces. Cannot commited to Solr.", e); 1879 } 1880 } 1881 1882 /** 1883 * Process a Solr commit operation in given workspace. 1884 * @param workspaceName The workspace's name 1885 * @throws SolrServerException if there is an error on the server 1886 * @throws IOException if there is a communication error with the server 1887 */ 1888 public void commit(String workspaceName) throws SolrServerException, IOException 1889 { 1890 long time_0 = System.currentTimeMillis(); 1891 1892 // Commit 1893 UpdateResponse solrResponse = _getSolrClient(workspaceName).commit(_solrClientProvider.getCollectionName(workspaceName)); 1894 int status = solrResponse.getStatus(); 1895 1896 if (status != 0) 1897 { 1898 throw new IOException("Ametys indexing: Solr commit operation - Expecting status code of '0' in the Solr response but got : '" + status + "'."); 1899 } 1900 1901 getLogger().debug("Successful Solr commit operation during an Ametys indexing process in {} ms", System.currentTimeMillis() - time_0); 1902 } 1903 1904 /** 1905 * Process a Solr rollback operation. 1906 * @param workspaceName The workspace's name 1907 * @throws SolrServerException if there is an error on the server 1908 * @throws IOException if there is a communication error with the server 1909 */ 1910 public void rollback(String workspaceName) throws SolrServerException, IOException 1911 { 1912 UpdateResponse solrResponse = _getSolrClient(workspaceName).rollback(_solrClientProvider.getCollectionName(workspaceName)); 1913 int status = solrResponse.getStatus(); 1914 1915 if (status != 0) 1916 { 1917 throw new IOException("Ametys indexing: Solr commit operation - Expecting status code of '0' in the Solr response but got : '" + status + "'."); 1918 } 1919 1920 getLogger().debug("Successful Solr commit operation during an Ametys indexing process."); 1921 } 1922 1923 /** 1924 * Launch a solr index optimization. 1925 * @param workspaceName The workspace's name 1926 * @throws SolrServerException if there is an error on the server 1927 * @throws IOException if there is a communication error with the server 1928 */ 1929 public void optimize(String workspaceName) throws SolrServerException, IOException 1930 { 1931 UpdateResponse solrResponse = _getSolrClient(workspaceName).optimize(_solrClientProvider.getCollectionName(workspaceName)); 1932 int status = solrResponse.getStatus(); 1933 1934 if (status != 0) 1935 { 1936 throw new IOException("Ametys indexing: Solr optimize operation - Expecting status code of '0' in the Solr response but got : '" + status + "'."); 1937 } 1938 1939 getLogger().debug("Successful Solr optimize operation during an Ametys indexing process."); 1940 } 1941 1942 /** 1943 * Delete all documents from the solr index. 1944 * @param workspaceName The workspace name 1945 * @param commit true to commit 1946 * @throws Exception if an error occurs while unindexing. 1947 */ 1948 public void unindexAllDocuments(String workspaceName, boolean commit) throws Exception 1949 { 1950 getLogger().debug("Deleting all documents from Solr."); 1951 1952 String collection = _solrClientProvider.getCollectionName(workspaceName); 1953 _getSolrClient(workspaceName).deleteByQuery(collection, "*:*"); 1954 1955 if (commit) 1956 { 1957 commit(workspaceName); 1958 } 1959 1960 getLogger().debug("Successfully deleted all documents from Solr."); 1961 } 1962 1963 /** 1964 * Delete a document from the Solr server. 1965 * @param id The id of the document to delete from Solr 1966 * @param workspaceName The workspace name 1967 * @throws Exception if an error occurs while indexing. 1968 */ 1969 protected void doUnindexDocument(String id, String workspaceName) throws Exception 1970 { 1971 UpdateResponse solrResponse = _getSolrClient(workspaceName).deleteById(_solrClientProvider.getCollectionName(workspaceName), id); 1972 int status = solrResponse.getStatus(); 1973 1974 if (status != 0) 1975 { 1976 throw new IOException("Deletion of document " + id + ": got status code '" + status + "'."); 1977 } 1978 } 1979 1980 /** 1981 * Delete content attachments documents of a given content from the Solr server. 1982 * @param contentId The id of the content 1983 * @param workspaceName The workspace name 1984 * @throws Exception if an error occurs while indexing. 1985 */ 1986 protected void doUnindexContentAttachments(String contentId, String workspaceName) throws Exception 1987 { 1988 String collectionName = _solrClientProvider.getCollectionName(workspaceName); 1989 SolrClient solrClient = _solrClientProvider.getUpdateClient(workspaceName); 1990 1991 Query query = new ContentAttachmentQuery(contentId); 1992 UpdateResponse solrResponse = solrClient.deleteByQuery(collectionName, query.build()); 1993 int status = solrResponse.getStatus(); 1994 1995 if (status != 0) 1996 { 1997 throw new IOException("Deletion of content attachments of content " + contentId + ": got status code '" + status + "'."); 1998 } 1999 } 2000 2001// /** 2002// * Get the collection to use. 2003// * @return The name of the collection to index into. 2004// */ 2005// protected String getCollection() 2006// { 2007// return _solrCorePrefix + _workspaceSelector.getWorkspace(); 2008// } 2009 2010 class IndexationResult 2011 { 2012 protected int _successCount; 2013 2014 protected int _errorCount; 2015 2016 /** 2017 * Constructor 2018 */ 2019 public IndexationResult() 2020 { 2021 this(0, 0); 2022 } 2023 2024 /** 2025 * Constructor 2026 * @param successCount The success count. 2027 * @param errorCount The error count. 2028 */ 2029 public IndexationResult(int successCount, int errorCount) 2030 { 2031 this._successCount = successCount; 2032 this._errorCount = errorCount; 2033 } 2034 2035 /** 2036 * Get the successCount. 2037 * @return the successCount 2038 */ 2039 public int getSuccessCount() 2040 { 2041 return _successCount; 2042 } 2043 2044 /** 2045 * Set the successCount. 2046 * @param successCount the successCount to set 2047 */ 2048 public void setSuccessCount(int successCount) 2049 { 2050 this._successCount = successCount; 2051 } 2052 2053 /** 2054 * Test if the indexation had errors. 2055 * @return true if the indexation had errors, false otherwise. 2056 */ 2057 public boolean hasErrors() 2058 { 2059 return _errorCount > 0; 2060 } 2061 2062 /** 2063 * Get the errorCount. 2064 * @return the errorCount 2065 */ 2066 public int getErrorCount() 2067 { 2068 return _errorCount; 2069 } 2070 2071 /** 2072 * Set the errorCount. 2073 * @param errorCount the errorCount to set 2074 */ 2075 public void setErrorCount(int errorCount) 2076 { 2077 this._errorCount = errorCount; 2078 } 2079 } 2080 2081}