diff --git a/search/resources/schemas/dbscripts/postgresql/search-26.000-26.001.sql b/search/resources/schemas/dbscripts/postgresql/search-26.000-26.001.sql new file mode 100644 index 00000000000..d7edef5652c --- /dev/null +++ b/search/resources/schemas/dbscripts/postgresql/search-26.000-26.001.sql @@ -0,0 +1,2 @@ +-- Upgrade Lucene to 10.3.2 +SELECT core.executeJavaUpgradeCode('reindex'); diff --git a/search/resources/schemas/dbscripts/sqlserver/search-26.000-26.001.sql b/search/resources/schemas/dbscripts/sqlserver/search-26.000-26.001.sql new file mode 100644 index 00000000000..74422f0aca1 --- /dev/null +++ b/search/resources/schemas/dbscripts/sqlserver/search-26.000-26.001.sql @@ -0,0 +1,2 @@ +-- Upgrade Lucene to 10.3.2 +EXEC core.executeJavaUpgradeCode 'reindex'; diff --git a/search/src/org/labkey/search/SearchMcp.java b/search/src/org/labkey/search/SearchMcp.java index d896830d8e0..96e31e1f1f9 100644 --- a/search/src/org/labkey/search/SearchMcp.java +++ b/search/src/org/labkey/search/SearchMcp.java @@ -22,7 +22,7 @@ public class SearchMcp implements McpService.McpImpl { final static String mdSearchHelp = """ - The search functionality is implmeneted by Lucene. The query syntax is + The search functionality is implemented by Lucene. The query syntax is Core Syntax Elements diff --git a/search/src/org/labkey/search/SearchModule.java b/search/src/org/labkey/search/SearchModule.java index 6305b9dae18..3623744b825 100644 --- a/search/src/org/labkey/search/SearchModule.java +++ b/search/src/org/labkey/search/SearchModule.java @@ -85,7 +85,7 @@ public String getName() @Override public Double getSchemaVersion() { - return 26.000; + return 26.001; } @Override diff --git a/search/src/org/labkey/search/model/LuceneSearchServiceImpl.java b/search/src/org/labkey/search/model/LuceneSearchServiceImpl.java index 338f05b1bd6..d96710290a9 100644 --- a/search/src/org/labkey/search/model/LuceneSearchServiceImpl.java +++ b/search/src/org/labkey/search/model/LuceneSearchServiceImpl.java @@ -80,7 +80,6 @@ import org.labkey.api.files.FileSystemDirectoryListener; import org.labkey.api.files.FileSystemWatchers; import org.labkey.api.mbean.SearchMXBean; -import org.labkey.api.module.ModuleLoader; import org.labkey.api.portal.ProjectUrls; import org.labkey.api.resource.Resource; import org.labkey.api.search.SearchService; @@ -114,7 +113,6 @@ import org.labkey.api.util.logging.LogHelper; import org.labkey.api.view.ActionURL; import org.labkey.api.view.UnauthorizedException; -import org.labkey.api.view.WebPartView; import org.labkey.api.webdav.FileSystemResource; import org.labkey.api.webdav.SimpleDocumentResource; import org.labkey.api.webdav.WebdavResource; @@ -1428,7 +1426,7 @@ private long getDocCount(Query query) throws IOException try { TopDocs docs = searcher.search(query, 1); - return docs.totalHits.value; + return docs.totalHits.value(); // TODO: This may not be exact, could be a lower bound. See totalHits.relation(). } finally { @@ -1842,7 +1840,7 @@ private void processSearchResult(int offset, int hitsToRetrieve, TopDocs topDocs } result.offset = offset; - result.totalHits = topDocs.totalHits.value; + result.totalHits = topDocs.totalHits.value(); // TODO: This may not be exact, could be a lower bound. See totalHits.relation(). result.hits = ret; } diff --git a/search/src/org/labkey/search/model/SecurityQuery.java b/search/src/org/labkey/search/model/SecurityQuery.java index 7542d2394a5..83dc10f6175 100644 --- a/search/src/org/labkey/search/model/SecurityQuery.java +++ b/search/src/org/labkey/search/model/SecurityQuery.java @@ -27,11 +27,13 @@ import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.ScorerSupplier; import org.apache.lucene.search.Weight; import org.apache.lucene.util.BitSetIterator; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.FixedBitSet; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.labkey.api.data.Container; import org.labkey.api.data.ContainerManager; import org.labkey.api.module.Module; @@ -96,7 +98,6 @@ public boolean isCacheable(LeafReaderContext ctx) private boolean isReadable(String containerId, String categories) { -// return _containerIds.containsKey(containerId); if (StringUtils.isEmpty(categories) || !_categoryContainers.containsKey(categories)) return _containerIds.containsKey(containerId); else @@ -104,56 +105,87 @@ private boolean isReadable(String containerId, String categories) } @Override - public Scorer scorer(LeafReaderContext context) throws IOException + public ScorerSupplier scorerSupplier(LeafReaderContext context) { - SearchService.SEARCH_PHASE currentPhase = _iTimer.getCurrentPhase(); - _iTimer.setPhase(SearchService.SEARCH_PHASE.applySecurityFilter); - - LeafReader reader = context.reader(); - int maxDoc = reader.maxDoc(); - FixedBitSet bits = new FixedBitSet(maxDoc); + return new ScorerSupplier() + { + private final LeafReader _reader; + private final @Nullable BinaryDocValues _securityContextDocValues; - BinaryDocValues securityContextDocValues = reader.getBinaryDocValues(FIELD_NAME.securityContext.name()); + { + SearchService.SEARCH_PHASE currentPhase = _iTimer.getCurrentPhase(); - try - { - int doc; + try + { + _iTimer.setPhase(SearchService.SEARCH_PHASE.applySecurityFilter); + _reader = context.reader(); + _securityContextDocValues = _reader.getBinaryDocValues(FIELD_NAME.securityContext.name()); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + finally + { + _iTimer.setPhase(currentPhase); + } + } - // Can be null, if no documents (e.g., shortly after bootstrap or clear index) - if (null != securityContextDocValues) + @Override + public Scorer get(long leadCost) throws IOException { - while (NO_MORE_DOCS != (doc = securityContextDocValues.nextDoc())) + SearchService.SEARCH_PHASE currentPhase = _iTimer.getCurrentPhase(); + + try { - BytesRef bytesRef = securityContextDocValues.binaryValue(); - String securityContext = StringUtils.trimToNull(bytesRef.utf8ToString()); - - final String containerId; - final String resourceId; - final String categories; - String[] parts = StringUtils.split(securityContext, "|"); - // SecurityContext is usually just a container ID and a string of categories, but in some cases it adds a resource ID. - containerId = parts[0]; - if (parts.length > 1) - categories = parts[1]; - else - categories = null; - if (parts.length > 2) - resourceId = parts[2]; - else - resourceId = null; - - // Must have read permission on the container (always). Must also have read permissions on resource ID, if non-null. - if (isReadable(containerId, categories) && (null == resourceId || canReadResource(resourceId, containerId))) - bits.set(doc); + _iTimer.setPhase(SearchService.SEARCH_PHASE.applySecurityFilter); + int maxDoc = _reader.maxDoc(); + FixedBitSet bits = new FixedBitSet(maxDoc); + int doc; + + // Can be null, if no documents (e.g., shortly after bootstrap or clear index) + if (null != _securityContextDocValues) + { + while (NO_MORE_DOCS != (doc = _securityContextDocValues.nextDoc())) + { + BytesRef bytesRef = _securityContextDocValues.binaryValue(); + String securityContext = StringUtils.trimToNull(bytesRef.utf8ToString()); + + final String containerId; + final String resourceId; + final String categories; + String[] parts = StringUtils.split(securityContext, "|"); + // SecurityContext is usually just a container ID and a string of categories, but in some cases it adds a resource ID. + containerId = parts[0]; + if (parts.length > 1) + categories = parts[1]; + else + categories = null; + if (parts.length > 2) + resourceId = parts[2]; + else + resourceId = null; + + // Must have read permission on the container (always). Must also have read permissions on resource ID, if non-null. + if (isReadable(containerId, categories) && (null == resourceId || canReadResource(resourceId, containerId))) + bits.set(doc); + } + } + + return new ConstantScoreScorer(score(), scoreMode, new BitSetIterator(bits, bits.approximateCardinality())); + } + finally + { + _iTimer.setPhase(currentPhase); } } - return new ConstantScoreScorer(this, score(), scoreMode, new BitSetIterator(bits, bits.approximateCardinality())); - } - finally - { - _iTimer.setPhase(currentPhase); - } + @Override + public long cost() + { + return null == _securityContextDocValues ? 0 : _securityContextDocValues.cost(); + } + }; } }; }