diff options
author | Scott Jackson <daneren2005@gmail.com> | 2013-07-27 14:33:25 -0700 |
---|---|---|
committer | Scott Jackson <daneren2005@gmail.com> | 2013-07-27 14:33:38 -0700 |
commit | 4738428c2c205f42200386ae09b44b9ec07b9144 (patch) | |
tree | a6402978fe1b4655f90c3c8a181f4d246fbc5e89 /subsonic-main/src/main/java/net/sourceforge/subsonic/service/SearchService.java | |
parent | 82ec8315f777c319f2372540098e21111019d629 (diff) | |
download | dsub-4738428c2c205f42200386ae09b44b9ec07b9144.tar.gz dsub-4738428c2c205f42200386ae09b44b9ec07b9144.tar.bz2 dsub-4738428c2c205f42200386ae09b44b9ec07b9144.zip |
Move subsonic-android to root
Diffstat (limited to 'subsonic-main/src/main/java/net/sourceforge/subsonic/service/SearchService.java')
-rw-r--r-- | subsonic-main/src/main/java/net/sourceforge/subsonic/service/SearchService.java | 567 |
1 files changed, 0 insertions, 567 deletions
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/service/SearchService.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/service/SearchService.java deleted file mode 100644 index 2698bbd6..00000000 --- a/subsonic-main/src/main/java/net/sourceforge/subsonic/service/SearchService.java +++ /dev/null @@ -1,567 +0,0 @@ -/* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.service; - -import net.sourceforge.subsonic.Logger; -import net.sourceforge.subsonic.dao.AlbumDao; -import net.sourceforge.subsonic.dao.ArtistDao; -import net.sourceforge.subsonic.domain.Album; -import net.sourceforge.subsonic.domain.Artist; -import net.sourceforge.subsonic.domain.MediaFile; -import net.sourceforge.subsonic.domain.MusicFolder; -import net.sourceforge.subsonic.domain.RandomSearchCriteria; -import net.sourceforge.subsonic.domain.SearchCriteria; -import net.sourceforge.subsonic.domain.SearchResult; -import net.sourceforge.subsonic.util.FileUtil; -import org.apache.lucene.analysis.ASCIIFoldingFilter; -import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.analysis.LowerCaseFilter; -import org.apache.lucene.analysis.StopFilter; -import org.apache.lucene.analysis.TokenStream; -import org.apache.lucene.analysis.standard.StandardAnalyzer; -import org.apache.lucene.analysis.standard.StandardFilter; -import org.apache.lucene.analysis.standard.StandardTokenizer; -import org.apache.lucene.document.Document; -import org.apache.lucene.document.Field; -import org.apache.lucene.document.NumericField; -import org.apache.lucene.index.IndexReader; -import org.apache.lucene.index.IndexWriter; -import org.apache.lucene.index.Term; -import org.apache.lucene.queryParser.MultiFieldQueryParser; -import org.apache.lucene.search.BooleanClause; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.MatchAllDocsQuery; -import org.apache.lucene.search.NumericRangeQuery; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Searcher; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.TopDocs; -import org.apache.lucene.store.Directory; -import org.apache.lucene.store.FSDirectory; -import org.apache.lucene.util.Version; - -import java.io.File; -import java.io.IOException; -import java.io.Reader; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; - -import static net.sourceforge.subsonic.service.SearchService.IndexType.*; -import static net.sourceforge.subsonic.service.SearchService.IndexType.SONG; - -/** - * Performs Lucene-based searching and indexing. - * - * @author Sindre Mehus - * @version $Id$ - * @see MediaScannerService - */ -public class SearchService { - - private static final Logger LOG = Logger.getLogger(SearchService.class); - - private static final String FIELD_ID = "id"; - private static final String FIELD_TITLE = "title"; - private static final String FIELD_ALBUM = "album"; - private static final String FIELD_ARTIST = "artist"; - private static final String FIELD_GENRE = "genre"; - private static final String FIELD_YEAR = "year"; - private static final String FIELD_MEDIA_TYPE = "mediaType"; - private static final String FIELD_FOLDER = "folder"; - - private static final Version LUCENE_VERSION = Version.LUCENE_30; - - private MediaFileService mediaFileService; - private SettingsService settingsService; - private ArtistDao artistDao; - private AlbumDao albumDao; - - private IndexWriter artistWriter; - private IndexWriter artistId3Writer; - private IndexWriter albumWriter; - private IndexWriter albumId3Writer; - private IndexWriter songWriter; - - public SearchService() { - removeLocks(); - } - - - public void startIndexing() { - try { - artistWriter = createIndexWriter(ARTIST); - artistId3Writer = createIndexWriter(ARTIST_ID3); - albumWriter = createIndexWriter(ALBUM); - albumId3Writer = createIndexWriter(ALBUM_ID3); - songWriter = createIndexWriter(SONG); - } catch (Exception x) { - LOG.error("Failed to create search index.", x); - } - } - - public void index(MediaFile mediaFile) { - try { - if (mediaFile.isFile()) { - songWriter.addDocument(SONG.createDocument(mediaFile)); - } else if (mediaFile.isAlbum()) { - albumWriter.addDocument(ALBUM.createDocument(mediaFile)); - } else { - artistWriter.addDocument(ARTIST.createDocument(mediaFile)); - } - } catch (Exception x) { - LOG.error("Failed to create search index for " + mediaFile, x); - } - } - - public void index(Artist artist) { - try { - artistId3Writer.addDocument(ARTIST_ID3.createDocument(artist)); - } catch (Exception x) { - LOG.error("Failed to create search index for " + artist, x); - } - } - - public void index(Album album) { - try { - albumId3Writer.addDocument(ALBUM_ID3.createDocument(album)); - } catch (Exception x) { - LOG.error("Failed to create search index for " + album, x); - } - } - - public void stopIndexing() { - try { - artistWriter.optimize(); - artistId3Writer.optimize(); - albumWriter.optimize(); - albumId3Writer.optimize(); - songWriter.optimize(); - } catch (Exception x) { - LOG.error("Failed to create search index.", x); - } finally { - FileUtil.closeQuietly(artistId3Writer); - FileUtil.closeQuietly(artistWriter); - FileUtil.closeQuietly(albumWriter); - FileUtil.closeQuietly(albumId3Writer); - FileUtil.closeQuietly(songWriter); - } - } - - public SearchResult search(SearchCriteria criteria, IndexType indexType) { - SearchResult result = new SearchResult(); - int offset = criteria.getOffset(); - int count = criteria.getCount(); - result.setOffset(offset); - - IndexReader reader = null; - try { - reader = createIndexReader(indexType); - Searcher searcher = new IndexSearcher(reader); - Analyzer analyzer = new SubsonicAnalyzer(); - - MultiFieldQueryParser queryParser = new MultiFieldQueryParser(LUCENE_VERSION, indexType.getFields(), analyzer, indexType.getBoosts()); - Query query = queryParser.parse(criteria.getQuery()); - - TopDocs topDocs = searcher.search(query, null, offset + count); - result.setTotalHits(topDocs.totalHits); - - int start = Math.min(offset, topDocs.totalHits); - int end = Math.min(start + count, topDocs.totalHits); - for (int i = start; i < end; i++) { - Document doc = searcher.doc(topDocs.scoreDocs[i].doc); - switch (indexType) { - case SONG: - case ARTIST: - case ALBUM: - MediaFile mediaFile = mediaFileService.getMediaFile(Integer.valueOf(doc.get(FIELD_ID))); - addIfNotNull(mediaFile, result.getMediaFiles()); - break; - case ARTIST_ID3: - Artist artist = artistDao.getArtist(Integer.valueOf(doc.get(FIELD_ID))); - addIfNotNull(artist, result.getArtists()); - break; - case ALBUM_ID3: - Album album = albumDao.getAlbum(Integer.valueOf(doc.get(FIELD_ID))); - addIfNotNull(album, result.getAlbums()); - break; - default: - break; - } - } - - } catch (Throwable x) { - LOG.error("Failed to execute Lucene search.", x); - } finally { - FileUtil.closeQuietly(reader); - } - return result; - } - - /** - * Returns a number of random songs. - * - * @param criteria Search criteria. - * @return List of random songs. - */ - public List<MediaFile> getRandomSongs(RandomSearchCriteria criteria) { - List<MediaFile> result = new ArrayList<MediaFile>(); - - String musicFolderPath = null; - if (criteria.getMusicFolderId() != null) { - MusicFolder musicFolder = settingsService.getMusicFolderById(criteria.getMusicFolderId()); - musicFolderPath = musicFolder.getPath().getPath(); - } - - IndexReader reader = null; - try { - reader = createIndexReader(SONG); - Searcher searcher = new IndexSearcher(reader); - - BooleanQuery query = new BooleanQuery(); - query.add(new TermQuery(new Term(FIELD_MEDIA_TYPE, MediaFile.MediaType.MUSIC.name().toLowerCase())), BooleanClause.Occur.MUST); - if (criteria.getGenre() != null) { - String genre = normalizeGenre(criteria.getGenre()); - query.add(new TermQuery(new Term(FIELD_GENRE, genre)), BooleanClause.Occur.MUST); - } - if (criteria.getFromYear() != null || criteria.getToYear() != null) { - NumericRangeQuery<Integer> rangeQuery = NumericRangeQuery.newIntRange(FIELD_YEAR, criteria.getFromYear(), criteria.getToYear(), true, true); - query.add(rangeQuery, BooleanClause.Occur.MUST); - } - if (musicFolderPath != null) { - query.add(new TermQuery(new Term(FIELD_FOLDER, musicFolderPath)), BooleanClause.Occur.MUST); - } - - TopDocs topDocs = searcher.search(query, null, Integer.MAX_VALUE); - Random random = new Random(System.currentTimeMillis()); - - for (int i = 0; i < Math.min(criteria.getCount(), topDocs.totalHits); i++) { - int index = random.nextInt(topDocs.totalHits); - Document doc = searcher.doc(topDocs.scoreDocs[index].doc); - int id = Integer.valueOf(doc.get(FIELD_ID)); - try { - result.add(mediaFileService.getMediaFile(id)); - } catch (Exception x) { - LOG.warn("Failed to get media file " + id); - } - } - - } catch (Throwable x) { - LOG.error("Failed to search or random songs.", x); - } finally { - FileUtil.closeQuietly(reader); - } - return result; - } - - private static String normalizeGenre(String genre) { - return genre.toLowerCase().replace(" ", ""); - } - - /** - * Returns a number of random albums. - * - * @param count Number of albums to return. - * @return List of random albums. - */ - public List<MediaFile> getRandomAlbums(int count) { - List<MediaFile> result = new ArrayList<MediaFile>(); - - IndexReader reader = null; - try { - reader = createIndexReader(ALBUM); - Searcher searcher = new IndexSearcher(reader); - - Query query = new MatchAllDocsQuery(); - TopDocs topDocs = searcher.search(query, null, Integer.MAX_VALUE); - Random random = new Random(System.currentTimeMillis()); - - for (int i = 0; i < Math.min(count, topDocs.totalHits); i++) { - int index = random.nextInt(topDocs.totalHits); - Document doc = searcher.doc(topDocs.scoreDocs[index].doc); - int id = Integer.valueOf(doc.get(FIELD_ID)); - try { - addIfNotNull(mediaFileService.getMediaFile(id), result); - } catch (Exception x) { - LOG.warn("Failed to get media file " + id, x); - } - } - - } catch (Throwable x) { - LOG.error("Failed to search for random albums.", x); - } finally { - FileUtil.closeQuietly(reader); - } - return result; - } - - /** - * Returns a number of random albums, using ID3 tag. - * - * @param count Number of albums to return. - * @return List of random albums. - */ - public List<Album> getRandomAlbumsId3(int count) { - List<Album> result = new ArrayList<Album>(); - - IndexReader reader = null; - try { - reader = createIndexReader(ALBUM_ID3); - Searcher searcher = new IndexSearcher(reader); - - Query query = new MatchAllDocsQuery(); - TopDocs topDocs = searcher.search(query, null, Integer.MAX_VALUE); - Random random = new Random(System.currentTimeMillis()); - - for (int i = 0; i < Math.min(count, topDocs.totalHits); i++) { - int index = random.nextInt(topDocs.totalHits); - Document doc = searcher.doc(topDocs.scoreDocs[index].doc); - int id = Integer.valueOf(doc.get(FIELD_ID)); - try { - addIfNotNull(albumDao.getAlbum(id), result); - } catch (Exception x) { - LOG.warn("Failed to get album file " + id, x); - } - } - - } catch (Throwable x) { - LOG.error("Failed to search for random albums.", x); - } finally { - FileUtil.closeQuietly(reader); - } - return result; - } - - private <T> void addIfNotNull(T value, List<T> list) { - if (value != null) { - list.add(value); - } - } - private IndexWriter createIndexWriter(IndexType indexType) throws IOException { - File dir = getIndexDirectory(indexType); - return new IndexWriter(FSDirectory.open(dir), new SubsonicAnalyzer(), true, new IndexWriter.MaxFieldLength(10)); - } - - private IndexReader createIndexReader(IndexType indexType) throws IOException { - File dir = getIndexDirectory(indexType); - return IndexReader.open(FSDirectory.open(dir), true); - } - - private File getIndexRootDirectory() { - return new File(SettingsService.getSubsonicHome(), "lucene2"); - } - - private File getIndexDirectory(IndexType indexType) { - return new File(getIndexRootDirectory(), indexType.toString().toLowerCase()); - } - - private void removeLocks() { - for (IndexType indexType : IndexType.values()) { - Directory dir = null; - try { - dir = FSDirectory.open(getIndexDirectory(indexType)); - if (IndexWriter.isLocked(dir)) { - IndexWriter.unlock(dir); - LOG.info("Removed Lucene lock file in " + dir); - } - } catch (Exception x) { - LOG.warn("Failed to remove Lucene lock file in " + dir, x); - } finally { - FileUtil.closeQuietly(dir); - } - } - } - - public void setMediaFileService(MediaFileService mediaFileService) { - this.mediaFileService = mediaFileService; - } - - public void setSettingsService(SettingsService settingsService) { - this.settingsService = settingsService; - } - - public void setArtistDao(ArtistDao artistDao) { - this.artistDao = artistDao; - } - - public void setAlbumDao(AlbumDao albumDao) { - this.albumDao = albumDao; - } - - public static enum IndexType { - - SONG(new String[]{FIELD_TITLE, FIELD_ARTIST}, FIELD_TITLE) { - @Override - public Document createDocument(MediaFile mediaFile) { - Document doc = new Document(); - doc.add(new NumericField(FIELD_ID, Field.Store.YES, false).setIntValue(mediaFile.getId())); - doc.add(new Field(FIELD_MEDIA_TYPE, mediaFile.getMediaType().name(), Field.Store.NO, Field.Index.ANALYZED_NO_NORMS)); - - if (mediaFile.getTitle() != null) { - doc.add(new Field(FIELD_TITLE, mediaFile.getTitle(), Field.Store.YES, Field.Index.ANALYZED)); - } - if (mediaFile.getArtist() != null) { - doc.add(new Field(FIELD_ARTIST, mediaFile.getArtist(), Field.Store.YES, Field.Index.ANALYZED)); - } - if (mediaFile.getGenre() != null) { - doc.add(new Field(FIELD_GENRE, normalizeGenre(mediaFile.getGenre()), Field.Store.NO, Field.Index.ANALYZED)); - } - if (mediaFile.getYear() != null) { - doc.add(new NumericField(FIELD_YEAR, Field.Store.NO, true).setIntValue(mediaFile.getYear())); - } - if (mediaFile.getFolder() != null) { - doc.add(new Field(FIELD_FOLDER, mediaFile.getFolder(), Field.Store.NO, Field.Index.NOT_ANALYZED_NO_NORMS)); - } - - return doc; - } - }, - - ALBUM(new String[]{FIELD_ALBUM, FIELD_ARTIST}, FIELD_ALBUM) { - @Override - public Document createDocument(MediaFile mediaFile) { - Document doc = new Document(); - doc.add(new NumericField(FIELD_ID, Field.Store.YES, false).setIntValue(mediaFile.getId())); - - if (mediaFile.getArtist() != null) { - doc.add(new Field(FIELD_ARTIST, mediaFile.getArtist(), Field.Store.YES, Field.Index.ANALYZED)); - } - if (mediaFile.getAlbumName() != null) { - doc.add(new Field(FIELD_ALBUM, mediaFile.getAlbumName(), Field.Store.YES, Field.Index.ANALYZED)); - } - - return doc; - } - }, - - ALBUM_ID3(new String[]{FIELD_ALBUM, FIELD_ARTIST}, FIELD_ALBUM) { - @Override - public Document createDocument(Album album) { - Document doc = new Document(); - doc.add(new NumericField(FIELD_ID, Field.Store.YES, false).setIntValue(album.getId())); - - if (album.getArtist() != null) { - doc.add(new Field(FIELD_ARTIST, album.getArtist(), Field.Store.YES, Field.Index.ANALYZED)); - } - if (album.getName() != null) { - doc.add(new Field(FIELD_ALBUM, album.getName(), Field.Store.YES, Field.Index.ANALYZED)); - } - - return doc; - } - }, - - ARTIST(new String[]{FIELD_ARTIST}, null) { - @Override - public Document createDocument(MediaFile mediaFile) { - Document doc = new Document(); - doc.add(new NumericField(FIELD_ID, Field.Store.YES, false).setIntValue(mediaFile.getId())); - - if (mediaFile.getArtist() != null) { - doc.add(new Field(FIELD_ARTIST, mediaFile.getArtist(), Field.Store.YES, Field.Index.ANALYZED)); - } - - return doc; - } - }, - - ARTIST_ID3(new String[]{FIELD_ARTIST}, null) { - @Override - public Document createDocument(Artist artist) { - Document doc = new Document(); - doc.add(new NumericField(FIELD_ID, Field.Store.YES, false).setIntValue(artist.getId())); - doc.add(new Field(FIELD_ARTIST, artist.getName(), Field.Store.YES, Field.Index.ANALYZED)); - - return doc; - } - }; - - private final String[] fields; - private final Map<String, Float> boosts; - - private IndexType(String[] fields, String boostedField) { - this.fields = fields; - boosts = new HashMap<String, Float>(); - if (boostedField != null) { - boosts.put(boostedField, 2.0F); - } - } - - public String[] getFields() { - return fields; - } - - protected Document createDocument(MediaFile mediaFile) { - throw new UnsupportedOperationException(); - } - - protected Document createDocument(Artist artist) { - throw new UnsupportedOperationException(); - } - - protected Document createDocument(Album album) { - throw new UnsupportedOperationException(); - } - - public Map<String, Float> getBoosts() { - return boosts; - } - } - - private class SubsonicAnalyzer extends StandardAnalyzer { - private SubsonicAnalyzer() { - super(LUCENE_VERSION); - } - - @Override - public TokenStream tokenStream(String fieldName, Reader reader) { - TokenStream result = super.tokenStream(fieldName, reader); - return new ASCIIFoldingFilter(result); - } - - @Override - public TokenStream reusableTokenStream(String fieldName, Reader reader) throws IOException { - class SavedStreams { - StandardTokenizer tokenStream; - TokenStream filteredTokenStream; - } - - SavedStreams streams = (SavedStreams) getPreviousTokenStream(); - if (streams == null) { - streams = new SavedStreams(); - setPreviousTokenStream(streams); - streams.tokenStream = new StandardTokenizer(LUCENE_VERSION, reader); - streams.filteredTokenStream = new StandardFilter(streams.tokenStream); - streams.filteredTokenStream = new LowerCaseFilter(streams.filteredTokenStream); - streams.filteredTokenStream = new StopFilter(true, streams.filteredTokenStream, STOP_WORDS_SET); - streams.filteredTokenStream = new ASCIIFoldingFilter(streams.filteredTokenStream); - } else { - streams.tokenStream.reset(reader); - } - streams.tokenStream.setMaxTokenLength(DEFAULT_MAX_TOKEN_LENGTH); - - return streams.filteredTokenStream; - } - } -} - - |