/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.mahout.cf.taste.impl.model.file;
import org.apache.commons.lang.mutable.MutableBoolean;
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.TasteTestCase;
import org.apache.mahout.cf.taste.impl.common.LongPrimitiveIterator;
import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood;
import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;
import org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.model.Preference;
import org.apache.mahout.cf.taste.model.PreferenceArray;
import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;
import org.apache.mahout.cf.taste.recommender.Recommender;
import org.apache.mahout.cf.taste.similarity.UserSimilarity;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import java.util.NoSuchElementException;
/** <p>Tests {@link FileDataModel}.</p> */
public final class FileDataModelTest extends TasteTestCase {
private static final String[] DATA = {
"123,456,0.1",
"123,789,0.6",
"123,654,0.7",
"234,123,0.5",
"234,234,1.0",
"234,999,0.9",
"345,789,0.6",
"345,654,0.7",
"345,123,1.0",
"345,234,0.5",
"345,999,0.5",
"456,456,0.1",
"456,789,0.5",
"456,654,0.0",
"456,999,0.2",};
private DataModel model;
private File testFile;
@Override
@Before
public void setUp() throws Exception {
super.setUp();
testFile = getTestTempFile("test.txt");
writeLines(testFile, DATA);
model = new FileDataModel(testFile);
}
@Test
public void testFile() throws Exception {
UserSimilarity userSimilarity = new PearsonCorrelationSimilarity(model);
UserNeighborhood neighborhood = new NearestNUserNeighborhood(3, userSimilarity, model);
Recommender recommender = new GenericUserBasedRecommender(model, neighborhood, userSimilarity);
assertEquals(1, recommender.recommend(123, 3).size());
assertEquals(0, recommender.recommend(234, 3).size());
assertEquals(1, recommender.recommend(345, 3).size());
// Make sure this doesn't throw an exception
model.refresh(null);
}
@Test
public void testTranspose() throws Exception {
FileDataModel tModel = new FileDataModel(testFile, true, FileDataModel.DEFAULT_MIN_RELOAD_INTERVAL_MS);
PreferenceArray userPrefs = tModel.getPreferencesFromUser(456);
assertNotNull("user prefs are null and it shouldn't be", userPrefs);
PreferenceArray pref = tModel.getPreferencesForItem(123);
assertNotNull("pref is null and it shouldn't be", pref);
assertEquals("pref Size: " + pref.length() + " is not: " + 3, 3, pref.length());
}
@Test(expected = NoSuchElementException.class)
public void testGetItems() throws Exception {
LongPrimitiveIterator it = model.getItemIDs();
assertNotNull(it);
assertTrue(it.hasNext());
assertEquals(123, it.nextLong());
assertTrue(it.hasNext());
assertEquals(234, it.nextLong());
assertTrue(it.hasNext());
assertEquals(456, it.nextLong());
assertTrue(it.hasNext());
assertEquals(654, it.nextLong());
assertTrue(it.hasNext());
assertEquals(789, it.nextLong());
assertTrue(it.hasNext());
assertEquals(999, it.nextLong());
assertFalse(it.hasNext());
it.next();
}
@Test
public void testPreferencesForItem() throws Exception {
PreferenceArray prefs = model.getPreferencesForItem(456);
assertNotNull(prefs);
Preference pref1 = prefs.get(0);
assertEquals(123, pref1.getUserID());
assertEquals(456, pref1.getItemID());
Preference pref2 = prefs.get(1);
assertEquals(456, pref2.getUserID());
assertEquals(456, pref2.getItemID());
assertEquals(2, prefs.length());
}
@Test
public void testGetNumUsers() throws Exception {
assertEquals(4, model.getNumUsers());
}
@Test
public void testNumUsersPreferring() throws Exception {
assertEquals(2, model.getNumUsersWithPreferenceFor(456));
assertEquals(0, model.getNumUsersWithPreferenceFor(111));
assertEquals(0, model.getNumUsersWithPreferenceFor(111, 456));
assertEquals(2, model.getNumUsersWithPreferenceFor(123, 234));
}
@Test
public void testRefresh() throws Exception {
final MutableBoolean initialized = new MutableBoolean(false);
Runnable initializer = new Runnable() {
@Override
public void run() {
try {
model.getNumUsers();
initialized.setValue(true);
} catch (TasteException te) {
// oops
}
}
};
new Thread(initializer).start();
Thread.sleep(1000L); // wait a second for thread to start and call getNumUsers()
model.getNumUsers(); // should block
assertTrue(initialized.booleanValue());
assertEquals(4, model.getNumUsers());
}
@Test
public void testExplicitRefreshAfterCompleteFileUpdate() throws Exception {
File file = getTestTempFile("refresh");
writeLines(file, "123,456,3.0");
/* create a FileDataModel that always reloads when the underlying file has changed */
FileDataModel dataModel = new FileDataModel(file, false, 0L);
assertEquals(3.0f, dataModel.getPreferenceValue(123L, 456L), EPSILON);
/* change the underlying file,
* we have to wait at least a second to see the change in the file's lastModified timestamp */
Thread.sleep(2000L);
writeLines(file, "123,456,5.0");
dataModel.refresh(null);
assertEquals(5.0f, dataModel.getPreferenceValue(123L, 456L), EPSILON);
}
@Test
public void testToString() {
assertFalse(model.toString().isEmpty());
}
@Test(expected = IllegalArgumentException.class)
public void testEmptyFile() throws Exception {
File file = getTestTempFile("empty");
writeLines(file); //required to create file.
new FileDataModel(file);
}
}