[MNT-24377] Fixed search date math by adding exclusion for date fields in a fix for SEARCH-779 (#2182)

This commit is contained in:
Cezary Witkowski
2025-02-17 13:11:41 +01:00
committed by GitHub
parent 48f3ace274
commit 5bef0651ca
4 changed files with 164 additions and 21 deletions

View File

@@ -119,6 +119,7 @@ import org.apache.solr.handler.component.ShardHandlerFactory;
import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.DocsStreamer; import org.apache.solr.response.DocsStreamer;
import org.apache.solr.schema.IndexSchema; import org.apache.solr.schema.IndexSchema;
import org.apache.solr.schema.NumberType;
import org.apache.solr.schema.SchemaField; import org.apache.solr.schema.SchemaField;
import org.apache.solr.search.SolrIndexSearcher; import org.apache.solr.search.SolrIndexSearcher;
import org.apache.solr.update.UpdateShardHandlerConfig; import org.apache.solr.update.UpdateShardHandlerConfig;
@@ -1517,27 +1518,17 @@ public class Solr4QueryParser extends QueryParser implements QueryConstants
// make sure the field exists or return a dummy query so we have no // make sure the field exists or return a dummy query so we have no
// error ....ACE-3231 // error ....ACE-3231
SchemaField schemaField = schema.getFieldOrNull(field); SchemaField schemaField = schema.getFieldOrNull(field);
boolean isNumeric = false;
if (schemaField == null) if (schemaField == null || schemaField.getType() == null)
{ {
return new TermQuery(new Term("_dummy_", "_miss_")); return new TermQuery(new Term("_dummy_", "_miss_"));
} }
else
{ NumberType schemaFieldNumberType = schemaField.getType().getNumberType();
isNumeric = (schemaField.getType().getNumericType() != null); if (isNonParsableNumberType(schemaFieldNumberType, queryText))
if (isNumeric)
{
//Check to see if queryText is numeric or else it will fail.
try
{
Double.valueOf(queryText);
}
catch (NumberFormatException e)
{ {
return new TermQuery(new Term("_dummy_", "_miss_")); return new TermQuery(new Term("_dummy_", "_miss_"));
} }
}
}
// Use the analyzer to get all the tokens, and then build a TermQuery, // Use the analyzer to get all the tokens, and then build a TermQuery,
// PhraseQuery, or noth // PhraseQuery, or noth
@@ -2357,7 +2348,8 @@ public class Solr4QueryParser extends QueryParser implements QueryConstants
{ {
nextToken = list.get(0); nextToken = list.get(0);
String termText = nextToken.toString(); String termText = nextToken.toString();
if (!isNumeric && (termText.contains("*") || termText.contains("?"))) boolean isNotNumberType = schemaFieldNumberType == null;
if (isNotNumberType && (termText.contains("*") || termText.contains("?")))
{ {
return newWildcardQuery(new Term(field, termText)); return newWildcardQuery(new Term(field, termText));
} }
@@ -2642,6 +2634,24 @@ public class Solr4QueryParser extends QueryParser implements QueryConstants
} }
} }
private boolean isNonParsableNumberType(NumberType schemaFieldNumberType, String queryText)
{
boolean isNumberType = schemaFieldNumberType != null;
boolean isNotDate = schemaFieldNumberType != NumberType.DATE;
if (isNumberType && isNotDate)
{
try
{
Double.parseDouble(queryText);
}
catch (NumberFormatException e)
{
return true;
}
}
return false;
}
/** /**
* @param list * @param list
* @return * @return

View File

@@ -60,10 +60,11 @@ import org.alfresco.solr.client.PropertyValue;
import org.alfresco.solr.client.StringPropertyValue; import org.alfresco.solr.client.StringPropertyValue;
import org.alfresco.solr.client.Transaction; import org.alfresco.solr.client.Transaction;
import org.alfresco.util.ISO9075; import org.alfresco.util.ISO9075;
import org.apache.solr.client.solrj.io.Tuple;
import org.apache.solr.core.SolrCore; import org.apache.solr.core.SolrCore;
import org.apache.solr.util.TestHarness; import org.apache.solr.util.TestHarness;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.util.AbstractMap; import java.util.AbstractMap;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
@@ -814,6 +815,26 @@ public class TestDataProvider implements AlfrescoSolrConstants
QName n32QName = QName.createQName(CONTENT_MODEL_1_0_URI, "thirtytwo"); QName n32QName = QName.createQName(CONTENT_MODEL_1_0_URI, "thirtytwo");
ChildAssociationRef n32CAR = new ChildAssociationRef(ASSOC_CONTAINS, rootNodeRef, n32QName, n32NodeRef, true, 0); ChildAssociationRef n32CAR = new ChildAssociationRef(ASSOC_CONTAINS, rootNodeRef, n32QName, n32NodeRef, true, 0);
addNode(core, dataModel, 1, 32, 1, TYPE_CONTENT, null, properties32, null, "system", new ChildAssociationRef[] {n32CAR}, new NodeRef[] {rootNodeRef}, new String[] { "/" + n32QName.toString() }, n32NodeRef, true); addNode(core, dataModel, 1, 32, 1, TYPE_CONTENT, null, properties32, null, "system", new ChildAssociationRef[] {n32CAR}, new NodeRef[] {rootNodeRef}, new String[] { "/" + n32QName.toString() }, n32NodeRef, true);
String acmeNamespaceURI = "http://www.acme.org/model/content/1.0";
QName propertyQname = QName.createQName(acmeNamespaceURI, "date");
QName acmeDocumentQName = QName.createQName(acmeNamespaceURI, "document");
Map<QName, PropertyValue> properties33 = new HashMap<>();
String todayStartOfDay = LocalDate.now().atStartOfDay().toInstant(ZoneOffset.UTC).toString();
properties33.put(propertyQname, value(todayStartOfDay));
NodeRef n33NodeRef = newNodeRef();
QName n33QName = QName.createQName(acmeNamespaceURI, "thirtythree");
ChildAssociationRef n33CAR = new ChildAssociationRef(ASSOC_CONTAINS, rootNodeRef, n33QName, n33NodeRef, true, 0);
addNode(core, dataModel, 1, 33, 1, acmeDocumentQName, null, properties33, null, "system", new ChildAssociationRef[] {n33CAR}, new NodeRef[] {rootNodeRef}, new String[] { "/" + n33QName.toString() }, n33NodeRef, true);
Map<QName, PropertyValue> properties34 = new HashMap<>();
String yesterdayStartOfDay = LocalDate.now().atStartOfDay().minusDays(1).toInstant(ZoneOffset.UTC).toString();
properties34.put(propertyQname, value(yesterdayStartOfDay));
NodeRef n34NodeRef = newNodeRef();
QName n34QName = QName.createQName(acmeNamespaceURI, "thirtyfour");
ChildAssociationRef n34CAR = new ChildAssociationRef(ASSOC_CONTAINS, rootNodeRef, n34QName, n34NodeRef, true, 0);
addNode(core, dataModel, 1, 34, 1, acmeDocumentQName, null, properties34, null, "system", new ChildAssociationRef[] {n34CAR}, new NodeRef[] {rootNodeRef}, new String[] { "/" + n34QName.toString() }, n34NodeRef, true);
} }
private Map<QName, PropertyValue> getOrderProperties() private Map<QName, PropertyValue> getOrderProperties()

View File

@@ -92,4 +92,18 @@ public class MNTIT extends AbstractRequestHandlerIT
assertResponseCardinality("\"AnalystName\" AND !\"AnalystName Craig\"", 1); assertResponseCardinality("\"AnalystName\" AND !\"AnalystName Craig\"", 1);
assertResponseCardinality("cm:name:\"BASF*.txt\"", 4); assertResponseCardinality("cm:name:\"BASF*.txt\"", 4);
} }
@Test
public void mnt24377()
{
assertResponseCardinality("acme:date:*", 2); // sanity check to make sure test nodes are indexed
assertResponseCardinality("acme:date:NOW/DAY+1DAY", 0);
assertResponseCardinality("acme:date:NOW/DAY", 1);
assertResponseCardinality("acme:date:NOW/DAY-1DAY", 1);
assertResponseCardinality("acme:date:NOW/DAY-2DAY", 0);
assertResponseCardinality("acme:date:TODAY", 1);
assertResponseCardinality("acme:date:NOW", 0);
}
} }

View File

@@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<model name="acme:myContentModel" xmlns="http://www.alfresco.org/model/dictionary/1.0">
<!-- Optional meta-data about the model -->
<description>Sample Document Model</description>
<author>My Name</author>
<version>1.0</version>
<imports>
<!-- Import Alfresco Dictionary Definitions -->
<import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d"/>
<!-- Import Alfresco Content Domain Model Definitions -->
<import uri="http://www.alfresco.org/model/content/1.0" prefix="cm"/>
<!-- Import Alfresco System Model Definitions -->
<import uri="http://www.alfresco.org/model/system/1.0" prefix="sys"/>
</imports>
<!-- Custom namespace for the ACME company -->
<namespaces>
<namespace uri="http://www.acme.org/model/content/1.0" prefix="acme"/>
</namespaces>
<constraints>
<constraint name="acme:securityClassificationOptions" type="LIST">
<parameter name="allowedValues">
<list>
<value></value>
<!-- Empty for default search-->
<value>Public</value>
<value>Client Confidential</value>
<value>Company Confidential</value>
<value>Strictly Confidential</value>
</list>
</parameter>
</constraint>
</constraints>
<!-- ===============================================================================================================
Constraints, Types, and Aspects go here...
-->
<types>
<!--
ACME Enterprise-wide Document root type.
All other custom document types would extend this one.
-->
<type name="acme:document">
<title>Sample Document Type</title>
<parent>cm:content</parent>
<properties>
<property name="acme:documentId">
<title>Document Identification Number</title>
<type>d:text</type>
</property>
<property name="acme:test">
<title>test property</title>
<type>d:text</type>
<index enabled="true">
<atomic>true</atomic>
<stored>false</stored>
<tokenised>false</tokenised>
</index>
</property>
<property name="acme:date">
<type>d:date</type>
<index enabled="true">
<tokenised>both</tokenised>
<facetable>true</facetable>
</index>
</property>
</properties>
</type>
</types>
<aspects>
<!-- A document can have security classification applied and
faceted search is specifically enabled for best performance and we change
default index config to not tokenize the value. -->
<aspect name="acme:securityClassified">
<title>ACME Security Classified</title>
<description>Content has been security classified</description>
<properties>
<property name="acme:securityClassification">
<type>d:text</type>
<index enabled="true">
<atomic>true</atomic>
<stored>false</stored>
<tokenised>false</tokenised>
</index>
<constraints>
<constraint ref="acme:securityClassificationOptions"/>
</constraints>
</property>
</properties>
</aspect>
</aspects>
</model>