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.web.indexing.solr; 017 018import java.io.IOException; 019import java.util.ArrayList; 020import java.util.Collection; 021import java.util.Collections; 022import java.util.Date; 023import java.util.HashSet; 024import java.util.List; 025import java.util.Map; 026import java.util.Set; 027 028import org.apache.avalon.framework.component.Component; 029import org.apache.avalon.framework.context.Context; 030import org.apache.avalon.framework.context.ContextException; 031import org.apache.avalon.framework.context.Contextualizable; 032import org.apache.avalon.framework.service.ServiceException; 033import org.apache.avalon.framework.service.ServiceManager; 034import org.apache.avalon.framework.service.Serviceable; 035import org.apache.cocoon.components.ContextHelper; 036import org.apache.cocoon.environment.Request; 037import org.apache.solr.client.solrj.SolrClient; 038import org.apache.solr.client.solrj.SolrServerException; 039import org.apache.solr.client.solrj.response.UpdateResponse; 040import org.apache.solr.common.SolrInputDocument; 041import org.apache.solr.common.SolrInputField; 042 043import org.ametys.cms.content.indexing.solr.SolrContentIndexer; 044import org.ametys.cms.content.indexing.solr.SolrFieldNames; 045import org.ametys.cms.content.indexing.solr.SolrIndexer; 046import org.ametys.cms.content.indexing.solr.SolrResourceIndexer; 047import org.ametys.cms.indexing.IndexingException; 048import org.ametys.cms.indexing.solr.AdditionalPropertyIndexer; 049import org.ametys.cms.indexing.solr.AdditionalPropertyIndexerExtensionPoint; 050import org.ametys.cms.repository.Content; 051import org.ametys.cms.repository.RequestAttributeWorkspaceSelector; 052import org.ametys.cms.search.query.AndQuery; 053import org.ametys.cms.search.query.DocumentTypeQuery; 054import org.ametys.cms.search.query.JoinQuery; 055import org.ametys.cms.search.query.OrQuery; 056import org.ametys.cms.search.query.Query; 057import org.ametys.cms.search.query.QuerySyntaxException; 058import org.ametys.cms.search.solr.SolrClientProvider; 059import org.ametys.cms.tag.Tag; 060import org.ametys.cms.tag.TagHelper; 061import org.ametys.cms.tag.TagProviderExtensionPoint; 062import org.ametys.plugins.explorer.resources.Resource; 063import org.ametys.plugins.explorer.resources.ResourceCollection; 064import org.ametys.plugins.repository.AmetysObject; 065import org.ametys.plugins.repository.AmetysObjectResolver; 066import org.ametys.plugins.repository.RepositoryConstants; 067import org.ametys.runtime.plugin.component.AbstractLogEnabled; 068import org.ametys.web.WebConstants; 069import org.ametys.web.repository.page.Page; 070import org.ametys.web.repository.page.Page.PageType; 071import org.ametys.web.repository.page.Zone; 072import org.ametys.web.repository.page.ZoneItem; 073import org.ametys.web.repository.page.ZoneItem.ZoneType; 074import org.ametys.web.repository.site.Site; 075import org.ametys.web.repository.sitemap.Sitemap; 076import org.ametys.web.search.query.AttachmentQuery; 077import org.ametys.web.search.query.PageQuery; 078import org.ametys.web.service.Service; 079import org.ametys.web.service.ServiceExtensionPoint; 080 081/** 082 * Component responsible for indexing a page with all its contents. 083 */ 084public class SolrPageIndexer extends AbstractLogEnabled implements Component, Serviceable, SolrWebFieldNames, Contextualizable 085{ 086 /** The avalon role. */ 087 public static final String ROLE = SolrPageIndexer.class.getName(); 088 089 /** The Solr client provider */ 090 protected SolrClientProvider _solrClientProvider; 091 /** The Solr indexer */ 092 protected SolrIndexer _solrIndexer; 093 /** Solr Ametys contents indexer */ 094 protected SolrContentIndexer _solrContentIndexer; 095 /** Solr Ametys resources indexer */ 096 protected SolrResourceIndexer _solrResourceIndexer; 097 /** The Solr page right indexer. */ 098 protected SolrPageRightIndexer _solrPageRightIndexer; 099 /** The additional property indexer extension point. */ 100 protected AdditionalPropertyIndexerExtensionPoint _additionalPropertiesIndexerEP; 101 /** The tag provider extension point. */ 102 protected TagProviderExtensionPoint _tagProviderEP; 103 104 /** The service extension point. */ 105 protected ServiceExtensionPoint _serviceExtensionPoint; 106 /** The Ametys object resolver*/ 107 protected AmetysObjectResolver _ametysObjectResolver; 108 /** The avalon context */ 109 protected Context _context; 110 111 @Override 112 public void service(ServiceManager manager) throws ServiceException 113 { 114 _ametysObjectResolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 115 _solrIndexer = (SolrIndexer) manager.lookup(SolrIndexer.ROLE); 116 _solrContentIndexer = (SolrContentIndexer) manager.lookup(SolrContentIndexer.ROLE); 117 _solrResourceIndexer = (SolrResourceIndexer) manager.lookup(SolrResourceIndexer.ROLE); 118 _solrPageRightIndexer = (SolrPageRightIndexer) manager.lookup(SolrPageRightIndexer.ROLE); 119 _solrClientProvider = (SolrClientProvider) manager.lookup(SolrClientProvider.ROLE); 120 _serviceExtensionPoint = (ServiceExtensionPoint) manager.lookup(ServiceExtensionPoint.ROLE); 121 _additionalPropertiesIndexerEP = (AdditionalPropertyIndexerExtensionPoint) manager.lookup(AdditionalPropertyIndexerExtensionPoint.ROLE); 122 _tagProviderEP = (TagProviderExtensionPoint) manager.lookup(TagProviderExtensionPoint.ROLE); 123 } 124 125 public void contextualize(Context context) throws ContextException 126 { 127 _context = context; 128 } 129 130 /** 131 * Index a page and eventually its children, recursively, in all workspaces and commit<br> 132 * By default, children pages will be actually indexed if indexRecursively is true and if those pages are not already indexed. 133 * @param pageId the page to be indexed. 134 * @param indexRecursively to also process children pages. 135 * @param indexAttachments to index page attachments 136 * @throws Exception if an error occurs during indexation. 137 */ 138 public void indexPage(String pageId, boolean indexRecursively, boolean indexAttachments) throws Exception 139 { 140 indexPage(pageId, RepositoryConstants.DEFAULT_WORKSPACE, indexRecursively, indexAttachments, true); 141 indexPage(pageId, WebConstants.LIVE_WORKSPACE, indexRecursively, indexAttachments, true); 142 } 143 144 /** 145 * Index a page and eventually its children, recursively.<br> 146 * By default, children pages will be actually indexed if indexRecursively is true and if those pages are not already indexed. 147 * @param pageId the page to be indexed. 148 * @param workspaceName the workspace where to index 149 * @param indexRecursively to also process children pages. 150 * @param indexAttachments to index page attachments 151 * @param commit Commit the indexation 152 * @throws Exception if an error occurs during indexation. 153 */ 154 public void indexPage(String pageId, String workspaceName, boolean indexRecursively, boolean indexAttachments, boolean commit) throws Exception 155 { 156 Request request = ContextHelper.getRequest(_context); 157 158 // Retrieve the current workspace. 159 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 160 // Retrieve the current site name. 161 String currentSiteName = (String) request.getAttribute("siteName"); 162 163 try 164 { 165 // Force the workspace. 166 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 167 168 getLogger().debug("Indexing page: {}", pageId); 169 170 if (_ametysObjectResolver.hasAmetysObjectForId(pageId)) // In 'live' the page may not exist 171 { 172 Page page = _ametysObjectResolver.resolveById(pageId); 173 _indexPage(page, workspaceName, indexRecursively, indexAttachments); 174 175 if (commit) 176 { 177 _solrIndexer.commit(workspaceName); 178 } 179 } 180 } 181 catch (Exception e) 182 { 183 String error = String.format("Failed to index page %s in workspace %s", pageId, workspaceName); 184 getLogger().error(error, e); 185 throw new IndexingException(error, e); 186 } 187 finally 188 { 189 // Restore the site name. 190 request.setAttribute("siteName", currentSiteName); 191 // Restore context 192 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 193 } 194 } 195 196 private void _indexPage(Page page, String workspaceName, boolean indexRecursively, boolean indexAttachments) throws Exception 197 { 198 getLogger().info("Indexing page: {} in workspace '{}'", page, workspaceName); 199 200 SolrInputDocument document = new SolrInputDocument(); 201 202 // Prepare the solr input document by adding fields. 203 _populatePageDocument(page, document); 204 205 // Set the additional properties in the document. 206 _populateAdditionalProperties(page, document); 207 208 // Indexation of the page access. 209 _indexPageAccess(page, document); 210 211 // Indexation of the document 212 _indexPageDocument(page, document, workspaceName); 213 214 // Index page attachments documents 215 if (indexAttachments) 216 { 217 indexPageAttachments(page.getRootAttachments(), page); 218 } 219 220 if (indexRecursively) 221 { 222 for (Page child : page.getChildrenPages()) 223 { 224 // FIXME index child pages if (and only if) not indexed... see original source. 225// indexPage(child, false, indexRecursively); 226// indexPage(child, false); 227 _indexPage(child, workspaceName, indexRecursively, indexAttachments); 228 } 229 } 230 } 231 232 /** 233 * Populate the solr input document by adding fields to index. 234 * @param page the page to index. 235 * @param document the solr input document 236 * @throws Exception if something goes wrong when processing the indexation of the page 237 */ 238 protected void _populatePageDocument(Page page, SolrInputDocument document) throws Exception 239 { 240 Sitemap sitemap = page.getSitemap(); 241 String sitemapName = sitemap.getName(); 242 Site site = page.getSite(); 243 String siteName = site.getName(); 244 String pageTitle = page.getTitle(); 245 String language = sitemapName; 246 247 // Page id and type 248 document.addField(SolrFieldNames.ID, page.getId()); 249 document.addField(SolrFieldNames.DOCUMENT_TYPE, SolrWebFieldNames.TYPE_PAGE); 250 251 // Fulltext 252 SolrContentIndexer.indexFulltextValue(document, page.getTitle(), language); 253 if (!page.getTitle().equals(page.getLongTitle())) 254 { 255 SolrContentIndexer.indexFulltextValue(document, page.getLongTitle(), language); 256 } 257 258 // Page title 259 document.addField(TITLE, pageTitle); 260 document.addField(LONG_TITLE, page.getLongTitle()); 261 262 document.addField(TEMPLATE, page.getTemplate()); 263 document.addField(PAGE_TYPE, page.getType().name()); 264 document.addField(PAGE_DEPTH, page.getDepth()); 265 266 // Contents (page title shoud be indexed before because the main content can override it). 267 _populatePageContentsDocument(page, document); 268 269 // Parents of the page 270 List<String> ancestorIds = new ArrayList<>(); 271 AmetysObject parent = page.getParent(); 272 while (parent instanceof Page) 273 { 274 ancestorIds.add(parent.getId()); 275 parent = parent.getParent(); 276 } 277 document.addField(ANCESTOR_IDS, ancestorIds); 278 279 document.addField(SITE_NAME, siteName); 280 document.addField(SITEMAP_NAME, sitemapName); 281 282 // Page tags (strict and tags including ancestor pages). 283 document.addField(SolrFieldNames.TAGS, page.getTags()); 284 document.addField(SolrFieldNames.ALL_TAGS, _getTagsWithAncestors(page)); 285 286 // Page last modification date - Store.YES, Index.ANALYZED 287 Date lastModified = _getLastModificationDate(page); 288 if (lastModified != null) 289 { 290 document.addField(LAST_MODIFIED + "_dt", SolrIndexer.dateFormat().format(lastModified)); 291 } 292 293 // Page last validation date - Store.YES, Index.ANALYZED 294 Date lastValidation = _getLastValidationDate(page); 295 if (lastValidation != null) 296 { 297 document.addField(LAST_VALIDATION, SolrIndexer.dateFormat().format(lastValidation)); 298 } 299 300 // date for sorting 301 SolrInputField dateField = document.getField(DATE_FOR_SORTING); 302 if (dateField == null) 303 { 304 Collection<Object> oDateValues = document.getFieldValues(CONTENT_INTERESTING_DATES); 305 if (oDateValues != null && !oDateValues.isEmpty()) 306 { 307 document.setField(DATE_FOR_SORTING, oDateValues.iterator().next()); 308 } 309 } 310 311 // Attachments 312 _solrResourceIndexer.indexResourceCollection(page.getRootAttachments(), document, language); 313 } 314 315 /** 316 * Get all the page tags with their ancestors. 317 * @param page The page. 318 * @return All the page tags with their ancestors. 319 */ 320 protected Set<String> _getTagsWithAncestors(Page page) 321 { 322 Set<String> allTags = new HashSet<>(page.getTags()); 323 324 Map<String, Object> tagParams = Collections.singletonMap("siteName", page.getSiteName()); 325 326 for (String tagName : page.getTags()) 327 { 328 allTags.add(tagName); 329 330 // Get the ancestor tags 331 Tag tag = _tagProviderEP.getTag(tagName, tagParams); 332 for (Tag ancestor : TagHelper.getAncestors(tag, false)) 333 { 334 allTags.add(ancestor.getName()); 335 } 336 } 337 338 return allTags; 339 } 340 341 /** 342 * Index the content of the page.<p> 343 * @param page the page to index. 344 * @param document the document to populate. 345 * @throws Exception if an error occurs. 346 */ 347 protected void _populatePageContentsDocument(Page page, SolrInputDocument document) throws Exception 348 { 349 if (page.getType() == PageType.CONTAINER) 350 { 351 for (Zone zone : page.getZones()) 352 { 353 for (ZoneItem zoneItem : zone.getZoneItems()) 354 { 355 if (zoneItem.getType() == ZoneType.CONTENT) 356 { 357 // Content 358 Content content = zoneItem.getContent(); 359 document.addField(CONTENT_IDS, content.getId()); 360 } 361 else if (zoneItem.getType() == ZoneType.SERVICE) 362 { 363 // service 364 String serviceId = zoneItem.getServiceId(); 365 document.addField(SERVICE_IDS, serviceId); 366 367 Service service = _serviceExtensionPoint.getExtension(serviceId); 368 if (service == null) 369 { 370 getLogger().error("The service id '{}' does not exist. It is referenced in the page {}/{}/{} ({} in zoneitem {})", serviceId, page.getSiteName(), page.getSitemapName(), page.getPathInSitemap(), page.getId(), zoneItem.getId()); 371 } 372 else 373 { 374 service.index(zoneItem, document); 375 } 376 } 377 } 378 } 379 } 380 } 381 382 /** 383 * Computes the last modification date of a page. 384 * @param page the page. 385 * @return the last modification date or <code>null</code>. 386 */ 387 protected Date _getLastModificationDate(Page page) 388 { 389 Date lastModified = null; 390 391 if (page.getType() == PageType.CONTAINER) 392 { 393 for (Zone zone : page.getZones()) 394 { 395 for (ZoneItem zoneItem : zone.getZoneItems()) 396 { 397 switch (zoneItem.getType()) 398 { 399 case SERVICE: 400 // A service has no last modification date 401 break; 402 case CONTENT: 403 Date contentLastModified = zoneItem.getContent().getLastModified(); 404 405 if (lastModified == null || contentLastModified.after(lastModified)) 406 { 407 // Keep the latest modification date 408 lastModified = contentLastModified; 409 } 410 break; 411 default: 412 break; 413 } 414 } 415 } 416 } 417 418 return lastModified; 419 } 420 421 /** 422 * Computes the last validation date of a page. 423 * @param page the page. 424 * @return the last validation date or <code>null</code>. 425 */ 426 protected Date _getLastValidationDate(Page page) 427 { 428 Date lastValidated = null; 429 430 if (page.getType() == PageType.CONTAINER) 431 { 432 for (Zone zone : page.getZones()) 433 { 434 for (ZoneItem zoneItem : zone.getZoneItems()) 435 { 436 switch (zoneItem.getType()) 437 { 438 case SERVICE: 439 // A service has no last validation date 440 break; 441 case CONTENT: 442 Date contentLastValidation = zoneItem.getContent().getLastValidationDate(); 443 444 if (contentLastValidation != null && (lastValidated == null || contentLastValidation.after(lastValidated))) 445 { 446 // Keep the latest modification date 447 lastValidated = contentLastValidation; 448 } 449 break; 450 default: 451 break; 452 } 453 } 454 } 455 } 456 457 return lastValidated; 458 } 459 460 /** 461 * Populate the solr input document by adding fields to index. 462 * @param page the page to index. 463 * @param document the solr input document 464 * @throws Exception if something goes wrong when processing the indexation of the page 465 */ 466 protected void _populateAdditionalProperties(Page page, SolrInputDocument document) throws Exception 467 { 468 Collection<AdditionalPropertyIndexer> indexers = _additionalPropertiesIndexerEP.getIndexers("page"); 469 for (AdditionalPropertyIndexer indexer : indexers) 470 { 471 indexer.index(page, document); 472 } 473 } 474 475 /** 476 * Index page attachments as new entries in the index. 477 * @param collection the collection of attachments 478 * @param page the page whose attachments will be indexed 479 * @throws Exception if something goes wrong when indexing the attachments of the page 480 */ 481 public void indexPageAttachments(ResourceCollection collection, Page page) throws Exception 482 { 483 if (collection == null) 484 { 485 return; 486 } 487 488 for (AmetysObject object : collection.getChildren()) 489 { 490 if (object instanceof ResourceCollection) 491 { 492 indexPageAttachments((ResourceCollection) object, page); 493 } 494 else if (object instanceof Resource) 495 { 496 Resource resource = (Resource) object; 497 indexPageAttachment(resource, page); 498 } 499 } 500 } 501 502 /** 503 * Index a page attachment 504 * @param resource the page attachment as a {@link Resource} 505 * @param page the page whose attachment is going to be indexed 506 * @throws Exception if something goes wrong when processing the indexation of the page attachment 507 */ 508 public void indexPageAttachment(Resource resource, Page page) throws Exception 509 { 510 SolrInputDocument document = new SolrInputDocument(); 511 512 // Prepare resource doc 513 _indexPageAttachment(resource, document, page); 514 515 // Indexation of the document 516 _indexResourceDocument(resource, document); 517 } 518 519 private void _indexPageAttachment(Resource resource, SolrInputDocument document, Page page) throws Exception 520 { 521 String language = page.getSitemapName(); 522 523 _solrResourceIndexer.indexResource(resource, document, TYPE_PAGE_RESOURCE, language); 524 525 // site name - Store.YES, Index.NOT_ANALYZED 526 document.addField(SITE_NAME, page.getSiteName()); 527 528 // Added for Solr. 529 // Page site map name - Store.YES, Index.NOT_ANALYZED 530 document.addField(SITEMAP_NAME, page.getSitemapName()); 531 532 // Need the id of the page for unindexing attachment during the unindexing of the page 533 document.addField(PAGE_ID, page.getId()); 534 document.addField(PAGE_ID + "_s_dv", page.getId()); 535 } 536 537 /** 538 * Index a populated solr input document of type Page. 539 * @param page the page from which the input document is created 540 * @param document the input document to add to the solr index 541 * @param workspaceName The workspace name 542 * @throws SolrServerException if there is an error on the Solr server 543 * @throws IOException if there is a communication error with the server 544 */ 545 protected void _indexPageDocument(Page page, SolrInputDocument document, String workspaceName) throws SolrServerException, IOException 546 { 547 // Retrieve appropriate solr client 548 String collectionName = _solrClientProvider.getCollectionName(workspaceName); 549 SolrClient solrClient = _solrClientProvider.getUpdateClient(workspaceName); 550 551 // Add document 552 UpdateResponse solrResponse = solrClient.add(collectionName, document); 553 int status = solrResponse.getStatus(); 554 555 if (status != 0) 556 { 557 throw new IOException("Ametys Page indexing - Expecting status code of '0' in the Solr response but got : '" + status + "'. Page id : " + page.getId()); 558 } 559 560 getLogger().debug("Successful page indexing. Page identifier : {}", page.getId()); 561 } 562 563 /** 564 * Index a populated solr input document of type Resource. 565 * @param resource the resource from which the input document is created 566 * @param document the input document 567 * @throws SolrServerException if there is an error on the server 568 * @throws IOException if there is a communication error with the server 569 */ 570 protected void _indexResourceDocument(Resource resource, SolrInputDocument document) throws SolrServerException, IOException 571 { 572 // Retrieve appropriate solr client 573 Request request = ContextHelper.getRequest(_context); 574 String workspaceName = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 575 String collectionName = _solrClientProvider.getCollectionName(workspaceName); 576 SolrClient solrClient = _solrClientProvider.getUpdateClient(workspaceName); 577 578 // Add document 579 UpdateResponse solrResponse = solrClient.add(collectionName, document); 580 int status = solrResponse.getStatus(); 581 582 if (status != 0) 583 { 584 throw new IOException("Ametys Page indexing - Expecting status code of '0' in the Solr response but got : '" + status + "'. Resource id : " + resource.getId()); 585 } 586 587 getLogger().debug("Successful resource indexing. Resource identifier : {}", resource.getId()); 588 } 589 590 /** 591 * Index the page access. 592 * @param page The page. 593 * @param document The document. 594 * @throws Exception If an error occurs while indexing. 595 */ 596 protected void _indexPageAccess(Page page, SolrInputDocument document) throws Exception 597 { 598 _solrPageRightIndexer.indexPageAccess(page, document); 599 } 600 601 /////////////////////////////////////////////////////////////////////////// 602 603 /** 604 * Un-index a page by its ID for all workspaces and commit 605 * @param pageId The page ID. 606 * @param unindexRecursively also unindex child pages if requested. 607 * @param unindexAttachments also unindex page attachments 608 * @throws Exception if an error occurs during index update. 609 */ 610 public void unindexPage(String pageId, boolean unindexRecursively, boolean unindexAttachments) throws Exception 611 { 612 unindexPage(pageId, RepositoryConstants.DEFAULT_WORKSPACE, unindexRecursively, unindexAttachments, true); 613 unindexPage(pageId, WebConstants.LIVE_WORKSPACE, unindexRecursively, unindexAttachments, true); 614 } 615 616 /** 617 * De-index a page (and optionally its children pages). 618 * @param pageId the page to be de-indexed. 619 * @param workspaceName The workspace where to work in 620 * @param unindexRecursively also unindex child pages if requested. 621 * @param unindexAttachments also unindex page attachments 622 * @param commit Commit the operator to Solr 623 * @throws Exception if an error occurs during index update. 624 */ 625 public void unindexPage(String pageId, String workspaceName, boolean unindexRecursively, boolean unindexAttachments, boolean commit) throws Exception 626 { 627 Request request = ContextHelper.getRequest(_context); 628 629 // Retrieve the current workspace. 630 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 631 // Retrieve the current site name. 632 String currentSiteName = (String) request.getAttribute("siteName"); 633 634 try 635 { 636 // Force the workspace. 637 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 638 639 getLogger().debug("Unindexing page: {}", pageId); 640 641 _unindexPageDocument(pageId, workspaceName, unindexRecursively, unindexAttachments); 642 643 if (commit) 644 { 645 _solrIndexer.commit(workspaceName); 646 } 647 } 648 catch (Exception e) 649 { 650 String error = String.format("Failed to unindex page %s in workspace %s", pageId, workspaceName); 651 getLogger().error(error, e); 652 throw new IndexingException(error, e); 653 } 654 finally 655 { 656 // Restore the site name. 657 request.setAttribute("siteName", currentSiteName); 658 // Restore context 659 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 660 } 661 } 662 663 /** 664 * Deindex a document of type Page. Also deindex attachments of a page 665 * @param pageId the id of the page to deindex 666 * @param workspaceName The workspace name 667 * @param unindexRecursively also unindex child pages if requested. 668 * @param unindexAttachments also unindex page attachments 669 * @throws SolrServerException if there is an error on the server 670 * @throws IOException if there is a communication error with the server 671 * @throws QuerySyntaxException if the uri query can't be built because of a syntax error. 672 */ 673 protected void _unindexPageDocument(String pageId, String workspaceName, boolean unindexRecursively, boolean unindexAttachments) throws SolrServerException, IOException, QuerySyntaxException 674 { 675 // Retrieve appropriate solr client 676 String collectionName = _solrClientProvider.getCollectionName(workspaceName); 677 SolrClient solrClient = _solrClientProvider.getUpdateClient(workspaceName); 678 679 getLogger().info("Unindexing page {} in workspace '{}'", pageId, workspaceName); 680 681 Query pages = new AndQuery(new DocumentTypeQuery(TYPE_PAGE), new PageQuery(pageId, unindexRecursively)); 682 Query query; 683 if (unindexRecursively && unindexAttachments) 684 { 685 // {!ametys join=pageId q=ancestorIds:"page://xxxx"} 686 Query joinQuery = new JoinQuery(() -> ANCESTOR_IDS + ":\"" + pageId + "\"", PAGE_ID); 687 Query attachments = new AndQuery(new DocumentTypeQuery(TYPE_PAGE_RESOURCE), new OrQuery(new AttachmentQuery(pageId), joinQuery)); 688 query = new OrQuery(attachments, pages); 689 } 690 else if (unindexAttachments) 691 { 692 Query attachments = new AndQuery(new DocumentTypeQuery(TYPE_PAGE_RESOURCE), new AttachmentQuery(pageId)); 693 query = new OrQuery(attachments, pages); 694 } 695 else 696 { 697 query = pages; 698 } 699 700 // Delete by query 701 UpdateResponse solrResponse = solrClient.deleteByQuery(collectionName, query.build()); 702 int status = solrResponse.getStatus(); 703 704 if (status != 0) 705 { 706 throw new IOException("Ametys Page de-indexing - Expecting status code of '0' in the Solr response but got : '" + status + "'. Page id : " + pageId); 707 } 708 709 getLogger().debug("Successful page de-indexing{}. Page identifier : {}", unindexRecursively ? " with its children" : "", pageId); 710 } 711 712 /////////////////////////////////////////////////////////////////////////// 713 714 /** 715 * Reindex a page by its ID for all workspaces and commit 716 * @param pageId The page ID. 717 * @param reindexRecursively also reindex child pages if requested. 718 * @param reindexAttachments also reindex page attachments 719 * @throws Exception if an error occurs during index update. 720 */ 721 public void reindexPage(String pageId, boolean reindexRecursively, boolean reindexAttachments) throws Exception 722 { 723 reindexPage(pageId, RepositoryConstants.DEFAULT_WORKSPACE, reindexRecursively, reindexAttachments, true); 724 reindexPage(pageId, WebConstants.LIVE_WORKSPACE, reindexRecursively, reindexAttachments, true); 725 } 726 727 728 /** 729 * Reindex a page by its ID. 730 * @param pageId The page ID. 731 * @param workspaceName The workspace where to work in 732 * @param reindexRecursively also reindex child pages if requested. 733 * @param reindexAttachments also reindex page attachments 734 * @param commit Commit the operator to Solr 735 * @throws Exception if an error occurs during index update. 736 */ 737 public void reindexPage(String pageId, String workspaceName, boolean reindexRecursively, boolean reindexAttachments, boolean commit) throws Exception 738 { 739 Request request = ContextHelper.getRequest(_context); 740 741 // Retrieve the current workspace. 742 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 743 // Retrieve the current site name. 744 String currentSiteName = (String) request.getAttribute("siteName"); 745 746 try 747 { 748 // Force the workspace. 749 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 750 751 getLogger().debug("Reindexing page: {}", pageId); 752 753 if (_ametysObjectResolver.hasAmetysObjectForId(pageId)) // In 'live' the page may not exist 754 { 755 Page page = _ametysObjectResolver.resolveById(pageId); 756 _unindexPageDocument(pageId, workspaceName, reindexRecursively, reindexAttachments); 757 _indexPage(page, workspaceName, reindexRecursively, reindexAttachments); 758 759 if (commit) 760 { 761 _solrIndexer.commit(workspaceName); 762 } 763 } 764 } 765 catch (Exception e) 766 { 767 String error = String.format("Failed to unindex page %s in workspace %s", pageId, workspaceName); 768 getLogger().error(error, e); 769 throw new IndexingException(error, e); 770 } 771 finally 772 { 773 // Restore the site name. 774 request.setAttribute("siteName", currentSiteName); 775 // Restore context 776 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 777 } 778 } 779}