-
Notifications
You must be signed in to change notification settings - Fork 793
Add sort arugment for the search API #4865
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,7 +24,11 @@ | |
| package org.opengrok.indexer.search; | ||
|
|
||
| import java.io.File; | ||
| import java.net.URL; | ||
| import java.nio.file.Path; | ||
| import java.util.ArrayList; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
| import java.util.TreeSet; | ||
|
|
||
| import org.junit.jupiter.api.AfterAll; | ||
|
|
@@ -36,7 +40,9 @@ | |
| import org.opengrok.indexer.util.TestRepository; | ||
|
|
||
| import org.opengrok.indexer.history.RepositoryFactory; | ||
| import org.opengrok.indexer.web.SortOrder; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.assertArrayEquals; | ||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||
| import static org.junit.jupiter.api.Assertions.assertFalse; | ||
| import static org.junit.jupiter.api.Assertions.assertNull; | ||
|
|
@@ -55,7 +61,9 @@ class SearchEngineTest { | |
| @BeforeAll | ||
| static void setUpClass() throws Exception { | ||
| repository = new TestRepository(); | ||
| repository.create(HistoryGuru.class.getResource("/repositories")); | ||
| URL url = HistoryGuru.class.getResource("/repositories"); | ||
| repository.createEmpty(); | ||
| repository.copyDirectoryWithUniqueModifiedTime(Path.of(url.toURI()), Path.of(repository.getSourceRoot())); | ||
|
|
||
| RuntimeEnvironment env = RuntimeEnvironment.getInstance(); | ||
| env.setSourceRoot(repository.getSourceRoot()); | ||
|
|
@@ -148,6 +156,69 @@ void testGetQuery() throws Exception { | |
| instance.getQuery()); | ||
| } | ||
|
|
||
| @Test | ||
| void testSortOrderLastModified() { | ||
| SearchEngine instance = new SearchEngine(); | ||
| instance.setFile("main.c"); | ||
| instance.setFreetext("arguments"); | ||
| instance.setSortOrder(SortOrder.LASTMODIFIED); | ||
| int hitsCount = instance.search(); | ||
| List<Hit> hits = new ArrayList<>(); | ||
| instance.results(0, hitsCount, hits); | ||
| assertTrue(hits.size() > 1, "Should return at least 2 hits for RELEVANCY sort to check order"); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not check the exact count ? If count is different than the array length, it does not make sense to proceed with comparison. |
||
|
|
||
| List<String> results = new ArrayList<>(); | ||
| for (Hit hit : hits) { | ||
| results.add(hit.getPath() + "@" + hit.getLineno()); | ||
| } | ||
|
Comment on lines
+170
to
+173
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd prefer something like this over iterating: String[] results = hits.stream().
map(hit -> hit.getPath() + "@" + hit.getLineno()).
toArray(String[]::new);Then for the |
||
| final String[] expectedResults = { | ||
| "/teamware/main.c@5", | ||
| "/rcs_test/main.c@5", | ||
| "/mercurial/main.c@5", | ||
| "/git/main.c@5", | ||
| "/cvs_test/cvsrepo/main.c@7", | ||
| "/bazaar/main.c@5" | ||
| }; | ||
|
|
||
| assertArrayEquals(expectedResults, results.toArray()); | ||
|
|
||
| instance.destroy(); | ||
| } | ||
|
|
||
| @Test | ||
| void testSortOrderByPath() { | ||
| SearchEngine instance = new SearchEngine(); | ||
| instance.setFile("main.c OR header.h"); | ||
| instance.setFreetext("arguments OR stdio"); | ||
| instance.setSortOrder(SortOrder.BY_PATH); | ||
| int hitsCount = instance.search(); | ||
| List<Hit> hits = new ArrayList<>(); | ||
| instance.results(0, hitsCount, hits); | ||
| assertTrue(hits.size() > 1, "Should return at least 2 hits for RELEVANCY sort to check order"); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto |
||
|
|
||
| List<String> results = new ArrayList<>(); | ||
| for (Hit hit : hits) { | ||
| results.add(hit.getPath() + "@" + hit.getLineno()); | ||
| } | ||
|
Comment on lines
+199
to
+202
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto |
||
| final String[] expectedResults = { | ||
| "/bazaar/header.h@2", | ||
| "/bazaar/main.c@5", | ||
| "/cvs_test/cvsrepo/main.c@7", | ||
| "/git/header.h@2", | ||
| "/git/main.c@5", | ||
| "/mercurial/header.h@2", | ||
| "/mercurial/main.c@5", | ||
| "/rcs_test/header.h@2", | ||
| "/rcs_test/main.c@5", | ||
| "/teamware/header.h@2", | ||
| "/teamware/main.c@5" | ||
| }; | ||
|
|
||
| assertArrayEquals(expectedResults, results.toArray()); | ||
|
|
||
| instance.destroy(); | ||
| } | ||
|
|
||
| /* see https://github.com/oracle/opengrok/issues/2030 | ||
| @Test | ||
| void testSearch() { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,6 +33,7 @@ | |
| import java.nio.file.Files; | ||
| import java.nio.file.Path; | ||
| import java.util.LinkedHashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.stream.Stream; | ||
|
|
||
|
|
@@ -133,6 +134,54 @@ public void copyDirectory(Path src, Path dest) throws IOException { | |
| } | ||
| } | ||
|
|
||
| /** | ||
| * Assumes the destination directory exists. | ||
| * @param src source directory | ||
| * @param dest destination directory | ||
| * @throws IOException on error | ||
| */ | ||
| public void copyDirectoryWithUniqueModifiedTime(Path src, Path dest) throws IOException { | ||
| // Create a deterministic order of paths for creation time, so last modified time indexing is stable in tests | ||
| // note we cannot use Files.copy(sourceFile, destPath, REPLACE_EXISTING, COPY_ATTRIBUTES) | ||
| // as the original creation time is the user checkout and not different accross files | ||
|
Comment on lines
+144
to
+146
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps move this comment to the method's javadoc so that it becomes more clear what it does.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, there is a typo in "accross". |
||
| List<Path> allPaths; | ||
| try (Stream<Path> stream = Files.walk(src)) { | ||
| allPaths = stream.filter(p -> !p.equals(src)).sorted().toList(); | ||
| } | ||
| // Set base time to now, and go ahead in time for each subsequent path by 1 minute | ||
| java.time.Instant baseTime = java.time.Instant.now(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd prefer to |
||
| for (int i = 0; i < allPaths.size(); i++) { | ||
| Path sourcePath = allPaths.get(i); | ||
| Path destRelativePath = getDestinationRelativePath(src, sourcePath); | ||
| Path destPath = dest.resolve(destRelativePath); | ||
| var fileTime = java.nio.file.attribute.FileTime.from(baseTime.plusSeconds(i * 60L)); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto for the |
||
| if (Files.isDirectory(sourcePath)) { | ||
| if (!Files.exists(destPath)) { | ||
| Files.createDirectories(destPath); | ||
| } | ||
| try { | ||
| Files.setLastModifiedTime(destPath, fileTime); | ||
| Files.setAttribute(destPath, "basic:creationTime", fileTime); | ||
| } catch (Exception ignored) { | ||
| // Not all filesystems support creationTime | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the creation time necessary for the |
||
| } | ||
| } else { | ||
| // Ensure parent directory exists before copying file | ||
| Path parentDir = destPath.getParent(); | ||
| if (parentDir != null && !Files.exists(parentDir)) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no need to check whether the destination exists for Also, I am pretty sure @ginoaugustine would come with a way how to use |
||
| Files.createDirectories(parentDir); | ||
| } | ||
| Files.copy(sourcePath, destPath, REPLACE_EXISTING, COPY_ATTRIBUTES); | ||
| Files.setLastModifiedTime(destPath, fileTime); | ||
| try { | ||
| Files.setAttribute(destPath, "basic:creationTime", fileTime); | ||
| } catch (Exception ignored) { | ||
| // Not all filesystems support creationTime | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private Path getDestinationRelativePath(Path sourceDirectory, Path sourceFile) { | ||
| // possibly strip zip filesystem for the startsWith method to work | ||
| var relativePath = Path.of(sourceDirectory.relativize(sourceFile).toString()); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,6 +39,7 @@ | |
| import org.opengrok.indexer.search.Hit; | ||
| import org.opengrok.indexer.search.SearchEngine; | ||
| import org.opengrok.indexer.web.QueryParameters; | ||
| import org.opengrok.indexer.web.SortOrder; | ||
| import org.opengrok.web.PageConfig; | ||
| import org.opengrok.web.api.v1.filter.CorsEnable; | ||
| import org.opengrok.web.api.v1.suggester.provider.service.SuggesterService; | ||
|
|
@@ -58,6 +59,7 @@ public class SearchController { | |
| public static final String PATH = "search"; | ||
|
|
||
| private static final int MAX_RESULTS = 1000; | ||
| private static final String DEFAULT_SORT_ORDER = "relevancy"; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should probably say this needs to be equal to Cannot use it directly as as |
||
|
|
||
| private final SuggesterService suggester; | ||
|
|
||
|
|
@@ -81,9 +83,10 @@ public SearchResult search( | |
| @QueryParam("projects") final List<String> projects, | ||
| @QueryParam("maxresults") // Akin to QueryParameters.COUNT_PARAM | ||
| @DefaultValue(MAX_RESULTS + "") final int maxResults, | ||
| @QueryParam(QueryParameters.START_PARAM) @DefaultValue(0 + "") final int startDocIndex | ||
| @QueryParam(QueryParameters.START_PARAM) @DefaultValue(0 + "") final int startDocIndex, | ||
| @QueryParam(QueryParameters.SORT_PARAM) @DefaultValue(DEFAULT_SORT_ORDER) final String sort | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nice to add a test that ensures the default sort order (perhaps it would be sufficient to call just |
||
| ) { | ||
| try (SearchEngineWrapper engine = new SearchEngineWrapper(full, def, symbol, path, hist, type)) { | ||
| try (SearchEngineWrapper engine = new SearchEngineWrapper(full, def, symbol, path, hist, type, SortOrder.get(sort))) { | ||
|
|
||
| if (!engine.isValid()) { | ||
| throw new WebApplicationException("Invalid request", Response.Status.BAD_REQUEST); | ||
|
|
@@ -119,14 +122,16 @@ private SearchEngineWrapper( | |
| final String symbol, | ||
| final String path, | ||
| final String hist, | ||
| final String type | ||
| final String type, | ||
| final SortOrder sortOrder | ||
| ) { | ||
| engine.setFreetext(full); | ||
| engine.setDefinition(def); | ||
| engine.setSymbol(symbol); | ||
| engine.setFile(path); | ||
| engine.setHistory(hist); | ||
| engine.setType(type); | ||
| engine.setSortOrder(sortOrder); | ||
| } | ||
|
|
||
| public List<Hit> search( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add
assertNotNullfor theurlbefore thetoURI()conversion like it is done in some other test classes to avoid code smells reported e.g. by SonarQube.