From 36ee2307ea083cfe8994b13a3023bc779b2b23ec Mon Sep 17 00:00:00 2001 From: James Moger <james.moger@gitblit.com> Date: Fri, 17 Feb 2012 18:43:40 -0500 Subject: [PATCH] Implemented multi-repository Lucene search --- src/com/gitblit/utils/LuceneUtils.java | 87 ++++++++++++++++++++++++++++++++++++------- 1 files changed, 73 insertions(+), 14 deletions(-) diff --git a/src/com/gitblit/utils/LuceneUtils.java b/src/com/gitblit/utils/LuceneUtils.java index 4ca72f0..483537d 100644 --- a/src/com/gitblit/utils/LuceneUtils.java +++ b/src/com/gitblit/utils/LuceneUtils.java @@ -8,7 +8,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -26,6 +26,7 @@ import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.index.IndexWriterConfig.OpenMode; +import org.apache.lucene.index.MultiReader; import org.apache.lucene.index.Term; import org.apache.lucene.queryParser.QueryParser; import org.apache.lucene.search.BooleanClause.Occur; @@ -90,13 +91,32 @@ private static final String FIELD_LABEL = "label"; private static final String FIELD_ATTACHMENT = "attachment"; - private static Set<String> excludes = new TreeSet<String>(Arrays.asList("7z", "arc", "arj", - "bin", "bmp", "dll", "doc", "docx", "exe", "gif", "gz", "jar", "jpg", "lib", "lzh", - "odg", "pdf", "ppt", "png", "so", "swf", "xcf", "xls", "xlsx", "zip")); + private static Set<String> excludedExtensions = new TreeSet<String>( + Arrays.asList("7z", "arc", "arj", "bin", "bmp", "dll", "doc", + "docx", "exe", "gif", "gz", "jar", "jpg", "lib", "lzh", + "odg", "pdf", "ppt", "png", "so", "swf", "xcf", "xls", + "xlsx", "zip")); + + private static Set<String> excludedBranches = new TreeSet<String>( + Arrays.asList("/refs/heads/gb-issues")); private static final Map<File, IndexSearcher> SEARCHERS = new ConcurrentHashMap<File, IndexSearcher>(); private static final Map<File, IndexWriter> WRITERS = new ConcurrentHashMap<File, IndexWriter>(); + /** + * Returns the name of the repository. + * + * @param repository + * @return the repository name + */ + private static String getName(Repository repository) { + if (repository.isBare()) { + return repository.getDirectory().getName(); + } else { + return repository.getDirectory().getParentFile().getName(); + } + } + /** * Deletes the Lucene index for the specified repository. * @@ -125,6 +145,7 @@ */ public static boolean index(Repository repository) { try { + String repositoryName = getName(repository); Set<String> indexedCommits = new TreeSet<String>(); IndexWriter writer = getIndexWriter(repository, true); // build a quick lookup of tags @@ -139,6 +160,9 @@ // walk through each branch List<RefModel> branches = JGitUtils.getLocalBranches(repository, true, -1); for (RefModel branch : branches) { + if (excludedBranches.contains(branch.getName())) { + continue; + } RevWalk revWalk = new RevWalk(repository); RevCommit rev = revWalk.parseCommit(branch.getObjectId()); @@ -154,6 +178,8 @@ Document doc = new Document(); doc.add(new Field(FIELD_OBJECT_TYPE, ObjectType.blob.name(), Store.YES, Index.NOT_ANALYZED_NO_NORMS)); + doc.add(new Field(FIELD_REPOSITORY, repositoryName, Store.YES, + Index.NOT_ANALYZED)); doc.add(new Field(FIELD_OBJECT_ID, treeWalk.getPathString(), Store.YES, Index.NOT_ANALYZED)); doc.add(new Field(FIELD_DATE, revDate, Store.YES, Index.NO)); @@ -171,7 +197,7 @@ ext = name.substring(name.lastIndexOf('.') + 1); } - if (StringUtils.isEmpty(ext) || !excludes.contains(ext)) { + if (StringUtils.isEmpty(ext) || !excludedExtensions.contains(ext)) { // read the blob content ObjectId entid = treeWalk.getObjectId(0); FileMode entmode = treeWalk.getFileMode(0); @@ -199,6 +225,8 @@ String head = rev.getId().getName(); if (indexedCommits.add(head)) { Document doc = createDocument(rev, tags.get(head)); + doc.add(new Field(FIELD_REPOSITORY, repositoryName, Store.YES, + Index.NOT_ANALYZED)); writer.addDocument(doc); } @@ -208,6 +236,8 @@ String hash = rev.getId().getName(); if (indexedCommits.add(hash)) { Document doc = createDocument(rev, tags.get(hash)); + doc.add(new Field(FIELD_REPOSITORY, repositoryName, Store.YES, + Index.NOT_ANALYZED)); writer.addDocument(doc); } } @@ -221,6 +251,8 @@ List<IssueModel> issues = IssueUtils.getIssues(repository, null); for (IssueModel issue : issues) { Document doc = createDocument(issue); + doc.add(new Field(FIELD_REPOSITORY, repositoryName, Store.YES, + Index.NOT_ANALYZED)); writer.addDocument(doc); } } @@ -344,6 +376,9 @@ */ private static boolean index(Repository repository, Document doc) { try { + String repositoryName = getName(repository); + doc.add(new Field(FIELD_REPOSITORY, repositoryName, Store.YES, + Index.NOT_ANALYZED)); IndexWriter writer = getIndexWriter(repository, false); writer.addDocument(doc); resetIndexSearcher(repository); @@ -363,6 +398,7 @@ result.author = doc.get(FIELD_AUTHOR); result.committer = doc.get(FIELD_COMMITTER); result.type = ObjectType.fromName(doc.get(FIELD_OBJECT_TYPE)); + result.repository = doc.get(FIELD_REPOSITORY); result.id = doc.get(FIELD_OBJECT_ID); if (doc.get(FIELD_LABEL) != null) { result.labels = StringUtils.getStringsFromValue(doc.get(FIELD_LABEL)); @@ -437,17 +473,27 @@ } /** - * Search the repository for the given text or query + * Searches the specified repositories for the given text or query * - * @param repository * @param text - * @return a list of SearchResults + * if the text is null or empty, null is returned + * @param maximumHits + * the maximum number of hits to collect + * @param repositories + * a list of repositories to search. if no repositories are + * specified null is returned. + * @return a list of SearchResults in order from highest to the lowest score + * */ - public static List<SearchResult> search(Repository repository, String text) { + public static List<SearchResult> search(String text, int maximumHits, + Repository... repositories) { if (StringUtils.isEmpty(text)) { return null; } - Set<SearchResult> results = new HashSet<SearchResult>(); + if (repositories.length == 0) { + return null; + } + Set<SearchResult> results = new LinkedHashSet<SearchResult>(); StandardAnalyzer analyzer = new StandardAnalyzer(LUCENE_VERSION); try { // default search checks summary and content @@ -461,10 +507,23 @@ qp.setAllowLeadingWildcard(true); query.add(qp.parse(text), Occur.SHOULD); - IndexSearcher searcher = getIndexSearcher(repository); + IndexSearcher searcher; + if (repositories.length == 1) { + // single repository search + searcher = getIndexSearcher(repositories[0]); + } else { + // multiple repository search + List<IndexReader> readers = new ArrayList<IndexReader>(); + for (Repository repository : repositories) { + IndexSearcher repositoryIndex = getIndexSearcher(repository); + readers.add(repositoryIndex.getIndexReader()); + } + IndexReader [] rdrs = readers.toArray(new IndexReader[readers.size()]); + MultiReader reader = new MultiReader(rdrs); + searcher = new IndexSearcher(reader); + } Query rewrittenQuery = searcher.rewrite(query); - - TopScoreDocCollector collector = TopScoreDocCollector.create(200, true); + TopScoreDocCollector collector = TopScoreDocCollector.create(maximumHits, true); searcher.search(rewrittenQuery, collector); ScoreDoc[] hits = collector.topDocs().scoreDocs; for (int i = 0; i < hits.length; i++) { @@ -477,7 +536,7 @@ e.printStackTrace(); } return new ArrayList<SearchResult>(results); - } + } /** * Close all the index writers and searchers -- Gitblit v1.9.1