Split SearchService
Signed-off-by: Andrew DeMaria <lostonamountain@gmail.com>
This commit is contained in:
+574
@@ -0,0 +1,574 @@
|
||||
|
||||
package org.airsonic.player.service.search;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.lucene.analysis.TokenStream;
|
||||
import org.apache.lucene.analysis.tokenattributes.TermAttribute;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Test case for Analyzer.
|
||||
* These cases have the purpose of observing the current situation
|
||||
* and observing the impact of upgrading Lucene.
|
||||
*/
|
||||
public class AnalyzerFactoryTestCase {
|
||||
|
||||
private AnalyzerFactory analyzerFactory = new AnalyzerFactory();
|
||||
|
||||
/**
|
||||
* Test for the number of character separators per field.
|
||||
*/
|
||||
@Test
|
||||
public void testTokenCounts() {
|
||||
|
||||
/*
|
||||
* Analyzer used in legacy uses the same Tokenizer for all fields.
|
||||
* (Some fields are converted to their own input string for integrity.)
|
||||
* As a result, specifications for strings are scattered and difficult to understand.
|
||||
* Using PerFieldAnalyzerWrapper,
|
||||
* it is possible to use different Analyzer (Tokenizer/Filter) for each field.
|
||||
* This allows consistent management of parsing definitions.
|
||||
* It is also possible to apply definitions such as "id3 delimiters Tokenizer" to specific fields.
|
||||
*/
|
||||
|
||||
// The number of words excluding articles is 7.
|
||||
String query = "The quick brown fox jumps over the lazy dog.";
|
||||
|
||||
Arrays.stream(IndexType.values()).flatMap(i -> Arrays.stream(i.getFields())).forEach(n -> {
|
||||
List<String> terms = toTermString(n, query);
|
||||
switch (n) {
|
||||
|
||||
/*
|
||||
* In the legacy, these field divide input into 7. It is not necessary to delimit
|
||||
* this field originally.
|
||||
*/
|
||||
case FieldNames.FOLDER:
|
||||
case FieldNames.MEDIA_TYPE:
|
||||
case FieldNames.GENRE:
|
||||
assertEquals("oneTokenFields : " + n, 7, terms.size());
|
||||
break;
|
||||
|
||||
/*
|
||||
* These should be divided into 7.
|
||||
*/
|
||||
case FieldNames.ARTIST:
|
||||
case FieldNames.ALBUM:
|
||||
case FieldNames.TITLE:
|
||||
assertEquals("multiTokenFields : " + n, 7, terms.size());
|
||||
break;
|
||||
/*
|
||||
* ID, FOLDER_ID, YEAR
|
||||
* This is not a problem because the input value does not contain a delimiter.
|
||||
*/
|
||||
default:
|
||||
assertEquals("oneTokenFields : " + n, 7, terms.size());
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Detailed tests on Punctuation.
|
||||
* In addition to the common delimiters, there are many delimiters.
|
||||
*/
|
||||
@Test
|
||||
public void testPunctuation1() {
|
||||
|
||||
String query = "B︴C";
|
||||
String expected1 = "b";
|
||||
String expected2 = "c";
|
||||
|
||||
Arrays.stream(IndexType.values()).flatMap(i -> Arrays.stream(i.getFields())).forEach(n -> {
|
||||
List<String> terms = toTermString(n, query);
|
||||
switch (n) {
|
||||
|
||||
/*
|
||||
* In the legacy, these field divide input into 2.
|
||||
* It is not necessary to delimit
|
||||
* this field originally.
|
||||
*/
|
||||
case FieldNames.FOLDER:
|
||||
case FieldNames.GENRE:
|
||||
case FieldNames.MEDIA_TYPE:
|
||||
assertEquals("tokenized : " + n, 2, terms.size());
|
||||
assertEquals("tokenized : " + n, expected1, terms.get(0));
|
||||
assertEquals("tokenized : " + n, expected2, terms.get(1));
|
||||
break;
|
||||
|
||||
/*
|
||||
* What should the fields of this be?
|
||||
* Generally discarded.
|
||||
*/
|
||||
case FieldNames.ARTIST:
|
||||
case FieldNames.ALBUM:
|
||||
case FieldNames.TITLE:
|
||||
assertEquals("tokenized : " + n, 2, terms.size());
|
||||
assertEquals("tokenized : " + n, expected1, terms.get(0));
|
||||
assertEquals("tokenized : " + n, expected2, terms.get(1));
|
||||
break;
|
||||
/*
|
||||
* ID, FOLDER_ID, YEAR
|
||||
* This is not a problem because the input value does not contain a delimiter.
|
||||
*/
|
||||
default:
|
||||
assertEquals("tokenized : " + n, 2, terms.size());
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Detailed tests on Punctuation.
|
||||
* Many of the symbols are delimiters or target to be removed.
|
||||
*/
|
||||
@Test
|
||||
public void testPunctuation2() {
|
||||
|
||||
String query = "{'“『【【】】[︴○◎@ $〒→+]";
|
||||
Arrays.stream(IndexType.values()).flatMap(i -> Arrays.stream(i.getFields())).forEach(n -> {
|
||||
List<String> terms = toTermString(n, query);
|
||||
switch (n) {
|
||||
case FieldNames.FOLDER:
|
||||
case FieldNames.MEDIA_TYPE:
|
||||
case FieldNames.GENRE:
|
||||
case FieldNames.ARTIST:
|
||||
case FieldNames.ALBUM:
|
||||
case FieldNames.TITLE:
|
||||
assertEquals("removed : " + n, 0, terms.size());
|
||||
break;
|
||||
default:
|
||||
assertEquals("removed : " + n, 0, terms.size());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Detailed tests on Stopward.
|
||||
*
|
||||
* @see org.apache.lucene.analysis.StopAnalyzer#ENGLISH_STOP_WORDS_SET
|
||||
*/
|
||||
@Test
|
||||
public void testStopward() {
|
||||
|
||||
/*
|
||||
* Legacy behavior is to remove ENGLISH_STOP_WORDS_SET from the Token stream.
|
||||
* (Putting whether or not it matches the specification of the music search.)
|
||||
*/
|
||||
|
||||
/*
|
||||
* article.
|
||||
* This is included in ENGLISH_STOP_WORDS_SET.
|
||||
*/
|
||||
String queryArticle = "a an the";
|
||||
|
||||
/*
|
||||
* The default set as index stop word.
|
||||
* But these are not included in ENGLISH_STOP_WORDS_SET.
|
||||
*/
|
||||
String queryArticle4Index = "el la los las le les";
|
||||
|
||||
/*
|
||||
* Non-article in the ENGLISH_STOP_WORDS_SET.
|
||||
* Stopwords are essential for newspapers and documents,
|
||||
* but offten they are over-processed for song titles.
|
||||
* For example, "we will rock you" can not be searched by "will".
|
||||
*/
|
||||
String queryStop = "and are as at be but by for if in into is it no not of on " //
|
||||
+ "or such that their then there these they this to was will with";
|
||||
|
||||
Arrays.stream(IndexType.values()).flatMap(i -> Arrays.stream(i.getFields())).forEach(n -> {
|
||||
List<String> articleTerms = toTermString(n, queryArticle);
|
||||
List<String> indexArticleTerms = toTermString(n, queryArticle4Index);
|
||||
List<String> stopedTerms = toTermString(n, queryStop);
|
||||
|
||||
switch (n) {
|
||||
|
||||
case FieldNames.FOLDER:
|
||||
case FieldNames.MEDIA_TYPE:
|
||||
case FieldNames.GENRE:
|
||||
case FieldNames.ARTIST:
|
||||
case FieldNames.ALBUM:
|
||||
case FieldNames.TITLE:
|
||||
|
||||
// It is removed because it is included in ENGLISH_STOP_WORDS_SET.
|
||||
assertEquals("article : " + n, 0, articleTerms.size());
|
||||
// Not removed because it is not included in ENGLISH_STOP_WORDS_SET.
|
||||
assertEquals("sonic server index article: " + n, 6, indexArticleTerms.size());
|
||||
// It is removed because it is included in ENGLISH_STOP_WORDS_SET.
|
||||
assertEquals("non-article stop words : " + n, 0, stopedTerms.size());
|
||||
break;
|
||||
|
||||
// Legacy has common behavior for all fields.
|
||||
default:
|
||||
assertEquals("article : " + n, 0, articleTerms.size());
|
||||
assertEquals("sonic server index article: " + n, 6, indexArticleTerms.size());
|
||||
assertEquals("non-article stop words : " + n, 0, stopedTerms.size());
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple test on FullWidth.
|
||||
*/
|
||||
@Test
|
||||
public void testFullWidth() {
|
||||
String query = "FULL-WIDTH";
|
||||
List<String> terms = toTermString(query);
|
||||
assertEquals(2, terms.size());
|
||||
assertEquals("full", terms.get(0));
|
||||
assertEquals("width", terms.get(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Combined case of Stop and full-width.
|
||||
*/
|
||||
@Test
|
||||
public void testStopwardAndFullWidth() {
|
||||
|
||||
/*
|
||||
* Stop word is removed.
|
||||
*/
|
||||
String queryHalfWidth = "THIS IS FULL-WIDTH SENTENCES.";
|
||||
List<String> terms = toTermString(queryHalfWidth);
|
||||
assertEquals(3, terms.size());
|
||||
assertEquals("full", terms.get(0));
|
||||
assertEquals("width", terms.get(1));
|
||||
assertEquals("sentences", terms.get(2));
|
||||
|
||||
/*
|
||||
* Legacy can avoid Stopward if it is full width.
|
||||
* It is unclear whether it is a specification or not.
|
||||
* (Problems due to a defect in filter application order?
|
||||
* or
|
||||
* Is it popular in English speaking countries?)
|
||||
*/
|
||||
String queryFullWidth = "THIS IS FULL-WIDTH SENTENCES.";
|
||||
terms = toTermString(queryFullWidth);
|
||||
assertEquals(5, terms.size());
|
||||
assertEquals("this", terms.get(0));// removal target is ignored
|
||||
assertEquals("is", terms.get(1));
|
||||
assertEquals("full", terms.get(2));
|
||||
assertEquals("width", terms.get(3));
|
||||
assertEquals("sentences", terms.get(4));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests on ligature and diacritical marks.
|
||||
* In UAX#29, determination of non-practical word boundaries is not considered.
|
||||
* Languages that use special strings require "practical word" sample.
|
||||
* Unit testing with only ligature and diacritical marks is not possible.
|
||||
*/
|
||||
@Test
|
||||
public void testAsciiFoldingStop() {
|
||||
|
||||
String queryLigature = "Cæsar";
|
||||
String expectedLigature = "caesar";
|
||||
|
||||
String queryDiacritical = "Café";
|
||||
String expectedDiacritical = "cafe";
|
||||
|
||||
Arrays.stream(IndexType.values()).flatMap(i -> Arrays.stream(i.getFields())).forEach(n -> {
|
||||
List<String> termsLigature = toTermString(n, queryLigature);
|
||||
List<String> termsDiacritical = toTermString(n, queryDiacritical);
|
||||
switch (n) {
|
||||
|
||||
/*
|
||||
* It is decomposed into the expected string.
|
||||
*/
|
||||
case FieldNames.FOLDER:
|
||||
case FieldNames.MEDIA_TYPE:
|
||||
case FieldNames.GENRE:
|
||||
case FieldNames.ARTIST:
|
||||
case FieldNames.ALBUM:
|
||||
case FieldNames.TITLE:
|
||||
assertEquals("Cæsar : " + n, 1, termsLigature.size());
|
||||
assertEquals("Cæsar : " + n, expectedLigature, termsLigature.get(0));
|
||||
assertEquals("Café : " + n, 1, termsDiacritical.size());
|
||||
assertEquals("Café : " + n, expectedDiacritical, termsDiacritical.get(0));
|
||||
break;
|
||||
|
||||
// Legacy has common behavior for all fields.
|
||||
default:
|
||||
assertEquals("Cæsar : " + n, 1, termsLigature.size());
|
||||
assertEquals("Cæsar : " + n, expectedLigature, termsLigature.get(0));
|
||||
assertEquals("Café : " + n, 1, termsDiacritical.size());
|
||||
assertEquals("Café : " + n, expectedDiacritical, termsDiacritical.get(0));
|
||||
break;
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Detailed tests on LowerCase.
|
||||
*/
|
||||
@Test
|
||||
public void testLowerCase() {
|
||||
|
||||
// Filter operation check only. Verify only some settings.
|
||||
String query = "ABCDEFG";
|
||||
String expected = "abcdefg";
|
||||
|
||||
Arrays.stream(IndexType.values()).flatMap(i -> Arrays.stream(i.getFields())).forEach(n -> {
|
||||
List<String> terms = toTermString(n, query);
|
||||
switch (n) {
|
||||
|
||||
/*
|
||||
* In legacy, it is converted to lower. (over-processed?)
|
||||
*/
|
||||
case FieldNames.FOLDER:
|
||||
case FieldNames.MEDIA_TYPE:
|
||||
assertEquals("lower : " + n, 1, terms.size());
|
||||
assertEquals("lower : " + n, expected, terms.get(0));
|
||||
break;
|
||||
|
||||
/*
|
||||
* These are searchable fields in lower case.
|
||||
*/
|
||||
case FieldNames.GENRE:
|
||||
case FieldNames.ARTIST:
|
||||
case FieldNames.ALBUM:
|
||||
case FieldNames.TITLE:
|
||||
assertEquals("lower : " + n, 1, terms.size());
|
||||
assertEquals("lower : " + n, expected, terms.get(0));
|
||||
break;
|
||||
|
||||
// Legacy has common behavior for all fields.
|
||||
default:
|
||||
assertEquals("lower : " + n, 1, terms.size());
|
||||
assertEquals("lower : " + n, expected, terms.get(0));
|
||||
break;
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Detailed tests on EscapeRequires.
|
||||
* The reserved string is discarded unless it is purposely Escape.
|
||||
* This is fine as a search specification(if it is considered as a kind of reserved stop word).
|
||||
* However, in the case of file path, it may be a problem.
|
||||
*/
|
||||
@Test
|
||||
public void testLuceneEscapeRequires() {
|
||||
|
||||
String queryEscapeRequires = "+-&&||!(){}[]^\"~*?:\\/";
|
||||
String queryFileUsable = "+-&&!(){}[]^~";
|
||||
|
||||
Arrays.stream(IndexType.values()).flatMap(i -> Arrays.stream(i.getFields())).forEach(n -> {
|
||||
List<String> terms = toTermString(n, queryEscapeRequires);
|
||||
switch (n) {
|
||||
|
||||
/*
|
||||
* Will be removed. (Can not distinguish the directory of a particular pattern?)
|
||||
*/
|
||||
case FieldNames.FOLDER:
|
||||
assertEquals("escape : " + n, 0, terms.size());
|
||||
terms = toTermString(n, queryFileUsable);
|
||||
assertEquals("escape : " + n, 0, terms.size());
|
||||
break;
|
||||
|
||||
/*
|
||||
* Will be removed.
|
||||
*/
|
||||
case FieldNames.MEDIA_TYPE:
|
||||
case FieldNames.GENRE:
|
||||
case FieldNames.ARTIST:
|
||||
case FieldNames.ALBUM:
|
||||
case FieldNames.TITLE:
|
||||
assertEquals("escape : " + n, 0, terms.size());
|
||||
break;
|
||||
|
||||
// Will be removed.
|
||||
default:
|
||||
assertEquals("escape : " + n, 0, terms.size());
|
||||
break;
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an example that makes UAX 29 differences easy to understand.
|
||||
*/
|
||||
@Test
|
||||
public void testUax29() {
|
||||
|
||||
/*
|
||||
* Case using test resource name
|
||||
*/
|
||||
|
||||
// title
|
||||
String query = "Bach: Goldberg Variations, BWV 988 - Aria";
|
||||
List<String> terms = toTermString(query);
|
||||
assertEquals(6, terms.size());
|
||||
assertEquals("bach", terms.get(0));
|
||||
assertEquals("goldberg", terms.get(1));
|
||||
assertEquals("variations", terms.get(2));
|
||||
assertEquals("bwv", terms.get(3));
|
||||
assertEquals("988", terms.get(4));
|
||||
assertEquals("aria", terms.get(5));
|
||||
|
||||
// artist
|
||||
query = "_ID3_ARTIST_ Céline Frisch: Café Zimmermann";
|
||||
terms = toTermString(query);
|
||||
assertEquals(5, terms.size());
|
||||
assertEquals("id3_artist", terms.get(0));
|
||||
assertEquals("celine", terms.get(1));
|
||||
assertEquals("frisch", terms.get(2));
|
||||
assertEquals("cafe", terms.get(3));
|
||||
assertEquals("zimmermann", terms.get(4));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Special handling of single quotes.
|
||||
*/
|
||||
@Test
|
||||
public void testSingleQuotes() {
|
||||
|
||||
/*
|
||||
* A somewhat cultural that seems to be related to a specific language.
|
||||
*/
|
||||
String query = "This is Airsonic's analysis.";
|
||||
List<String> terms = toTermString(query);
|
||||
assertEquals(2, terms.size());
|
||||
assertEquals("airsonic", terms.get(0));
|
||||
assertEquals("analysis", terms.get(1));
|
||||
|
||||
query = "We’ve been here before.";
|
||||
terms = toTermString(query);
|
||||
assertEquals(5, terms.size());
|
||||
assertEquals("we", terms.get(0));
|
||||
assertEquals("ve", terms.get(1));
|
||||
assertEquals("been", terms.get(2));
|
||||
assertEquals("here", terms.get(3));
|
||||
assertEquals("before", terms.get(4));
|
||||
|
||||
query = "LʼHomme";
|
||||
terms = toTermString(query);
|
||||
assertEquals(1, terms.size());
|
||||
assertEquals("lʼhomme", terms.get(0));
|
||||
|
||||
query = "L'Homme";
|
||||
terms = toTermString(query);
|
||||
assertEquals(1, terms.size());
|
||||
assertEquals("l'homme", terms.get(0));
|
||||
|
||||
query = "aujourd'hui";
|
||||
terms = toTermString(query);
|
||||
assertEquals(1, terms.size());
|
||||
assertEquals("aujourd'hui", terms.get(0));
|
||||
|
||||
query = "fo'c'sle";
|
||||
terms = toTermString(query);
|
||||
assertEquals(1, terms.size());
|
||||
assertEquals("fo'c'sle", terms.get(0));
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* There is also a filter that converts the tense to correspond to the search by the present
|
||||
* tense.
|
||||
*/
|
||||
@Test
|
||||
public void testPastParticiple() {
|
||||
|
||||
/*
|
||||
* Confirming no conversion to present tense.
|
||||
*/
|
||||
String query = "This is formed with a form of the verb \"have\" and a past participl.";
|
||||
List<String> terms = toTermString(query);
|
||||
assertEquals(6, terms.size());
|
||||
assertEquals("formed", terms.get(0));// leave passive / not "form"
|
||||
assertEquals("form", terms.get(1));
|
||||
assertEquals("verb", terms.get(2));
|
||||
assertEquals("have", terms.get(3));
|
||||
assertEquals("past", terms.get(4));
|
||||
assertEquals("participl", terms.get(5));
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* There are also filters that convert plurals to singular.
|
||||
*/
|
||||
@Test
|
||||
public void testNumeral() {
|
||||
|
||||
/*
|
||||
* Confirming no conversion to singular.
|
||||
*/
|
||||
|
||||
String query = "books boxes cities leaves men glasses";
|
||||
List<String> terms = toTermString(query);
|
||||
assertEquals(6, terms.size());
|
||||
assertEquals("books", terms.get(0));// leave numeral / not singular
|
||||
assertEquals("boxes", terms.get(1));
|
||||
assertEquals("cities", terms.get(2));
|
||||
assertEquals("leaves", terms.get(3));
|
||||
assertEquals("men", terms.get(4));
|
||||
assertEquals("glasses", terms.get(5));
|
||||
}
|
||||
|
||||
private List<String> toTermString(String str) {
|
||||
return toTermString(null, str);
|
||||
}
|
||||
|
||||
private List<String> toTermString(String field, String str) {
|
||||
List<String> result = new ArrayList<>();
|
||||
try {
|
||||
TokenStream stream = analyzerFactory.getAnalyzer().tokenStream(field,
|
||||
new StringReader(str));
|
||||
stream.reset();
|
||||
while (stream.incrementToken()) {
|
||||
result.add(stream.getAttribute(TermAttribute.class).toString()
|
||||
.replaceAll("^term\\=", ""));
|
||||
}
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
LoggerFactory.getLogger(AnalyzerFactoryTestCase.class)
|
||||
.error("Error during Token processing.", e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Should be added in later versions.
|
||||
*/
|
||||
public void testWildCard() {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private List<String> toQueryTermString(String field, String str) {
|
||||
List<String> result = new ArrayList<>();
|
||||
try {
|
||||
TokenStream stream = analyzerFactory.getQueryAnalyzer().tokenStream(field,
|
||||
new StringReader(str));
|
||||
stream.reset();
|
||||
while (stream.incrementToken()) {
|
||||
result.add(stream.getAttribute(TermAttribute.class).toString()
|
||||
.replaceAll("^term\\=", ""));
|
||||
}
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
LoggerFactory.getLogger(AnalyzerFactoryTestCase.class)
|
||||
.error("Error during Token processing.", e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
+256
@@ -0,0 +1,256 @@
|
||||
|
||||
package org.airsonic.player.service.search;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.airsonic.player.domain.MusicFolder;
|
||||
import org.airsonic.player.domain.RandomSearchCriteria;
|
||||
import org.airsonic.player.domain.SearchCriteria;
|
||||
import org.airsonic.player.util.HomeRule;
|
||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||
import org.apache.lucene.queryParser.ParseException;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.util.NumericUtils;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.rules.SpringClassRule;
|
||||
import org.springframework.test.context.junit4.rules.SpringMethodRule;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Test case for QueryFactory.
|
||||
* These cases have the purpose of observing the current situation
|
||||
* and observing the impact of upgrading Lucene.
|
||||
*/
|
||||
@ContextConfiguration(
|
||||
locations = {
|
||||
"/applicationContext-service.xml",
|
||||
"/applicationContext-cache.xml",
|
||||
"/applicationContext-testdb.xml",
|
||||
"/applicationContext-mockSonos.xml" })
|
||||
@DirtiesContext(
|
||||
classMode = DirtiesContext.ClassMode.AFTER_CLASS)
|
||||
public class QueryFactoryTestCase {
|
||||
|
||||
@ClassRule
|
||||
public static final SpringClassRule classRule = new SpringClassRule() {
|
||||
HomeRule homeRule = new HomeRule();
|
||||
|
||||
@Override
|
||||
public Statement apply(Statement base, Description description) {
|
||||
Statement spring = super.apply(base, description);
|
||||
return homeRule.apply(spring, description);
|
||||
}
|
||||
};
|
||||
|
||||
@Rule
|
||||
public final SpringMethodRule springMethodRule = new SpringMethodRule();
|
||||
|
||||
@Autowired
|
||||
private QueryFactory queryFactory;
|
||||
|
||||
private static final String QUERY_ENG_ONLY = "ABC DEF";
|
||||
|
||||
private static final String SEPA = System.getProperty("file.separator");
|
||||
|
||||
private static final String PATH1 = SEPA + "var" + SEPA + "music1";
|
||||
private static final String PATH2 = SEPA + "var" + SEPA + "music2";
|
||||
|
||||
private static final int FID1 = 10;
|
||||
private static final int FID2 = 20;
|
||||
|
||||
private static final MusicFolder MUSIC_FOLDER1 =
|
||||
new MusicFolder(Integer.valueOf(FID1), new File(PATH1), "music1", true, new java.util.Date());
|
||||
private static final MusicFolder MUSIC_FOLDER2 =
|
||||
new MusicFolder(Integer.valueOf(FID2), new File(PATH2), "music2", true, new java.util.Date());
|
||||
|
||||
private static final List<MusicFolder> SINGLE_FOLDERS = Arrays.asList(MUSIC_FOLDER1);
|
||||
private static final List<MusicFolder> MULTI_FOLDERS = Arrays.asList(MUSIC_FOLDER1, MUSIC_FOLDER2);
|
||||
|
||||
@Test
|
||||
public void testSearchArtist() throws ParseException, IOException {
|
||||
SearchCriteria criteria = new SearchCriteria();
|
||||
criteria.setOffset(10);
|
||||
criteria.setCount(Integer.MAX_VALUE);
|
||||
criteria.setQuery(QUERY_ENG_ONLY);
|
||||
|
||||
Query query = queryFactory.search(criteria, SINGLE_FOLDERS, IndexType.ARTIST);
|
||||
assertEquals("SearchArtist",
|
||||
"+((artist:abc* folder:abc*) (artist:def* folder:def*)) +spanOr([folder:" + PATH1 + "])",
|
||||
query.toString());
|
||||
|
||||
query = queryFactory.search(criteria, MULTI_FOLDERS, IndexType.ARTIST);
|
||||
assertEquals("SearchArtist", "+((artist:abc* folder:abc*) (artist:def* folder:def*)) +spanOr([folder:" + PATH1
|
||||
+ ", folder:" + PATH2 + "])", query.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchAlbum() throws ParseException, IOException {
|
||||
SearchCriteria criteria = new SearchCriteria();
|
||||
criteria.setOffset(10);
|
||||
criteria.setCount(Integer.MAX_VALUE);
|
||||
criteria.setQuery(QUERY_ENG_ONLY);
|
||||
|
||||
Query query = queryFactory.search(criteria, SINGLE_FOLDERS, IndexType.ALBUM);
|
||||
assertEquals("SearchAlbum",
|
||||
"+((album:abc* artist:abc* folder:abc*) (album:def* artist:def* folder:def*)) +spanOr([folder:" + PATH1
|
||||
+ "])",
|
||||
query.toString());
|
||||
|
||||
query = queryFactory.search(criteria, MULTI_FOLDERS, IndexType.ALBUM);
|
||||
assertEquals("SearchAlbum",
|
||||
"+((album:abc* artist:abc* folder:abc*) (album:def* artist:def* folder:def*)) +spanOr([folder:" + PATH1
|
||||
+ ", folder:" + PATH2 + "])",
|
||||
query.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchSong() throws ParseException, IOException {
|
||||
SearchCriteria criteria = new SearchCriteria();
|
||||
criteria.setOffset(10);
|
||||
criteria.setCount(Integer.MAX_VALUE);
|
||||
criteria.setQuery(QUERY_ENG_ONLY);
|
||||
|
||||
Query query = queryFactory.search(criteria, SINGLE_FOLDERS, IndexType.SONG);
|
||||
assertEquals("SearchSong",
|
||||
"+((title:abc* artist:abc*) (title:def* artist:def*)) +spanOr([folder:" + PATH1 + "])",
|
||||
query.toString());
|
||||
|
||||
query = queryFactory.search(criteria, MULTI_FOLDERS, IndexType.SONG);
|
||||
assertEquals("SearchSong", "+((title:abc* artist:abc*) (title:def* artist:def*)) +spanOr([folder:" + PATH1
|
||||
+ ", folder:" + PATH2 + "])", query.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchArtistId3() throws ParseException, IOException {
|
||||
SearchCriteria criteria = new SearchCriteria();
|
||||
criteria.setOffset(10);
|
||||
criteria.setCount(Integer.MAX_VALUE);
|
||||
criteria.setQuery(QUERY_ENG_ONLY);
|
||||
|
||||
Query query = queryFactory.search(criteria, SINGLE_FOLDERS, IndexType.ARTIST_ID3);
|
||||
assertEquals("SearchSong", "+((artist:abc*) (artist:def*)) +spanOr([folderId:"
|
||||
+ NumericUtils.intToPrefixCoded(FID1) + "])", query.toString());
|
||||
|
||||
query = queryFactory.search(criteria, MULTI_FOLDERS, IndexType.ARTIST_ID3);
|
||||
assertEquals("SearchSong",
|
||||
"+((artist:abc*) (artist:def*)) +spanOr([folderId:" + NumericUtils.intToPrefixCoded(FID1)
|
||||
+ ", folderId:" + NumericUtils.intToPrefixCoded(FID2) + "])",
|
||||
query.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchAlbumId3() throws ParseException, IOException {
|
||||
SearchCriteria criteria = new SearchCriteria();
|
||||
criteria.setOffset(10);
|
||||
criteria.setCount(Integer.MAX_VALUE);
|
||||
criteria.setQuery(QUERY_ENG_ONLY);
|
||||
|
||||
Query query = queryFactory.search(criteria, SINGLE_FOLDERS, IndexType.ALBUM_ID3);
|
||||
assertEquals(
|
||||
"SearchAlbumId3", "+((album:abc* artist:abc* folderId:abc*) (album:def* artist:def* folderId:def*)) "
|
||||
+ "+spanOr([folderId:" + NumericUtils.intToPrefixCoded(FID1) + "])",
|
||||
query.toString());
|
||||
|
||||
query = queryFactory.search(criteria, MULTI_FOLDERS, IndexType.ALBUM_ID3);
|
||||
assertEquals("SearchAlbumId3",
|
||||
"+((album:abc* artist:abc* folderId:abc*) (album:def* artist:def* folderId:def*)) +spanOr([folderId:"
|
||||
+ NumericUtils.intToPrefixCoded(FID1) + ", folderId:"
|
||||
+ NumericUtils.intToPrefixCoded(FID2) + "])",
|
||||
query.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchByNameArtist() throws ParseException {
|
||||
Query query = queryFactory.searchByName(FieldNames.ARTIST, QUERY_ENG_ONLY);
|
||||
assertEquals("SearchByNameArtist", "artist:abc artist:def*", query.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchByNameAlbum() throws ParseException {
|
||||
Query query = queryFactory.searchByName(FieldNames.ALBUM, QUERY_ENG_ONLY);
|
||||
assertEquals("SearchByNameAlbum", "album:abc album:def*", query.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchByNameTitle() throws ParseException {
|
||||
Query query = queryFactory.searchByName(FieldNames.TITLE, QUERY_ENG_ONLY);
|
||||
assertEquals("SearchByNameTitle", "title:abc title:def*", query.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRandomSongs() {
|
||||
RandomSearchCriteria criteria = new RandomSearchCriteria(50, "Classic Rock",
|
||||
Integer.valueOf(1900), Integer.valueOf(2000), SINGLE_FOLDERS);
|
||||
|
||||
Query query = queryFactory.getRandomSongs(criteria);
|
||||
assertEquals(ToStringBuilder.reflectionToString(criteria),
|
||||
"+mediaType:music +genre:classicrock +year:[1900 TO 2000] +spanOr([folder:" + PATH1 + "])",
|
||||
query.toString());
|
||||
|
||||
criteria = new RandomSearchCriteria(50, "Classic Rock", Integer.valueOf(1900),
|
||||
Integer.valueOf(2000), MULTI_FOLDERS);
|
||||
query = queryFactory.getRandomSongs(criteria);
|
||||
assertEquals(ToStringBuilder.reflectionToString(criteria),
|
||||
"+mediaType:music +genre:classicrock +year:[1900 TO 2000] +spanOr([folder:" + PATH1 + ", folder:" + PATH2
|
||||
+ "])",
|
||||
query.toString());
|
||||
|
||||
criteria = new RandomSearchCriteria(50, "Classic Rock", null, null, MULTI_FOLDERS);
|
||||
query = queryFactory.getRandomSongs(criteria);
|
||||
assertEquals(ToStringBuilder.reflectionToString(criteria),
|
||||
"+mediaType:music +genre:classicrock +spanOr([folder:" + PATH1 + ", folder:" + PATH2 + "])",
|
||||
query.toString());
|
||||
|
||||
criteria = new RandomSearchCriteria(50, "Classic Rock", Integer.valueOf(1900), null,
|
||||
MULTI_FOLDERS);
|
||||
query = queryFactory.getRandomSongs(criteria);
|
||||
assertEquals(ToStringBuilder.reflectionToString(criteria),
|
||||
"+mediaType:music +genre:classicrock +year:[1900 TO *] +spanOr([folder:" + PATH1 + ", folder:" + PATH2
|
||||
+ "])",
|
||||
query.toString());
|
||||
|
||||
criteria = new RandomSearchCriteria(50, "Classic Rock", null, Integer.valueOf(2000),
|
||||
MULTI_FOLDERS);
|
||||
query = queryFactory.getRandomSongs(criteria);
|
||||
assertEquals(ToStringBuilder.reflectionToString(criteria),
|
||||
"+mediaType:music +genre:classicrock +year:[* TO 2000] +spanOr([folder:" + PATH1 + ", folder:" + PATH2
|
||||
+ "])",
|
||||
query.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRandomAlbums() {
|
||||
Query query = queryFactory.getRandomAlbums(SINGLE_FOLDERS);
|
||||
assertEquals(ToStringBuilder.reflectionToString(SINGLE_FOLDERS),
|
||||
"spanOr([folder:" + PATH1 + "])", query.toString());
|
||||
|
||||
query = queryFactory.getRandomAlbums(MULTI_FOLDERS);
|
||||
assertEquals(ToStringBuilder.reflectionToString(MULTI_FOLDERS),
|
||||
"spanOr([folder:" + PATH1 + ", folder:" + PATH2 + "])", query.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRandomAlbumsId3() {
|
||||
Query query = queryFactory.getRandomAlbumsId3(SINGLE_FOLDERS);
|
||||
assertEquals(ToStringBuilder.reflectionToString(SINGLE_FOLDERS),
|
||||
"spanOr([folderId:" + NumericUtils.intToPrefixCoded(FID1) + "])", query.toString());
|
||||
|
||||
query = queryFactory.getRandomAlbumsId3(MULTI_FOLDERS);
|
||||
assertEquals(ToStringBuilder.reflectionToString(MULTI_FOLDERS),
|
||||
"spanOr([folderId:" + NumericUtils.intToPrefixCoded(FID1) + ", folderId:"
|
||||
+ NumericUtils.intToPrefixCoded(FID2) + "])",
|
||||
query.toString());
|
||||
}
|
||||
|
||||
}
|
||||
+466
@@ -0,0 +1,466 @@
|
||||
|
||||
package org.airsonic.player.service.search;
|
||||
|
||||
import com.codahale.metrics.ConsoleReporter;
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.codahale.metrics.Timer;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.airsonic.player.TestCaseUtils;
|
||||
import org.airsonic.player.dao.AlbumDao;
|
||||
import org.airsonic.player.dao.DaoHelper;
|
||||
import org.airsonic.player.dao.MediaFileDao;
|
||||
import org.airsonic.player.dao.MusicFolderDao;
|
||||
import org.airsonic.player.dao.MusicFolderTestData;
|
||||
import org.airsonic.player.domain.Album;
|
||||
import org.airsonic.player.domain.Artist;
|
||||
import org.airsonic.player.domain.MediaFile;
|
||||
import org.airsonic.player.domain.MediaFile.MediaType;
|
||||
import org.airsonic.player.domain.MusicFolder;
|
||||
import org.airsonic.player.domain.ParamSearchResult;
|
||||
import org.airsonic.player.domain.RandomSearchCriteria;
|
||||
import org.airsonic.player.domain.SearchCriteria;
|
||||
import org.airsonic.player.domain.SearchResult;
|
||||
import org.airsonic.player.service.MediaScannerService;
|
||||
import org.airsonic.player.service.SearchService;
|
||||
import org.airsonic.player.service.SettingsService;
|
||||
import org.airsonic.player.service.search.IndexType;
|
||||
import org.airsonic.player.util.HomeRule;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.rules.SpringClassRule;
|
||||
import org.springframework.test.context.junit4.rules.SpringMethodRule;
|
||||
import org.subsonic.restapi.ArtistID3;
|
||||
|
||||
@ContextConfiguration(
|
||||
locations = {
|
||||
"/applicationContext-service.xml",
|
||||
"/applicationContext-cache.xml",
|
||||
"/applicationContext-testdb.xml",
|
||||
"/applicationContext-mockSonos.xml" })
|
||||
@DirtiesContext(
|
||||
classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
|
||||
public class SearchServiceTestCase {
|
||||
|
||||
@ClassRule
|
||||
public static final SpringClassRule classRule = new SpringClassRule() {
|
||||
HomeRule homeRule = new HomeRule();
|
||||
|
||||
@Override
|
||||
public Statement apply(Statement base, Description description) {
|
||||
Statement spring = super.apply(base, description);
|
||||
return homeRule.apply(spring, description);
|
||||
}
|
||||
};
|
||||
|
||||
@Rule
|
||||
public final SpringMethodRule springMethodRule = new SpringMethodRule();
|
||||
|
||||
private final MetricRegistry metrics = new MetricRegistry();
|
||||
|
||||
@Autowired
|
||||
private MediaScannerService mediaScannerService;
|
||||
|
||||
@Autowired
|
||||
private MediaFileDao mediaFileDao;
|
||||
|
||||
@Autowired
|
||||
private MusicFolderDao musicFolderDao;
|
||||
|
||||
@Autowired
|
||||
private DaoHelper daoHelper;
|
||||
|
||||
@Autowired
|
||||
private AlbumDao albumDao;
|
||||
|
||||
@Autowired
|
||||
private SearchService searchService;
|
||||
|
||||
@Autowired
|
||||
private SettingsService settingsService;
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
|
||||
@Autowired
|
||||
ResourceLoader resourceLoader;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
populateDatabase();
|
||||
}
|
||||
|
||||
private static boolean dataBasePopulated;
|
||||
|
||||
private int count = 1;
|
||||
|
||||
/*
|
||||
* Cases susceptible to SerchService refactoring and version upgrades.
|
||||
* It is not exhaustive.
|
||||
*/
|
||||
private synchronized void populateDatabase() {
|
||||
|
||||
/*
|
||||
* It seems that there is a case that does not work well
|
||||
* if you test immediately after initialization in 1 method.
|
||||
* It may be improved later.
|
||||
*/
|
||||
try {
|
||||
Thread.sleep(300 * count++);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (!dataBasePopulated) {
|
||||
|
||||
MusicFolderTestData.getTestMusicFolders().forEach(musicFolderDao::createMusicFolder);
|
||||
settingsService.clearMusicFolderCache();
|
||||
TestCaseUtils.execScan(mediaScannerService);
|
||||
System.out.println("--- Report of records count per table ---");
|
||||
Map<String, Integer> records = TestCaseUtils.recordsInAllTables(daoHelper);
|
||||
records.keySet().stream().filter(s -> s.equals("MEDIA_FILE") // 20
|
||||
| s.equals("ARTIST") // 5
|
||||
| s.equals("MUSIC_FOLDER")// 3
|
||||
| s.equals("ALBUM"))// 5
|
||||
.forEach(tableName -> System.out
|
||||
.println("\t" + tableName + " : " + records.get(tableName).toString()));
|
||||
// Music Folder Music must have 3 children
|
||||
List<MediaFile> listeMusicChildren = mediaFileDao.getChildrenOf(
|
||||
new File(MusicFolderTestData.resolveMusicFolderPath()).getPath());
|
||||
Assert.assertEquals(3, listeMusicChildren.size());
|
||||
// Music Folder Music2 must have 1 children
|
||||
List<MediaFile> listeMusic2Children = mediaFileDao.getChildrenOf(
|
||||
new File(MusicFolderTestData.resolveMusic2FolderPath()).getPath());
|
||||
Assert.assertEquals(1, listeMusic2Children.size());
|
||||
System.out.println("--- *********************** ---");
|
||||
dataBasePopulated = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchTypical() {
|
||||
|
||||
/*
|
||||
* A simple test that is expected to easily detect API syntax differences when updating lucene.
|
||||
* Complete route coverage and data coverage in this case alone are not conscious.
|
||||
*/
|
||||
|
||||
List<MusicFolder> allMusicFolders = musicFolderDao.getAllMusicFolders();
|
||||
Assert.assertEquals(3, allMusicFolders.size());
|
||||
|
||||
// *** testSearch() ***
|
||||
|
||||
String query = "Sarah Walker";
|
||||
final SearchCriteria searchCriteria = new SearchCriteria();
|
||||
searchCriteria.setQuery(query);
|
||||
searchCriteria.setCount(Integer.MAX_VALUE);
|
||||
searchCriteria.setOffset(0);
|
||||
|
||||
/*
|
||||
* _ID3_ALBUMARTIST_ Sarah Walker/Nash Ensemble
|
||||
* Should find any version of Lucene.
|
||||
*/
|
||||
SearchResult result = searchService.search(searchCriteria, allMusicFolders,
|
||||
IndexType.ALBUM);
|
||||
Assert.assertEquals("(0) Specify '" + query + "' as query, total Hits is", 1,
|
||||
result.getTotalHits());
|
||||
Assert.assertEquals("(1) Specify artist '" + query + "' as query. Artist SIZE is", 0,
|
||||
result.getArtists().size());
|
||||
Assert.assertEquals("(2) Specify artist '" + query + "' as query. Album SIZE is", 0,
|
||||
result.getAlbums().size());
|
||||
Assert.assertEquals("(3) Specify artist '" + query + "' as query, MediaFile SIZE is", 1,
|
||||
result.getMediaFiles().size());
|
||||
Assert.assertEquals("(4) ", MediaType.ALBUM, result.getMediaFiles().get(0).getMediaType());
|
||||
Assert.assertEquals(
|
||||
"(5) Specify artist '" + query + "' as query, and get a album. Name is ",
|
||||
"_ID3_ALBUMARTIST_ Sarah Walker/Nash Ensemble",
|
||||
result.getMediaFiles().get(0).getArtist());
|
||||
Assert.assertEquals(
|
||||
"(6) Specify artist '" + query + "' as query, and get a album. Name is ",
|
||||
"_ID3_ALBUM_ Ravel - Chamber Music With Voice",
|
||||
result.getMediaFiles().get(0).getAlbumName());
|
||||
|
||||
/*
|
||||
* _ID3_ALBUM_ Ravel - Chamber Music With Voice
|
||||
* Should find any version of Lucene.
|
||||
*/
|
||||
query = "music";
|
||||
searchCriteria.setQuery(query);
|
||||
result = searchService.search(searchCriteria, allMusicFolders, IndexType.ALBUM_ID3);
|
||||
Assert.assertEquals("Specify '" + query + "' as query, total Hits is", 1,
|
||||
result.getTotalHits());
|
||||
Assert.assertEquals("(7) Specify '" + query + "' as query, and get a song. Artist SIZE is ",
|
||||
0, result.getArtists().size());
|
||||
Assert.assertEquals("(8) Specify '" + query + "' as query, and get a song. Album SIZE is ",
|
||||
1, result.getAlbums().size());
|
||||
Assert.assertEquals(
|
||||
"(9) Specify '" + query + "' as query, and get a song. MediaFile SIZE is ", 0,
|
||||
result.getMediaFiles().size());
|
||||
Assert.assertEquals("(9) Specify '" + query + "' as query, and get a album. Name is ",
|
||||
"_ID3_ALBUMARTIST_ Sarah Walker/Nash Ensemble",
|
||||
result.getAlbums().get(0).getArtist());
|
||||
Assert.assertEquals("(10) Specify '" + query + "' as query, and get a album. Name is ",
|
||||
"_ID3_ALBUM_ Ravel - Chamber Music With Voice",
|
||||
result.getAlbums().get(0).getName());
|
||||
|
||||
/*
|
||||
* _ID3_ALBUM_ Ravel - Chamber Music With Voice
|
||||
* Should find any version of Lucene.
|
||||
*/
|
||||
query = "Ravel - Chamber Music";
|
||||
searchCriteria.setQuery(query);
|
||||
result = searchService.search(searchCriteria, allMusicFolders, IndexType.SONG);
|
||||
Assert.assertEquals("(11) Specify album '" + query + "' as query, total Hits is", 2,
|
||||
result.getTotalHits());
|
||||
Assert.assertEquals("(12) Specify album '" + query + "', and get a song. Artist SIZE is", 0,
|
||||
result.getArtists().size());
|
||||
Assert.assertEquals("(13) Specify album '" + query + "', and get a song. Album SIZE is", 0,
|
||||
result.getAlbums().size());
|
||||
Assert.assertEquals("(14) Specify album '" + query + "', and get a song. MediaFile SIZE is",
|
||||
2, result.getMediaFiles().size());
|
||||
Assert.assertEquals("(15) Specify album '" + query + "', and get songs. The first song is ",
|
||||
"01 - Gaspard de la Nuit - i. Ondine", result.getMediaFiles().get(0).getTitle());
|
||||
Assert.assertEquals(
|
||||
"(16) Specify album '" + query + "', and get songs. The second song is ",
|
||||
"02 - Gaspard de la Nuit - ii. Le Gibet", result.getMediaFiles().get(1).getTitle());
|
||||
|
||||
// *** testSearchByName() ***
|
||||
|
||||
/*
|
||||
* _ID3_ALBUM_ Sackcloth 'n' Ashes
|
||||
* Should be 1 in Lucene 3.0(Because Single quate is not a delimiter).
|
||||
*/
|
||||
query = "Sackcloth 'n' Ashes";
|
||||
ParamSearchResult<Album> albumResult = searchService.searchByName(query, 0,
|
||||
Integer.MAX_VALUE, allMusicFolders, Album.class);
|
||||
Assert.assertEquals(
|
||||
"(17) Specify album name '" + query + "' as the name, and get an album.", 1,
|
||||
albumResult.getItems().size());
|
||||
Assert.assertEquals("(18) Specify '" + query + "' as the name, The album name is ",
|
||||
"_ID3_ALBUM_ Sackcloth 'n' Ashes", albumResult.getItems().get(0).getName());
|
||||
Assert.assertEquals(
|
||||
"(19) Whether the acquired album contains data of the specified album name", 1L,
|
||||
albumResult.getItems().stream()
|
||||
.filter(r -> "_ID3_ALBUM_ Sackcloth \'n\' Ashes".equals(r.getName()))
|
||||
.count());
|
||||
|
||||
/*
|
||||
* Should be 0 in Lucene 3.0(Since the slash is not a delimiter).
|
||||
*/
|
||||
query = "lker/Nash";
|
||||
ParamSearchResult<ArtistID3> artistId3Result = searchService.searchByName(query, 0,
|
||||
Integer.MAX_VALUE, allMusicFolders, ArtistID3.class);
|
||||
Assert.assertEquals("(20) Specify '" + query + "' as the name, and get an artist.", 0,
|
||||
artistId3Result.getItems().size());
|
||||
ParamSearchResult<Artist> artistResult = searchService.searchByName(query, 0,
|
||||
Integer.MAX_VALUE, allMusicFolders, Artist.class);
|
||||
Assert.assertEquals("(21) Specify '" + query + "' as the name, and get an artist.", 0,
|
||||
artistResult.getItems().size());
|
||||
|
||||
// *** testGetRandomSongs() ***
|
||||
|
||||
/*
|
||||
* Regardless of the Lucene version,
|
||||
* RandomSearchCriteria can specify null and means the maximum range.
|
||||
* 11 should be obtainable.
|
||||
*/
|
||||
RandomSearchCriteria randomSearchCriteria = new RandomSearchCriteria(Integer.MAX_VALUE, // count
|
||||
null, // genre,
|
||||
null, // fromYear
|
||||
null, // toYear
|
||||
allMusicFolders // musicFolders
|
||||
);
|
||||
List<MediaFile> allRandomSongs = searchService.getRandomSongs(randomSearchCriteria);
|
||||
Assert.assertEquals(
|
||||
"(22) Specify MAX_VALUE as the upper limit, and randomly acquire songs.", 11,
|
||||
allRandomSongs.size());
|
||||
|
||||
/*
|
||||
* Regardless of the Lucene version,
|
||||
* 7 should be obtainable.
|
||||
*/
|
||||
randomSearchCriteria = new RandomSearchCriteria(Integer.MAX_VALUE, // count
|
||||
null, // genre,
|
||||
1900, // fromYear
|
||||
null, // toYear
|
||||
allMusicFolders // musicFolders
|
||||
);
|
||||
allRandomSongs = searchService.getRandomSongs(randomSearchCriteria);
|
||||
Assert.assertEquals("(23) Specify 1900 as 'fromYear', and randomly acquire songs.", 7,
|
||||
allRandomSongs.size());
|
||||
|
||||
/*
|
||||
* Regardless of the Lucene version,
|
||||
* It should be 0 because it is a non-existent genre.
|
||||
*/
|
||||
randomSearchCriteria = new RandomSearchCriteria(Integer.MAX_VALUE, // count
|
||||
"Chamber Music", // genre,
|
||||
null, // fromYear
|
||||
null, // toYear
|
||||
allMusicFolders // musicFolders
|
||||
);
|
||||
allRandomSongs = searchService.getRandomSongs(randomSearchCriteria);
|
||||
Assert.assertEquals("(24) Specify music as 'genre', and randomly acquire songs.", 0,
|
||||
allRandomSongs.size());
|
||||
|
||||
// *** testGetRandomAlbums() ***
|
||||
|
||||
/*
|
||||
* Acquisition of maximum number(5).
|
||||
*/
|
||||
List<Album> allAlbums = albumDao.getAlphabeticalAlbums(0, 0, true, true, allMusicFolders);
|
||||
Assert.assertEquals("(25) Get all albums with Dao.", 5, allAlbums.size());
|
||||
List<MediaFile> allRandomAlbums = searchService.getRandomAlbums(Integer.MAX_VALUE,
|
||||
allMusicFolders);
|
||||
Assert.assertEquals("(26) Specify Integer.MAX_VALUE as the upper limit,"
|
||||
+ "and randomly acquire albums(file struct).", 5, allRandomAlbums.size());
|
||||
|
||||
/*
|
||||
* Acquisition of maximum number(5).
|
||||
*/
|
||||
List<Album> allRandomAlbumsId3 = searchService.getRandomAlbumsId3(Integer.MAX_VALUE,
|
||||
allMusicFolders);
|
||||
Assert.assertEquals(
|
||||
"(27) Specify Integer.MAX_VALUE as the upper limit, and randomly acquire albums(ID3).",
|
||||
5, allRandomAlbumsId3.size());
|
||||
|
||||
/*
|
||||
* Total is 4.
|
||||
*/
|
||||
query = "ID 3 ARTIST";
|
||||
searchCriteria.setQuery(query);
|
||||
result = searchService.search(searchCriteria, allMusicFolders, IndexType.ARTIST_ID3);
|
||||
Assert.assertEquals("(28) Specify '" + query + "', total Hits is", 4,
|
||||
result.getTotalHits());
|
||||
Assert.assertEquals("(29) Specify '" + query + "', and get an artists. Artist SIZE is ", 4,
|
||||
result.getArtists().size());
|
||||
Assert.assertEquals("(30) Specify '" + query + "', and get a artists. Album SIZE is ", 0,
|
||||
result.getAlbums().size());
|
||||
Assert.assertEquals("(31) Specify '" + query + "', and get a artists. MediaFile SIZE is ",
|
||||
0, result.getMediaFiles().size());
|
||||
|
||||
/*
|
||||
* Three hits to the artist.
|
||||
* ALBUMARTIST is not registered with these.
|
||||
* Therefore, the registered value of ARTIST is substituted in ALBUMARTIST.
|
||||
*/
|
||||
long count = result.getArtists().stream()
|
||||
.filter(a -> a.getName().startsWith("_ID3_ARTIST_")).count();
|
||||
Assert.assertEquals("(32) Artist whose name contains \\\"_ID3_ARTIST_\\\" is 3 records.",
|
||||
3L, count);
|
||||
|
||||
/*
|
||||
* The structure of "01 - Sonata Violin & Cello I. Allegro.ogg"
|
||||
* ARTIST -> _ID3_ARTIST_ Sarah Walker/Nash Ensemble
|
||||
* ALBUMARTIST -> _ID3_ALBUMARTIST_ Sarah Walker/Nash Ensemble
|
||||
* (The result must not contain duplicates. And ALBUMARTIST must be returned correctly.)
|
||||
*/
|
||||
count = result.getArtists().stream()
|
||||
.filter(a -> a.getName().startsWith("_ID3_ALBUMARTIST_")).count();
|
||||
Assert.assertEquals("(33) Artist whose name is \"_ID3_ARTIST_\" is 1 records.", 1L, count);
|
||||
|
||||
/*
|
||||
* Below is a simple loop test.
|
||||
* How long is the total time?
|
||||
*/
|
||||
int countForEachMethod = 500;
|
||||
String[] randomWords4Search = createRandomWords(countForEachMethod);
|
||||
String[] randomWords4SearchByName = createRandomWords(countForEachMethod);
|
||||
|
||||
Timer globalTimer = metrics
|
||||
.timer(MetricRegistry.name(SearchServiceTestCase.class, "Timer.global"));
|
||||
final Timer.Context globalTimerContext = globalTimer.time();
|
||||
|
||||
System.out.println("--- Random search (" + countForEachMethod * 5 + " times) ---");
|
||||
|
||||
// testSearch()
|
||||
Arrays.stream(randomWords4Search).forEach(w -> {
|
||||
searchCriteria.setQuery(w);
|
||||
searchService.search(searchCriteria, allMusicFolders, IndexType.ALBUM);
|
||||
});
|
||||
|
||||
// testSearchByName()
|
||||
Arrays.stream(randomWords4SearchByName).forEach(w -> {
|
||||
searchService.searchByName(w, 0, Integer.MAX_VALUE, allMusicFolders, Artist.class);
|
||||
});
|
||||
|
||||
// testGetRandomSongs()
|
||||
RandomSearchCriteria criteria = new RandomSearchCriteria(Integer.MAX_VALUE, // count
|
||||
null, // genre,
|
||||
null, // fromYear
|
||||
null, // toYear
|
||||
allMusicFolders // musicFolders
|
||||
);
|
||||
for (int i = 0; i < countForEachMethod; i++) {
|
||||
searchService.getRandomSongs(criteria);
|
||||
}
|
||||
|
||||
// testGetRandomAlbums()
|
||||
for (int i = 0; i < countForEachMethod; i++) {
|
||||
searchService.getRandomAlbums(Integer.MAX_VALUE, allMusicFolders);
|
||||
}
|
||||
|
||||
// testGetRandomAlbumsId3()
|
||||
for (int i = 0; i < countForEachMethod; i++) {
|
||||
searchService.getRandomAlbumsId3(Integer.MAX_VALUE, allMusicFolders);
|
||||
}
|
||||
|
||||
globalTimerContext.stop();
|
||||
|
||||
/*
|
||||
* Whether or not IndexReader is exhausted.
|
||||
*/
|
||||
query = "Sarah Walker";
|
||||
searchCriteria.setQuery(query);
|
||||
result = searchService.search(searchCriteria, allMusicFolders, IndexType.ALBUM);
|
||||
Assert.assertEquals("(35) Can the normal case be implemented.", 0,
|
||||
result.getArtists().size());
|
||||
Assert.assertEquals("(36) Can the normal case be implemented.", 0,
|
||||
result.getAlbums().size());
|
||||
Assert.assertEquals("(37) Can the normal case be implemented.", 1,
|
||||
result.getMediaFiles().size());
|
||||
Assert.assertEquals("(38) Can the normal case be implemented.", MediaType.ALBUM,
|
||||
result.getMediaFiles().get(0).getMediaType());
|
||||
Assert.assertEquals("(39) Can the normal case be implemented.",
|
||||
"_ID3_ALBUMARTIST_ Sarah Walker/Nash Ensemble",
|
||||
result.getMediaFiles().get(0).getArtist());
|
||||
|
||||
System.out.println("--- SUCCESS ---");
|
||||
|
||||
ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics)
|
||||
.convertRatesTo(TimeUnit.SECONDS).convertDurationsTo(TimeUnit.MILLISECONDS).build();
|
||||
reporter.report();
|
||||
|
||||
System.out.println("End. ");
|
||||
}
|
||||
|
||||
private static String[] createRandomWords(int count) {
|
||||
String[] randomStrings = new String[count];
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < count; i++) {
|
||||
char[] word = new char[random.nextInt(8) + 3];
|
||||
for (int j = 0; j < word.length; j++) {
|
||||
word[j] = (char) ('a' + random.nextInt(26));
|
||||
}
|
||||
randomStrings[i] = new String(word);
|
||||
}
|
||||
return randomStrings;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user