001/* 002 * File : $Source$ 003 * Date : $Date$ 004 * Version: $Revision$ 005 * 006 * This library is part of OpenCms - 007 * the Open Source Content Management System 008 * 009 * Copyright (C) 2002 - 2009 Alkacon Software (http://www.alkacon.com) 010 * 011 * This library is free software; you can redistribute it and/or 012 * modify it under the terms of the GNU Lesser General Public 013 * License as published by the Free Software Foundation; either 014 * version 2.1 of the License, or (at your option) any later version. 015 * 016 * This library is distributed in the hope that it will be useful, 017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 019 * Lesser General Public License for more details. 020 * 021 * For further information about Alkacon Software, please see the 022 * company website: http://www.alkacon.com 023 * 024 * For further information about OpenCms, please see the 025 * project website: http://www.opencms.org 026 * 027 * You should have received a copy of the GNU Lesser General Public 028 * License along with this library; if not, write to the Free Software 029 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 030 */ 031 032package org.opencms.search.solr; 033 034import org.opencms.file.CmsObject; 035import org.opencms.file.CmsResource; 036import org.opencms.main.CmsLog; 037import org.opencms.main.OpenCms; 038import org.opencms.relations.CmsCategory; 039import org.opencms.search.CmsSearchUtil; 040import org.opencms.search.I_CmsSearchDocument; 041import org.opencms.search.documents.CmsDocumentDependency; 042import org.opencms.search.fields.CmsSearchField; 043import org.opencms.util.CmsStringUtil; 044import org.opencms.util.CmsUUID; 045 046import java.nio.ByteBuffer; 047import java.text.ParseException; 048import java.util.ArrayList; 049import java.util.Collection; 050import java.util.Collections; 051import java.util.Date; 052import java.util.List; 053import java.util.Locale; 054 055import org.apache.commons.logging.Log; 056import org.apache.solr.common.SolrDocument; 057import org.apache.solr.common.SolrException; 058import org.apache.solr.common.SolrInputDocument; 059import org.apache.solr.schema.DatePointField; 060import org.apache.solr.schema.FieldType; 061import org.apache.solr.schema.IndexSchema; 062import org.apache.solr.schema.SchemaField; 063 064/** 065 * A search document implementation for Solr indexes.<p> 066 * 067 * @since 8.5.0 068 */ 069public class CmsSolrDocument implements I_CmsSearchDocument { 070 071 /** The log object for this class. */ 072 private static final Log LOG = CmsLog.getLog(CmsSolrDocument.class); 073 074 /** The Solr document. */ 075 private SolrInputDocument m_doc; 076 077 /** Holds the score for this document. */ 078 private float m_score; 079 080 /** 081 * Public constructor to create a encapsulate a Solr document.<p> 082 * 083 * @param doc the Solr document 084 */ 085 public CmsSolrDocument(SolrDocument doc) { 086 087 this(); 088 m_doc = CmsSearchUtil.toSolrInputDocument(doc); 089 } 090 091 /** 092 * Public constructor to create a encapsulate a Solr document.<p> 093 * 094 * @param doc the Solr document 095 */ 096 public CmsSolrDocument(SolrInputDocument doc) { 097 098 this(); 099 m_doc = doc; 100 } 101 102 /** 103 * Private constructor.<p> 104 */ 105 private CmsSolrDocument() { 106 107 } 108 109 /** 110 * @see org.opencms.search.I_CmsSearchDocument#addCategoryField(java.util.List) 111 */ 112 public void addCategoryField(List<CmsCategory> categories) { 113 114 if ((categories != null) && (categories.size() > 0)) { 115 for (CmsCategory category : categories) { 116 m_doc.addField(CmsSearchField.FIELD_CATEGORY, category.getPath()); 117 } 118 } 119 } 120 121 /** 122 * @see org.opencms.search.I_CmsSearchDocument#addContentField(byte[]) 123 */ 124 public void addContentField(byte[] data) { 125 126 m_doc.setField(CmsSearchField.FIELD_CONTENT_BLOB, ByteBuffer.wrap(data)); 127 } 128 129 /** 130 * @see org.opencms.search.I_CmsSearchDocument#addContentLocales(java.util.Collection) 131 */ 132 public void addContentLocales(Collection<Locale> locales) { 133 134 if ((locales != null) && !locales.isEmpty()) { 135 for (Locale locale : locales) { 136 m_doc.addField(CmsSearchField.FIELD_CONTENT_LOCALES, locale.toString()); 137 } 138 } 139 } 140 141 /** 142 * @see org.opencms.search.I_CmsSearchDocument#addDateField(java.lang.String, long, boolean) 143 */ 144 public void addDateField(String name, long time, boolean analyzed) { 145 146 String val = CmsSearchUtil.getDateAsIso8601(time); 147 m_doc.addField(name, val); 148 if (analyzed) { 149 m_doc.addField(name + CmsSearchField.FIELD_DATE_LOOKUP_SUFFIX, val); 150 } 151 } 152 153 /** 154 * Adds the given document dependency to this document.<p> 155 * 156 * @param cms the current CmsObject 157 * @param resDeps the dependency 158 */ 159 public void addDocumentDependency(CmsObject cms, CmsDocumentDependency resDeps) { 160 161 if (resDeps != null) { 162 m_doc.addField(CmsSearchField.FIELD_DEPENDENCY_TYPE, resDeps.getType()); 163 if ((resDeps.getMainDocument() != null) && (resDeps.getType() != null)) { 164 m_doc.addField( 165 CmsSearchField.FIELD_PREFIX_DEPENDENCY + resDeps.getType().toString(), 166 resDeps.getMainDocument().toDependencyString(cms)); 167 } 168 for (CmsDocumentDependency dep : resDeps.getVariants()) { 169 m_doc.addField( 170 CmsSearchField.FIELD_PREFIX_DEPENDENCY + dep.getType().toString(), 171 dep.toDependencyString(cms)); 172 } 173 for (CmsDocumentDependency dep : resDeps.getAttachments()) { 174 m_doc.addField( 175 CmsSearchField.FIELD_PREFIX_DEPENDENCY + dep.getType().toString(), 176 dep.toDependencyString(cms)); 177 } 178 } 179 } 180 181 /** 182 * @see org.opencms.search.I_CmsSearchDocument#addFileSizeField(int) 183 */ 184 public void addFileSizeField(int length) { 185 186 if (OpenCms.getSearchManager().getSolrServerConfiguration().getSolrSchema().hasExplicitField( 187 CmsSearchField.FIELD_SIZE)) { 188 m_doc.addField(CmsSearchField.FIELD_SIZE, Integer.valueOf(length)); 189 } 190 } 191 192 /** 193 * Adds a multi-valued field.<p> 194 * 195 * @param fieldName the field name to put the values in 196 * @param values the values to put in the field 197 */ 198 public void addMultiValuedField(String fieldName, List<String> values) { 199 200 if ((values != null) && (values.size() > 0)) { 201 for (String value : values) { 202 m_doc.addField(fieldName, value); 203 } 204 } 205 } 206 207 /** 208 * @see org.opencms.search.I_CmsSearchDocument#addPathField(java.lang.String) 209 */ 210 public void addPathField(String rootPath) { 211 212 String folderName = CmsResource.getFolderPath(rootPath); 213 for (int i = 0; i < folderName.length(); i++) { 214 char c = folderName.charAt(i); 215 if (c == '/') { 216 m_doc.addField(CmsSearchField.FIELD_PARENT_FOLDERS, folderName.substring(0, i + 1)); 217 } 218 } 219 } 220 221 /** 222 * @see org.opencms.search.I_CmsSearchDocument#addResourceLocales(java.util.Collection) 223 */ 224 public void addResourceLocales(Collection<Locale> locales) { 225 226 if ((locales != null) && !locales.isEmpty()) { 227 for (Locale locale : locales) { 228 m_doc.addField(CmsSearchField.FIELD_RESOURCE_LOCALES, locale.toString()); 229 } 230 } 231 } 232 233 /** 234 * @see org.opencms.search.I_CmsSearchDocument#addRootPathField(java.lang.String) 235 */ 236 public void addRootPathField(String rootPath) { 237 238 m_doc.addField(CmsSearchField.FIELD_PATH, rootPath); 239 } 240 241 /** 242 * @see org.opencms.search.I_CmsSearchDocument#addSearchField(org.opencms.search.fields.CmsSearchField, java.lang.String) 243 */ 244 public void addSearchField(CmsSearchField sfield, String value) { 245 246 CmsSolrField field = (CmsSolrField)sfield; 247 List<String> fieldsToAdd = new ArrayList<String>(Collections.singletonList(field.getName())); 248 if ((field.getCopyFields() != null) && !field.getCopyFields().isEmpty()) { 249 fieldsToAdd.addAll(field.getCopyFields()); 250 } 251 IndexSchema schema = OpenCms.getSearchManager().getSolrServerConfiguration().getSolrSchema(); 252 for (String fieldName : fieldsToAdd) { 253 try { 254 List<String> splitedValues = new ArrayList<String>(); 255 boolean multi = false; 256 boolean overrideValue = false; 257 258 try { 259 SchemaField f = schema.getField(fieldName); 260 if ((f != null) && (!field.getName().startsWith(CmsSearchField.FIELD_CONTENT))) { 261 multi = f.multiValued(); 262 overrideValue = !multi; 263 } 264 } catch (@SuppressWarnings("unused") SolrException e) { 265 LOG.warn(Messages.get().getBundle().key(Messages.LOG_SOLR_FIELD_NOT_FOUND_1, field.toString())); 266 } 267 if (multi) { 268 splitedValues = CmsStringUtil.splitAsList(value.toString(), "\n"); 269 } else { 270 splitedValues.add(value); 271 } 272 for (String val : splitedValues) { 273 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(val)) { 274 try { 275 FieldType fieldType = schema.getFieldType(fieldName); 276 if (fieldType instanceof DatePointField) { 277 //sometime,the val is already Iso8601 formated 278 if (!val.contains("Z")) { 279 val = CmsSearchUtil.getDateAsIso8601(Long.valueOf(val).longValue()); 280 } 281 } 282 } catch (SolrException e) { 283 LOG.debug(e.getMessage(), e); 284 throw new RuntimeException(e); 285 } 286 if (fieldName.endsWith(CmsSearchField.FIELD_EXCERPT)) { 287 // TODO: make the length and the area configurable 288 val = CmsStringUtil.trimToSize(val, 1000, 50, ""); 289 } 290 if (overrideValue) { 291 m_doc.setField(fieldName, val); 292 } else { 293 m_doc.addField(fieldName, val); 294 } 295 } 296 } 297 } catch (SolrException e) { 298 LOG.error(e.getMessage(), e); 299 } catch (@SuppressWarnings("unused") RuntimeException e) { 300 // noop 301 } 302 } 303 } 304 305 /** 306 * @see org.opencms.search.I_CmsSearchDocument#addSuffixField(java.lang.String) 307 */ 308 public void addSuffixField(String suffix) { 309 310 m_doc.addField(CmsSearchField.FIELD_SUFFIX, suffix); 311 } 312 313 /** 314 * @see org.opencms.search.I_CmsSearchDocument#addTypeField(java.lang.String) 315 */ 316 public void addTypeField(String type) { 317 318 m_doc.addField(CmsSearchField.FIELD_TYPE, type); 319 } 320 321 /** 322 * @see org.opencms.search.I_CmsSearchDocument#getContentBlob() 323 */ 324 public byte[] getContentBlob() { 325 326 Object o = m_doc.getFieldValue(CmsSearchField.FIELD_CONTENT_BLOB); 327 if (o != null) { 328 if (o instanceof byte[]) { 329 return (byte[])o; 330 } 331 return o.toString().getBytes(); 332 } 333 return null; 334 } 335 336 /** 337 * @see org.opencms.search.I_CmsSearchDocument#getDocument() 338 */ 339 public Object getDocument() { 340 341 return m_doc; 342 } 343 344 /** 345 * @see org.opencms.search.I_CmsSearchDocument#getFieldNames() 346 */ 347 public List<String> getFieldNames() { 348 349 return new ArrayList<String>(m_doc.getFieldNames()); 350 } 351 352 /** 353 * @see org.opencms.search.I_CmsSearchDocument#getFieldValueAsDate(java.lang.String) 354 */ 355 public Date getFieldValueAsDate(String fieldName) { 356 357 Object o = m_doc.getFieldValue(fieldName); 358 if (o instanceof Date) { 359 return (Date)o; 360 } 361 if (o != null) { 362 try { 363 return CmsSearchUtil.parseDate(o.toString()); 364 } catch (ParseException e) { 365 // ignore: not a valid date format 366 LOG.debug(e.getLocalizedMessage(), e); 367 } 368 } 369 return null; 370 } 371 372 /** 373 * @see org.opencms.search.I_CmsSearchDocument#getFieldValueAsString(java.lang.String) 374 */ 375 public String getFieldValueAsString(String fieldName) { 376 377 List<String> values = getMultivaluedFieldAsStringList(fieldName); 378 if ((values != null) && !values.isEmpty()) { 379 return CmsStringUtil.listAsString(values, "\n"); 380 } else { 381 Object o = m_doc.getFieldValue(fieldName); 382 if (o != null) { 383 return o.toString(); 384 } 385 } 386 return null; 387 } 388 389 /** 390 * @see org.opencms.search.I_CmsSearchDocument#getMultivaluedFieldAsStringList(java.lang.String) 391 */ 392 public List<String> getMultivaluedFieldAsStringList(String fieldName) { 393 394 List<String> result = new ArrayList<String>(); 395 Collection<Object> coll = m_doc.getFieldValues(fieldName); 396 if (coll != null) { 397 for (Object o : coll) { 398 if (o != null) { 399 result.add(o.toString()); 400 } 401 } 402 return result; 403 } 404 return null; 405 } 406 407 /** 408 * @see org.opencms.search.I_CmsSearchDocument#getPath() 409 */ 410 public String getPath() { 411 412 return getFieldValueAsString(CmsSearchField.FIELD_PATH); 413 } 414 415 /** 416 * @see org.opencms.search.I_CmsSearchDocument#getScore() 417 */ 418 public float getScore() { 419 420 Float score = (Float)getSolrDocument().getFirstValue(CmsSearchField.FIELD_SCORE); 421 if (score != null) { 422 m_score = score.floatValue(); 423 return m_score; 424 } 425 return 0F; 426 } 427 428 /** 429 * Returns the Solr document.<p> 430 * 431 * @return the Solr document 432 */ 433 public SolrDocument getSolrDocument() { 434 435 return CmsSearchUtil.toSolrDocument(m_doc); 436 } 437 438 /** 439 * @see org.opencms.search.I_CmsSearchDocument#getType() 440 */ 441 public String getType() { 442 443 return getFieldValueAsString(CmsSearchField.FIELD_TYPE); 444 } 445 446 /** 447 * Sets the id of this document.<p> 448 * 449 * @param structureId the structure id to use 450 */ 451 public void setId(CmsUUID structureId) { 452 453 m_doc.addField(CmsSearchField.FIELD_ID, structureId.toString()); 454 455 } 456 457 /** 458 * @see org.opencms.search.I_CmsSearchDocument#setScore(float) 459 */ 460 public void setScore(float score) { 461 462 m_score = score; 463 } 464 465 /** 466 * @see java.lang.Object#toString() 467 */ 468 @Override 469 public String toString() { 470 471 return getFieldValueAsString(CmsSearchField.FIELD_PATH); 472 } 473}