mirror of
https://github.com/Alfresco/SearchServices.git
synced 2026-09-16 18:12:56 +00:00
Feature/search 2171 datamodel change
This commit is contained in:
+41
-2
@@ -152,6 +152,23 @@ public class AlfrescoSolrDataModel implements QueryConstants
|
||||
}
|
||||
|
||||
public static final String CONTENT_S_LOCALE_PREFIX = "content@s__locale@";
|
||||
|
||||
static final String PART_FIELDNAME_PREFIX = "part@sd@";
|
||||
|
||||
/**
|
||||
* Infix used for denoting a primitive single valued field with no doc values enabled
|
||||
*
|
||||
* @see #getFieldForText
|
||||
*/
|
||||
private static final String SINGLE_VALUE_WITHOUT_DOC_VALUES_MARKER = "@s_@";
|
||||
|
||||
/**
|
||||
* Infix used for denoting a primitive single valued field with no doc values enabled
|
||||
*
|
||||
* @see #getFieldForText
|
||||
*/
|
||||
private static final String SINGLE_VALUE_WITH_DOC_VALUES_MARKER = "@sd@";
|
||||
|
||||
static final String SHARED_PROPERTIES = "shared.properties";
|
||||
|
||||
protected final static Logger log = LoggerFactory.getLogger(AlfrescoSolrDataModel.class);
|
||||
@@ -760,7 +777,6 @@ public class AlfrescoSolrDataModel implements QueryConstants
|
||||
|
||||
private void addHighlightSearchFields( PropertyDefinition propertyDefinition , IndexedField indexedField)
|
||||
{
|
||||
|
||||
QName propertyName = propertyDefinition.getName();
|
||||
QName propertyDataTypeQName = propertyDefinition.getDataType().getName();
|
||||
String fieldName;
|
||||
@@ -1416,7 +1432,7 @@ public class AlfrescoSolrDataModel implements QueryConstants
|
||||
|
||||
public static class IndexedField
|
||||
{
|
||||
private List<FieldInstance> fields = new LinkedList<>();
|
||||
private final List<FieldInstance> fields = new LinkedList<>();
|
||||
|
||||
public List<FieldInstance> getFields()
|
||||
{
|
||||
@@ -1747,4 +1763,27 @@ public class AlfrescoSolrDataModel implements QueryConstants
|
||||
getNamespaceDAO().addPrefix("", NamespaceService.CONTENT_MODEL_1_0_URI);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the prefix used for denoting a field which is meant to represent a constituent part of a
|
||||
* date or datetime (e.g. year, month, second, minute).
|
||||
*
|
||||
* @param sourceFieldName the date/datetime source field name.
|
||||
* @param sourceDataTypeDefinition the datatype of the source field name (date or datetime)
|
||||
* @return the prefix that can be used for denoting a date or datetime part
|
||||
*/
|
||||
public String destructuredDateTimePartFieldNamePrefix(String sourceFieldName, DataTypeDefinition sourceDataTypeDefinition) {
|
||||
// source field name example: datetime@sd@{http://www.alfresco.org/model/content/1.0}created
|
||||
// prefix: datetime
|
||||
String prefix = sourceFieldName.substring(0, sourceFieldName.indexOf("@"));
|
||||
|
||||
// prefix, docValues disabled option: datetime@s_@
|
||||
String sourceFieldNamePrefixWithoutDocValues = prefix + SINGLE_VALUE_WITHOUT_DOC_VALUES_MARKER;
|
||||
|
||||
// prefix, docValues enabled option: datetime@sd@
|
||||
String sourceFieldNamePrefixWithDocValues = prefix + SINGLE_VALUE_WITH_DOC_VALUES_MARKER;
|
||||
|
||||
return sourceFieldName.replace(sourceFieldNamePrefixWithoutDocValues, PART_FIELDNAME_PREFIX)
|
||||
.replace(sourceFieldNamePrefixWithDocValues, PART_FIELDNAME_PREFIX);
|
||||
}
|
||||
}
|
||||
+111
-18
@@ -72,6 +72,7 @@ import static org.alfresco.repo.search.adaptor.lucene.QueryConstants.FIELD_VERSI
|
||||
import static org.alfresco.solr.AlfrescoSolrDataModel.getAclChangeSetDocumentId;
|
||||
import static org.alfresco.solr.AlfrescoSolrDataModel.getAclDocumentId;
|
||||
import static org.alfresco.solr.utils.Utils.notNullOrEmpty;
|
||||
import static org.alfresco.util.ISO8601DateFormat.isTimeComponentDefined;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -80,6 +81,8 @@ import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.DecimalFormat;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -118,6 +121,7 @@ import org.alfresco.repo.dictionary.M2Model;
|
||||
import org.alfresco.repo.dictionary.NamespaceDAO;
|
||||
import org.alfresco.repo.search.adaptor.lucene.QueryConstants;
|
||||
import org.alfresco.service.cmr.dictionary.AspectDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.TypeDefinition;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
@@ -363,6 +367,14 @@ public class SolrInformationServer implements InformationServer
|
||||
/** Shared property to determine if the cascade tracking is enabled. */
|
||||
public static final String CASCADE_TRACKER_ENABLED = "alfresco.cascade.tracker.enabled";
|
||||
|
||||
private static final String UNIT_OF_TIME_FIELD_INFIX = "_unit_of_time";
|
||||
static final String UNIT_OF_TIME_YEAR_FIELD_SUFFIX = UNIT_OF_TIME_FIELD_INFIX + "_year";
|
||||
static final String UNIT_OF_TIME_MONTH_FIELD_SUFFIX = UNIT_OF_TIME_FIELD_INFIX + "_month";
|
||||
static final String UNIT_OF_TIME_DAY_FIELD_SUFFIX = UNIT_OF_TIME_FIELD_INFIX + "_day";
|
||||
static final String UNIT_OF_TIME_HOUR_FIELD_SUFFIX = UNIT_OF_TIME_FIELD_INFIX + "_hour";
|
||||
static final String UNIT_OF_TIME_MINUTE_FIELD_SUFFIX = UNIT_OF_TIME_FIELD_INFIX + "_minute";
|
||||
static final String UNIT_OF_TIME_SECOND_FIELD_SUFFIX = UNIT_OF_TIME_FIELD_INFIX + "_second";
|
||||
|
||||
private final static Function<String, List<Object>> LAZY_EMPTY_MUTABLE_LIST = key -> new ArrayList<>();
|
||||
|
||||
private final AlfrescoCoreAdminHandler adminHandler;
|
||||
@@ -403,6 +415,8 @@ public class SolrInformationServer implements InformationServer
|
||||
private String skippingDocsQueryString;
|
||||
private boolean isSkippingDocsInitialized;
|
||||
|
||||
private final boolean dateFieldDestructuringHasBeenEnabledOnThisInstance;
|
||||
|
||||
static class DocListCollector implements Collector, LeafCollector
|
||||
{
|
||||
private final IntArrayList docs = new IntArrayList();
|
||||
@@ -580,6 +594,11 @@ public class SolrInformationServer implements InformationServer
|
||||
|
||||
port = portNumber(props);
|
||||
baseUrl = baseUrl(props);
|
||||
|
||||
dateFieldDestructuringHasBeenEnabledOnThisInstance = Boolean.parseBoolean(coreConfiguration.getProperty("alfresco.destructureDateFields", "true"));
|
||||
LOGGER.info(
|
||||
"Date fields destructuring has been {} on this instance.",
|
||||
dateFieldDestructuringHasBeenEnabledOnThisInstance ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2191,7 +2210,7 @@ public class SolrInformationServer implements InformationServer
|
||||
}
|
||||
}
|
||||
|
||||
static void mltextProperty(QName propertyQName, MLTextPropertyValue value, final BiConsumer<String, Object> valueHolder)
|
||||
void mltextProperty(QName propertyQName, MLTextPropertyValue value, final BiConsumer<String, Object> valueHolder)
|
||||
{
|
||||
AlfrescoSolrDataModel dataModel = AlfrescoSolrDataModel.getInstance();
|
||||
List<FieldInstance> fields = dataModel.getIndexedFieldNamesForProperty(propertyQName).getFields();
|
||||
@@ -2204,7 +2223,7 @@ public class SolrInformationServer implements InformationServer
|
||||
.forEach(field -> addMLTextProperty(valueHolder, field, value));
|
||||
}
|
||||
|
||||
static void stringProperty(QName propertyQName, StringPropertyValue value, PropertyValue locale, final BiConsumer<String, Object> valueHolder)
|
||||
void stringProperty(QName propertyQName, StringPropertyValue value, PropertyValue locale, final BiConsumer<String, Object> valueHolder)
|
||||
{
|
||||
AlfrescoSolrDataModel dataModel = AlfrescoSolrDataModel.getInstance();
|
||||
PropertyDefinition definition = dataModel.getPropertyDefinition(propertyQName);
|
||||
@@ -2217,15 +2236,21 @@ public class SolrInformationServer implements InformationServer
|
||||
dataModel.getIndexedFieldNamesForProperty(propertyQName).getFields()
|
||||
.stream()
|
||||
.filter(field -> field.getField().startsWith("text@sd___@"))
|
||||
.forEach(field -> addStringProperty(valueHolder, field, value, locale));
|
||||
.forEach(field -> addStringProperty(valueHolder, field, value, locale, definition));
|
||||
} else
|
||||
{
|
||||
dataModel.getIndexedFieldNamesForProperty(propertyQName).getFields()
|
||||
.forEach(field -> addStringProperty(valueHolder, field, value, locale));
|
||||
.forEach(field -> {
|
||||
if (canBeDestructured(definition, field.getField()))
|
||||
{
|
||||
setUnitOfTimeFields(valueHolder, field.getField(), value.getValue(), definition.getDataType());
|
||||
}
|
||||
addStringProperty(valueHolder, field, value, locale, definition);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static void populateProperties(
|
||||
void populateProperties(
|
||||
Map<QName, PropertyValue> properties,
|
||||
boolean contentIndexingHasBeenRequestedForThisNode,
|
||||
SolrInputDocument document,
|
||||
@@ -2384,7 +2409,7 @@ public class SolrInformationServer implements InformationServer
|
||||
}
|
||||
}
|
||||
|
||||
private static void addContentPropertyMetadata(
|
||||
private void addContentPropertyMetadata(
|
||||
BiConsumer<String, Object> consumer,
|
||||
QName propertyQName,
|
||||
ContentPropertyValue contentPropertyValue,
|
||||
@@ -2424,7 +2449,7 @@ public class SolrInformationServer implements InformationServer
|
||||
*
|
||||
* @see #insertContentUpdateMarker(SolrInputDocument, ContentPropertyValue)
|
||||
*/
|
||||
private static void markAsContentInSynch(SolrInputDocument document)
|
||||
private void markAsContentInSynch(SolrInputDocument document)
|
||||
{
|
||||
markAsContentInSynch(document, (ContentPropertyValue)null);
|
||||
}
|
||||
@@ -2435,7 +2460,7 @@ public class SolrInformationServer implements InformationServer
|
||||
*
|
||||
* @see #insertContentUpdateMarker(SolrInputDocument, ContentPropertyValue)
|
||||
*/
|
||||
private static void markAsContentInSynch(SolrInputDocument document, ContentPropertyValue value)
|
||||
private void markAsContentInSynch(SolrInputDocument document, ContentPropertyValue value)
|
||||
{
|
||||
ofNullable(value)
|
||||
.map(ContentPropertyValue::getId)
|
||||
@@ -2450,7 +2475,7 @@ public class SolrInformationServer implements InformationServer
|
||||
*
|
||||
* @see #insertContentUpdateMarker(SolrInputDocument, ContentPropertyValue)
|
||||
*/
|
||||
private static void markAsContentInSynch(SolrInputDocument document, Long id)
|
||||
private void markAsContentInSynch(SolrInputDocument document, Long id)
|
||||
{
|
||||
long contentVersionId = ofNullable(id).orElse(CONTENT_UPDATED_MARKER);
|
||||
|
||||
@@ -2513,7 +2538,7 @@ public class SolrInformationServer implements InformationServer
|
||||
*
|
||||
* As a side note, please keep in mind the FTSSTATUS field has been removed.
|
||||
*/
|
||||
private static void insertContentUpdateMarker(SolrInputDocument document, ContentPropertyValue value)
|
||||
private void insertContentUpdateMarker(SolrInputDocument document, ContentPropertyValue value)
|
||||
{
|
||||
ofNullable(value)
|
||||
.map(ContentPropertyValue::getId)
|
||||
@@ -2524,7 +2549,7 @@ public class SolrInformationServer implements InformationServer
|
||||
() -> document.setField(LAST_INCOMING_CONTENT_VERSION_ID, CONTENT_OUTDATED_MARKER));
|
||||
}
|
||||
|
||||
private static void addContentProperty(
|
||||
private void addContentProperty(
|
||||
BiConsumer<String, Object> consumer,
|
||||
SolrInputDocument document,
|
||||
QName propertyName,
|
||||
@@ -2629,7 +2654,7 @@ public class SolrInformationServer implements InformationServer
|
||||
return indexOfSeparator == -1 ? locale : locale.substring(0, indexOfSeparator);
|
||||
}
|
||||
|
||||
private static List<String> getLocalisedValues(MLTextPropertyValue mlTextPropertyValue)
|
||||
private List<String> getLocalisedValues(MLTextPropertyValue mlTextPropertyValue)
|
||||
{
|
||||
if (mlTextPropertyValue == null)
|
||||
{
|
||||
@@ -2650,7 +2675,7 @@ public class SolrInformationServer implements InformationServer
|
||||
return values;
|
||||
}
|
||||
|
||||
private static void addMLTextProperty(BiConsumer<String, Object> consumer, FieldInstance field, MLTextPropertyValue mlTextPropertyValue)
|
||||
private void addMLTextProperty(BiConsumer<String, Object> consumer, FieldInstance field, MLTextPropertyValue mlTextPropertyValue)
|
||||
{
|
||||
if (mlTextPropertyValue == null)
|
||||
{
|
||||
@@ -3700,7 +3725,7 @@ public class SolrInformationServer implements InformationServer
|
||||
}
|
||||
}
|
||||
|
||||
private static String getLocalisedValue(StringPropertyValue property, PropertyValue localeProperty)
|
||||
private String getLocalisedValue(StringPropertyValue property, PropertyValue localeProperty)
|
||||
{
|
||||
Locale locale =
|
||||
ofNullable(localeProperty)
|
||||
@@ -3712,12 +3737,80 @@ public class SolrInformationServer implements InformationServer
|
||||
return "\u0000" + locale.getLanguage() + "\u0000" + property.getValue();
|
||||
}
|
||||
|
||||
private static void addStringProperty(BiConsumer<String, Object> consumer, FieldInstance field, StringPropertyValue property, PropertyValue localeProperty)
|
||||
private void addStringProperty(BiConsumer<String, Object> consumer, FieldInstance field, StringPropertyValue property, PropertyValue localeProperty, PropertyDefinition definition)
|
||||
{
|
||||
consumer.accept(field.getField(), field.isLocalised() ? getLocalisedValue(property, localeProperty) : property.getValue());
|
||||
}
|
||||
|
||||
private static void addFieldIfNotSet(SolrInputDocument doc, String name)
|
||||
/**
|
||||
* Checks if the given property definition refers to a field that can be destructured in parts.
|
||||
* A field can be destructured only if:
|
||||
*
|
||||
* <ul>
|
||||
* <li>It is not multivalued</li>
|
||||
* <li>Destructuring is not disabled in configuration</li>
|
||||
* <li>Its data type is {@link DataTypeDefinition#DATE} or {@link DataTypeDefinition#DATETIME}</li>
|
||||
* </ul>
|
||||
*/
|
||||
boolean canBeDestructured(PropertyDefinition definition, String fieldName)
|
||||
{
|
||||
return !definition.isMultiValued() &&
|
||||
fieldName != null &&
|
||||
fieldName.contains("@") && // avoid static date/datetime fields
|
||||
dateFieldDestructuringHasBeenEnabledOnThisInstance &&
|
||||
(definition.getDataType().getName().equals(DataTypeDefinition.DATETIME) ||
|
||||
definition.getDataType().getName().equals(DataTypeDefinition.DATE));
|
||||
}
|
||||
|
||||
/**
|
||||
* To add support for SQL date functions SEARCH-2171 introduced date and datetime fields destructuration.
|
||||
* Other than being indexed as plain TrieDate fields, date and datetime fields are also indexed in separate fields that
|
||||
* contain their constituent parts.
|
||||
*
|
||||
* Date fields:
|
||||
*
|
||||
* <ul>
|
||||
* <li>YEAR</li>
|
||||
* <li>MONTH (1-12)</li>
|
||||
* <li>DAY (OF THE MONTH)</li>
|
||||
* </ul>
|
||||
*
|
||||
* Datetime fields also add the following:
|
||||
*
|
||||
* <ul>
|
||||
* <li>HOUR (0-23)</li>
|
||||
* <li>MINUTE</li>
|
||||
* <li>SECOND</li>
|
||||
* </ul>
|
||||
*
|
||||
* Note the destructured parts are not localised to a specific Timezone: they are always expressed in UTC.
|
||||
*
|
||||
* @see <a href="https://issues.alfresco.com/jira/browse/SEARCH-2171">SEARCH-2171</a>
|
||||
*/
|
||||
void setUnitOfTimeFields(BiConsumer<String, Object> consumer, String sourceFieldName, String value, DataTypeDefinition dataType)
|
||||
{
|
||||
try
|
||||
{
|
||||
String fieldNamePrefix = dataModel.destructuredDateTimePartFieldNamePrefix(sourceFieldName, dataType);
|
||||
ZonedDateTime dateTime = ZonedDateTime.parse(value, DateTimeFormatter.ISO_ZONED_DATE_TIME);
|
||||
consumer.accept(fieldNamePrefix + UNIT_OF_TIME_YEAR_FIELD_SUFFIX, dateTime.getYear());
|
||||
consumer.accept(fieldNamePrefix + UNIT_OF_TIME_MONTH_FIELD_SUFFIX, dateTime.getMonth().getValue());
|
||||
consumer.accept(fieldNamePrefix + UNIT_OF_TIME_DAY_FIELD_SUFFIX, dateTime.getDayOfMonth());
|
||||
|
||||
if (DataTypeDefinition.DATETIME.equals(dataType.getName()) && isTimeComponentDefined(value))
|
||||
{
|
||||
consumer.accept(fieldNamePrefix + UNIT_OF_TIME_MINUTE_FIELD_SUFFIX, dateTime.getMinute());
|
||||
consumer.accept(fieldNamePrefix + UNIT_OF_TIME_HOUR_FIELD_SUFFIX, dateTime.getHour());
|
||||
consumer.accept(fieldNamePrefix + UNIT_OF_TIME_SECOND_FIELD_SUFFIX, dateTime.getSecond());
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
LOGGER.error("Unable to destructure date/datetime value {} (Field was {})", value, sourceFieldName, exception);
|
||||
}
|
||||
}
|
||||
|
||||
private void addFieldIfNotSet(SolrInputDocument doc, String name)
|
||||
{
|
||||
doc.addField(FIELD_FIELDS, name);
|
||||
}
|
||||
@@ -3796,12 +3889,12 @@ public class SolrInformationServer implements InformationServer
|
||||
return searchers;
|
||||
}
|
||||
|
||||
private static void clearFields(SolrInputDocument document, List<String> fields)
|
||||
private void clearFields(SolrInputDocument document, List<String> fields)
|
||||
{
|
||||
notNullOrEmpty(fields).forEach(document::removeField);
|
||||
}
|
||||
|
||||
private static void clearFields(SolrInputDocument document, String... fields)
|
||||
private void clearFields(SolrInputDocument document, String... fields)
|
||||
{
|
||||
stream(notNullOrEmpty(fields)).forEach(document::removeField);
|
||||
}
|
||||
|
||||
+13
-1
@@ -660,7 +660,19 @@
|
||||
<!-- Single value indexed -->
|
||||
<dynamicField name="any@s_@*" type="oldStandardAnalysis" indexed="true" omitNorms="true" stored="false" multiValued="false" sortMissingLast="true" />
|
||||
<dynamicField name="encrypted@s_@*" type="oldStandardAnalysis" indexed="true" omitNorms="true" stored="false" multiValued="false" sortMissingLast="true" />
|
||||
<dynamicField name="int@s_@*" type="int" indexed="true" omitNorms="true" stored="false" multiValued="false" sortMissingLast="false" sortMissingFirst="false" />
|
||||
|
||||
<!--
|
||||
SEARCH-2171 introduced these fields for capturing the destructured parts of a date/datetime field.
|
||||
An investigation [1] revealed the optimal field/field type definition for these fields doesn't match any current
|
||||
field / field type currently defined. The closest is the "int" datatype but we still need to override some option.
|
||||
As consequence of that, until we work on a refactoring ticket for primitive datatypes, we need to define a specific
|
||||
field for destructured date / datetime parts.
|
||||
|
||||
[1] https://issues.alfresco.com/jira/browse/SEARCH-2171?focusedCommentId=767859&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-767859
|
||||
-->
|
||||
<dynamicField name="part@sd@*" type="int" indexed="true" stored="false" docValues="true"/>
|
||||
|
||||
<dynamicField name="int@s_@*" type="int" indexed="true" omitNorms="true" stored="false" multiValued="false" sortMissingLast="false" sortMissingFirst="false" />
|
||||
<dynamicField name="long@s_@*" type="long" indexed="true" omitNorms="true" stored="false" multiValued="false" sortMissingLast="false" sortMissingFirst="false" />
|
||||
<dynamicField name="float@s_@*" type="float" indexed="true" omitNorms="true" stored="false" multiValued="false" sortMissingLast="false" sortMissingFirst="false" />
|
||||
<dynamicField name="double@s_@*" type="double" indexed="true" omitNorms="true" stored="false" multiValued="false" sortMissingLast="false" sortMissingFirst="false" />
|
||||
|
||||
+10
@@ -168,6 +168,16 @@ alfresco.metadata.ignore.datatype.1=app:configurations
|
||||
alfresco.metadata.skipDescendantDocsForSpecificAspects=false
|
||||
#alfresco.metadata.ignore.aspect.0=
|
||||
|
||||
# Date/Datetime fields only: if this property is set to true (default value) each date/datetime field
|
||||
#
|
||||
# - will be indexed as a whole value
|
||||
# - will generate additional fields corresponding to its constituent parts (year, month, day, hour, minute, second)
|
||||
#
|
||||
# If this property is set to false then the only the whole value is indexed.
|
||||
# This will result in a smaller index, but date function support will be disabled.
|
||||
#
|
||||
# alfresco.destructureDateFields=true
|
||||
|
||||
# The number of matches from the index to include when rewriting wildcard search terms as an OR-ed list.
|
||||
alfresco.topTermSpanRewriteLimit=1000
|
||||
|
||||
|
||||
+12
@@ -1067,6 +1067,18 @@
|
||||
<!-- Single value indexed -->
|
||||
<dynamicField name="any@s_@*" type="oldStandardAnalysis" sortMissingLast="true" />
|
||||
<dynamicField name="encrypted@s_@*" type="oldStandardAnalysis" sortMissingLast="true" />
|
||||
|
||||
<!--
|
||||
SEARCH-2171 introduced these fields for capturing the destructured parts of a date/datetime field.
|
||||
An investigation [1] revealed the optimal field/field type definition for these fields doesn't match any current
|
||||
field / field type currently defined. The closest is the "int" datatype but we still need to override some option.
|
||||
As consequence of that, until we work on a refactoring ticket for primitive datatypes, we need to define a specific
|
||||
field for destructured date / datetime parts.
|
||||
|
||||
[1] https://issues.alfresco.com/jira/browse/SEARCH-2171?focusedCommentId=767859&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-767859
|
||||
-->
|
||||
<dynamicField name="part@sd@*" type="int" indexed="true" stored="false" docValues="true"/>
|
||||
|
||||
<dynamicField name="int@s_@*" type="int" />
|
||||
<dynamicField name="long@s_@*" type="long" />
|
||||
<dynamicField name="float@s_@*" type="float" />
|
||||
|
||||
+10
@@ -165,6 +165,16 @@ alfresco.metadata.ignore.datatype.1=app:configurations
|
||||
alfresco.metadata.skipDescendantDocsForSpecificAspects=false
|
||||
#alfresco.metadata.ignore.aspect.0=
|
||||
|
||||
# Date/Datetime fields only: if this property is set to true (default value) each date/datetime field
|
||||
#
|
||||
# - will be indexed as a whole value
|
||||
# - will generate additional fields corresponding to its constituent parts (year, month, day, hour, minute, second)
|
||||
#
|
||||
# If this property is set to false then the only the whole value is indexed.
|
||||
# This will result in a smaller index, but date function support will be disabled.
|
||||
#
|
||||
# alfresco.destructureDateFields=true
|
||||
|
||||
# The number of matches from the index to include when rewriting wildcard search terms as an OR-ed list.
|
||||
alfresco.topTermSpanRewriteLimit=1000
|
||||
|
||||
|
||||
+2
-2
@@ -86,7 +86,7 @@ public class AlfrescoSolrReloadIT extends AbstractAlfrescoSolrIT
|
||||
waitForDocCount(new TermQuery(new Term("content@s___t@{http://www.alfresco.org/model/content/1.0}content", "world")), numNodes, 100000);
|
||||
|
||||
Collection<Tracker> trackers = getTrackers();
|
||||
|
||||
|
||||
|
||||
int numOfTrackers = trackers.size();
|
||||
int jobs = getJobsCount();
|
||||
@@ -125,4 +125,4 @@ public class AlfrescoSolrReloadIT extends AbstractAlfrescoSolrIT
|
||||
{
|
||||
return admin.getScheduler().getJobsCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-4
@@ -477,11 +477,14 @@ public class AlfrescoSolrUtils
|
||||
SolrServletRequest solrQueryRequest = null;
|
||||
try
|
||||
{
|
||||
AlfrescoCoreAdminHandler admin = (AlfrescoCoreAdminHandler) core.getCoreContainer().getMultiCoreHandler();
|
||||
SolrInformationServer solrInformationServer = (SolrInformationServer) admin.getInformationServers().get(core.getName());
|
||||
|
||||
solrQueryRequest = new SolrServletRequest(core, null);
|
||||
AddUpdateCommand addDocCmd = new AddUpdateCommand(solrQueryRequest);
|
||||
addDocCmd.overwrite = true;
|
||||
addDocCmd.solrDoc = createDocument(dataModel, new Long(txid), new Long(dbid), nodeRef, type, aspects,
|
||||
properties, content, new Long(aclid), paths, owner, parentAssocs, ancestors);
|
||||
properties, content, new Long(aclid), paths, owner, parentAssocs, ancestors, solrInformationServer);
|
||||
core.getUpdateHandler().addDoc(addDocCmd);
|
||||
if (commit)
|
||||
{
|
||||
@@ -528,7 +531,8 @@ public class AlfrescoSolrUtils
|
||||
String[] paths,
|
||||
String owner,
|
||||
ChildAssociationRef[] parentAssocs,
|
||||
NodeRef[] ancestors)throws IOException
|
||||
NodeRef[] ancestors,
|
||||
SolrInformationServer solrInformationServer)
|
||||
{
|
||||
SolrInputDocument doc = new SolrInputDocument();
|
||||
String id = AlfrescoSolrDataModel.getNodeDocumentId(AlfrescoSolrDataModel.DEFAULT_TENANT, dbid);
|
||||
@@ -615,7 +619,7 @@ public class AlfrescoSolrUtils
|
||||
{
|
||||
final boolean isContentIndexedForNode = true;
|
||||
final boolean transformContentFlag = true;
|
||||
SolrInformationServer.populateProperties(properties, isContentIndexedForNode, doc, transformContentFlag);
|
||||
solrInformationServer.populateProperties(properties, isContentIndexedForNode, doc, transformContentFlag);
|
||||
if (content != null)
|
||||
{
|
||||
addContentToDoc(doc, content);
|
||||
@@ -740,12 +744,15 @@ public class AlfrescoSolrUtils
|
||||
SolrServletRequest solrQueryRequest = null;
|
||||
try
|
||||
{
|
||||
AlfrescoCoreAdminHandler admin = (AlfrescoCoreAdminHandler) core.getCoreContainer().getMultiCoreHandler();
|
||||
SolrInformationServer solrInformationServer = (SolrInformationServer) admin.getInformationServers().get(core.getName());
|
||||
|
||||
solrQueryRequest = new SolrServletRequest(core, null);
|
||||
AddUpdateCommand addDocCmd = new AddUpdateCommand(solrQueryRequest);
|
||||
addDocCmd.overwrite = true;
|
||||
addDocCmd.solrDoc = createDocument(dataModel, new Long(txid), new Long(dbid), rootNodeRef,
|
||||
ContentModel.TYPE_STOREROOT, new QName[]{ContentModel.ASPECT_ROOT}, null, null, new Long(aclid),
|
||||
new String[]{"/"}, "system", null, null);
|
||||
new String[]{"/"}, "system", null, null, solrInformationServer);
|
||||
core.getUpdateHandler().addDoc(addDocCmd);
|
||||
addAcl(solrQueryRequest, core, dataModel, acltxid, aclid, 0, 0);
|
||||
AddUpdateCommand txCmd = new AddUpdateCommand(solrQueryRequest);
|
||||
|
||||
+175
-1
@@ -20,9 +20,13 @@
|
||||
package org.alfresco.solr;
|
||||
|
||||
import java.util.Properties;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
||||
import org.alfresco.solr.client.SOLRAPIClient;
|
||||
import org.apache.solr.common.SolrDocument;
|
||||
import org.apache.solr.common.SolrInputDocument;
|
||||
import org.apache.solr.common.params.CommonParams;
|
||||
import org.apache.solr.common.util.SimpleOrderedMap;
|
||||
import org.apache.solr.core.SolrCore;
|
||||
@@ -34,11 +38,40 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.ANY;
|
||||
import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.ASSOC_REF;
|
||||
import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.BOOLEAN;
|
||||
import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.CATEGORY;
|
||||
import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.CHILD_ASSOC_REF;
|
||||
import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.CONTENT;
|
||||
import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.DOUBLE;
|
||||
import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.ENCRYPTED;
|
||||
import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.FLOAT;
|
||||
import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.INT;
|
||||
import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.LOCALE;
|
||||
import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.LONG;
|
||||
import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.MLTEXT;
|
||||
import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.NODE_REF;
|
||||
import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.PATH;
|
||||
import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.PERIOD;
|
||||
import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.QNAME;
|
||||
import static org.alfresco.service.cmr.dictionary.DataTypeDefinition.TEXT;
|
||||
import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_DAY_FIELD_SUFFIX;
|
||||
import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_HOUR_FIELD_SUFFIX;
|
||||
import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_MINUTE_FIELD_SUFFIX;
|
||||
import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_MONTH_FIELD_SUFFIX;
|
||||
import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_SECOND_FIELD_SUFFIX;
|
||||
import static org.alfresco.solr.SolrInformationServer.UNIT_OF_TIME_YEAR_FIELD_SUFFIX;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -112,6 +145,147 @@ public class SolrInformationServerTest
|
||||
assertSame(state, document);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setUnitOfTimeFieldsWithDatetimeField_shouldSetDateAndTimeFields()
|
||||
{
|
||||
DataTypeDefinition datatype = mock(DataTypeDefinition.class);
|
||||
when(datatype.getName()).thenReturn(DataTypeDefinition.DATETIME);
|
||||
|
||||
SolrInputDocument document = new SolrInputDocument();
|
||||
|
||||
String fieldPrefix = "datetime@sd@";
|
||||
String fieldSuffix = "{http://www.alfresco.org/model/content/1.0}created";
|
||||
String sourceFieldName = fieldPrefix + fieldSuffix;
|
||||
|
||||
infoServer.setUnitOfTimeFields(document::setField, sourceFieldName, "1972-09-16T17:33:18Z", datatype);
|
||||
|
||||
assertEquals(1972,
|
||||
unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_YEAR_FIELD_SUFFIX));
|
||||
assertEquals(9,
|
||||
unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_MONTH_FIELD_SUFFIX));
|
||||
assertEquals(16,
|
||||
unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_DAY_FIELD_SUFFIX));
|
||||
assertEquals(17,
|
||||
unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_HOUR_FIELD_SUFFIX));
|
||||
assertEquals(33,
|
||||
unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_MINUTE_FIELD_SUFFIX));
|
||||
assertEquals(18,
|
||||
unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_SECOND_FIELD_SUFFIX));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setUnitOfTimeFieldsWithDatetimeField_shouldSetOnlyDateFields()
|
||||
{
|
||||
DataTypeDefinition datatype = mock(DataTypeDefinition.class);
|
||||
when(datatype.getName()).thenReturn(DataTypeDefinition.DATE);
|
||||
|
||||
SolrInputDocument document = new SolrInputDocument();
|
||||
|
||||
String fieldPrefix = "datetime@sd@";
|
||||
String fieldSuffix = "{http://www.alfresco.org/model/content/1.0}created";
|
||||
String sourceFieldName = fieldPrefix + fieldSuffix;
|
||||
|
||||
infoServer.setUnitOfTimeFields(document::setField, sourceFieldName, "1972-09-16T17:33:18Z", datatype);
|
||||
|
||||
assertEquals(1972,
|
||||
unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_YEAR_FIELD_SUFFIX));
|
||||
assertEquals(9,
|
||||
unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_MONTH_FIELD_SUFFIX));
|
||||
assertEquals(16,
|
||||
unitOfTimeFieldValue(document, AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_DAY_FIELD_SUFFIX));
|
||||
|
||||
assertFalse(document.containsKey(AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_HOUR_FIELD_SUFFIX));
|
||||
assertFalse(document.containsKey(AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_MINUTE_FIELD_SUFFIX));
|
||||
assertFalse(document.containsKey(AlfrescoSolrDataModel.PART_FIELDNAME_PREFIX + fieldSuffix + UNIT_OF_TIME_SECOND_FIELD_SUFFIX));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void destructuringCannotBeAppliedToMultivaluedFields()
|
||||
{
|
||||
PropertyDefinition multiValuedProperty = mock(PropertyDefinition.class);
|
||||
when(multiValuedProperty.isMultiValued()).thenReturn(true);
|
||||
|
||||
assertFalse(infoServer.canBeDestructured(multiValuedProperty, "datetime@sd@{http://www.alfresco.org/model/content/1.0}created"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructuring cannot only be applied to those fields matching a dynamic field in the schema.
|
||||
* The dynamic fields defined in the schema follow the naming:
|
||||
*
|
||||
* [datatype]@[options]@[namespace][local name]
|
||||
*
|
||||
* For example
|
||||
*
|
||||
* datetime@sd@{http://www.alfresco.org/model/content/1.0}created
|
||||
*
|
||||
* SearchServices calls those fields "PROPERTIES", while the static fields (even belonging to a Node) are called
|
||||
* "FIELDS".
|
||||
*/
|
||||
@Test
|
||||
public void destructuringCannotBeAppliedToProperties()
|
||||
{
|
||||
PropertyDefinition multiValuedProperty = mock(PropertyDefinition.class);
|
||||
when(multiValuedProperty.isMultiValued()).thenReturn(false);
|
||||
|
||||
assertFalse(
|
||||
"Destructuring can only be applied to Node properties (e.g. dynamic fields in Solr schema)",
|
||||
infoServer.canBeDestructured(multiValuedProperty, "this_is_a_field_that_doesnt_follow_the_dynamic_naming"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void destructuringCanBeAppliedToDateFields()
|
||||
{
|
||||
PropertyDefinition propertyThatCanBeDestructured = mock(PropertyDefinition.class);
|
||||
when(propertyThatCanBeDestructured.isMultiValued()).thenReturn(false);
|
||||
|
||||
DataTypeDefinition date = mock(DataTypeDefinition.class);
|
||||
when(date.getName()).thenReturn(DataTypeDefinition.DATE);
|
||||
when(propertyThatCanBeDestructured.getDataType()).thenReturn(date);
|
||||
|
||||
assertTrue(
|
||||
"Destructuring must be supported in Date fields!",
|
||||
infoServer.canBeDestructured(propertyThatCanBeDestructured,"date@sd@{http://www.alfresco.org/model/content/1.0}created"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void destructuringCanBeAppliedToDateTimeFields()
|
||||
{
|
||||
PropertyDefinition propertyThatCanBeDestructured = mock(PropertyDefinition.class);
|
||||
when(propertyThatCanBeDestructured.isMultiValued()).thenReturn(false);
|
||||
|
||||
DataTypeDefinition datetime = mock(DataTypeDefinition.class);
|
||||
when(datetime.getName()).thenReturn(DataTypeDefinition.DATETIME);
|
||||
when(propertyThatCanBeDestructured.getDataType()).thenReturn(datetime);
|
||||
|
||||
assertTrue(
|
||||
"Destructuring must be supported in Datetime fields!",
|
||||
infoServer.canBeDestructured(propertyThatCanBeDestructured, "datetime@sd@{http://www.alfresco.org/model/content/1.0}created"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void destructuringCanBeAppliedOnlyToDateOrDatetimeFields()
|
||||
{
|
||||
Stream.of(ANY,ENCRYPTED,TEXT,MLTEXT,CONTENT,INT,LONG,FLOAT,DOUBLE,
|
||||
BOOLEAN,QNAME,CATEGORY,NODE_REF,CHILD_ASSOC_REF,ASSOC_REF,
|
||||
PATH,LOCALE,PERIOD)
|
||||
.map(qname -> {
|
||||
PropertyDefinition propertyThatCannotBeDestructured = mock(PropertyDefinition.class);
|
||||
DataTypeDefinition def = Mockito.mock(DataTypeDefinition.class);
|
||||
when(def.getName()).thenReturn(qname);
|
||||
when(propertyThatCannotBeDestructured.getDataType()).thenReturn(def);
|
||||
return propertyThatCannotBeDestructured;})
|
||||
.forEach(property -> assertFalse(
|
||||
"Destructuring must be supported only on Date or Datetime fields!",
|
||||
infoServer.canBeDestructured(property, "somedatatype@sd@{http://www.alfresco.org/model/content/1.0}somefield")));
|
||||
}
|
||||
|
||||
private int unitOfTimeFieldValue(SolrInputDocument doc, String fieldName) {
|
||||
return ofNullable(doc.getFieldValue(fieldName))
|
||||
.map(Number.class::cast)
|
||||
.map(Number::intValue)
|
||||
.orElseThrow(() -> new IllegalArgumentException(fieldName + " hasn't been set."));
|
||||
}
|
||||
|
||||
/**
|
||||
* GetState returns null in case the given id doesn't correspond to an existing state document.
|
||||
*/
|
||||
@@ -134,4 +308,4 @@ public class SolrInformationServerTest
|
||||
|
||||
assertNull(document);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user