You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mahout.apache.org by ro...@apache.org on 2010/02/13 21:54:31 UTC
svn commit: r909912 [5/10] - in
/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste: common/
eval/ hadoop/ hadoop/cooccurence/ hadoop/item/ hadoop/pseudo/
hadoop/slopeone/ impl/common/ impl/common/jdbc/ impl/eval/ impl/model/
impl/model/...
Modified: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousUserDataModel.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousUserDataModel.java?rev=909912&r1=909911&r2=909912&view=diff
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousUserDataModel.java (original)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousUserDataModel.java Sat Feb 13 20:54:05 2010
@@ -17,6 +17,8 @@
package org.apache.mahout.cf.taste.impl.model;
+import java.util.Collection;
+
import org.apache.mahout.cf.taste.common.Refreshable;
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.common.FastIDSet;
@@ -24,33 +26,40 @@
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.model.PreferenceArray;
-import java.util.Collection;
-
/**
- * <p>This {@link DataModel} decorator class is useful in a situation where you wish to recommend
- * to a user that doesn't really exist yet in your actual {@link DataModel}. For example maybe you
- * wish to recommend DVDs to a user who has browsed a few titles on your DVD store site, but, the
- * user is not yet registered.</p>
- *
- * <p>This enables you to temporarily add a temporary user to an existing {@link DataModel} in a way
- * that recommenders can then produce recommendations anyway. To do so, wrap your real implementation
- * in this class:</p>
- *
- * <p><pre>
+ * <p>
+ * This {@link DataModel} decorator class is useful in a situation where you wish to recommend to a user that
+ * doesn't really exist yet in your actual {@link DataModel}. For example maybe you wish to recommend DVDs to
+ * a user who has browsed a few titles on your DVD store site, but, the user is not yet registered.
+ * </p>
+ *
+ * <p>
+ * This enables you to temporarily add a temporary user to an existing {@link DataModel} in a way that
+ * recommenders can then produce recommendations anyway. To do so, wrap your real implementation in this
+ * class:
+ * </p>
+ *
+ * <p>
+ *
+ * <pre>
* DataModel realModel = ...;
* DataModel plusModel = new PlusAnonymousUserDataModel(realModel);
* ...
* ItemSimilarity similarity = new LogLikelihoodSimilarity(realModel); // not plusModel
- * </pre></p>
- *
- * <p>But, continue to use <code>realModel</code> as input to other components.
- * To recommend, first construct and
- * set the temporary user information on the model and then simply call the recommender.
- * The <code>synchronized</code> block exists to remind you that this is of course not
- * thread-safe. Only one set of temp data can be inserted into the model and used at one
- * time.</p>
- *
- * <p><pre>
+ * </pre>
+ *
+ * </p>
+ *
+ * <p>
+ * But, continue to use <code>realModel</code> as input to other components. To recommend, first construct and
+ * set the temporary user information on the model and then simply call the recommender. The
+ * <code>synchronized</code> block exists to remind you that this is of course not thread-safe. Only one set
+ * of temp data can be inserted into the model and used at one time.
+ * </p>
+ *
+ * <p>
+ *
+ * <pre>
* Recommender recommender = ...;
* ...
* synchronized(...) {
@@ -58,21 +67,23 @@
* plusModel.setTempPrefs(tempPrefs);
* recommender.recommend(PlusAnonymousUserDataModel.TEMP_USER_ID, 10);
* }
- * </pre></p>
+ * </pre>
+ *
+ * </p>
*/
public final class PlusAnonymousUserDataModel implements DataModel {
-
+
public static final long TEMP_USER_ID = Long.MIN_VALUE;
-
+
private final DataModel delegate;
private PreferenceArray tempPrefs;
private final FastIDSet prefItemIDs;
-
+
public PlusAnonymousUserDataModel(DataModel delegate) {
this.delegate = delegate;
this.prefItemIDs = new FastIDSet();
}
-
+
public void setTempPrefs(PreferenceArray prefs) {
this.tempPrefs = prefs;
this.prefItemIDs.clear();
@@ -80,36 +91,37 @@
this.prefItemIDs.add(prefs.getItemID(i));
}
}
-
+
@Override
public LongPrimitiveIterator getUserIDs() throws TasteException {
- return new PlusAnonymousUserLongPrimitiveIterator(delegate.getUserIDs(), TEMP_USER_ID);
+ return new PlusAnonymousUserLongPrimitiveIterator(delegate.getUserIDs(),
+ PlusAnonymousUserDataModel.TEMP_USER_ID);
}
-
+
@Override
public PreferenceArray getPreferencesFromUser(long userID) throws TasteException {
- if (userID == TEMP_USER_ID) {
+ if (userID == PlusAnonymousUserDataModel.TEMP_USER_ID) {
return tempPrefs;
} else {
return delegate.getPreferencesFromUser(userID);
}
}
-
+
@Override
public FastIDSet getItemIDsFromUser(long userID) throws TasteException {
- if (userID == TEMP_USER_ID) {
+ if (userID == PlusAnonymousUserDataModel.TEMP_USER_ID) {
return prefItemIDs;
} else {
return delegate.getItemIDsFromUser(userID);
}
}
-
+
@Override
public LongPrimitiveIterator getItemIDs() throws TasteException {
return delegate.getItemIDs();
// Yeah ignoring items that only the plus-one user knows about... can't really happen
}
-
+
@Override
public PreferenceArray getPreferencesForItem(long itemID) throws TasteException {
PreferenceArray delegatePrefs = delegate.getPreferencesForItem(itemID);
@@ -131,10 +143,10 @@
}
return delegatePrefs;
}
-
+
@Override
public Float getPreferenceValue(long userID, long itemID) throws TasteException {
- if (userID == TEMP_USER_ID) {
+ if (userID == PlusAnonymousUserDataModel.TEMP_USER_ID) {
for (int i = 0; i < tempPrefs.length(); i++) {
if (tempPrefs.getItemID(i) == itemID) {
return tempPrefs.getValue(i);
@@ -145,17 +157,17 @@
return delegate.getPreferenceValue(userID, itemID);
}
}
-
+
@Override
public int getNumItems() throws TasteException {
return delegate.getNumItems();
}
-
+
@Override
public int getNumUsers() throws TasteException {
return delegate.getNumUsers() + 1;
}
-
+
@Override
public int getNumUsersWithPreferenceFor(long... itemIDs) throws TasteException {
boolean hasAll = true;
@@ -174,29 +186,28 @@
}
return delegate.getNumUsersWithPreferenceFor(itemIDs) + (hasAll ? 1 : 0);
}
-
+
@Override
public void setPreference(long userID, long itemID, float value) throws TasteException {
- if (userID == TEMP_USER_ID) {
+ if (userID == PlusAnonymousUserDataModel.TEMP_USER_ID) {
throw new UnsupportedOperationException();
} else {
delegate.setPreference(userID, itemID, value);
}
}
-
+
@Override
public void removePreference(long userID, long itemID) throws TasteException {
- if (userID == TEMP_USER_ID) {
+ if (userID == PlusAnonymousUserDataModel.TEMP_USER_ID) {
throw new UnsupportedOperationException();
} else {
delegate.removePreference(userID, itemID);
}
}
-
+
@Override
public void refresh(Collection<Refreshable> alreadyRefreshed) {
delegate.refresh(alreadyRefreshed);
}
-
+
}
-
Modified: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousUserLongPrimitiveIterator.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousUserLongPrimitiveIterator.java?rev=909912&r1=909911&r2=909912&view=diff
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousUserLongPrimitiveIterator.java (original)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousUserLongPrimitiveIterator.java Sat Feb 13 20:54:05 2010
@@ -17,21 +17,21 @@
package org.apache.mahout.cf.taste.impl.model;
-import org.apache.mahout.cf.taste.impl.common.LongPrimitiveIterator;
import org.apache.mahout.cf.taste.impl.common.AbstractLongPrimitiveIterator;
+import org.apache.mahout.cf.taste.impl.common.LongPrimitiveIterator;
final class PlusAnonymousUserLongPrimitiveIterator extends AbstractLongPrimitiveIterator {
-
+
private final LongPrimitiveIterator delegate;
private final long extraDatum;
private boolean datumConsumed;
-
+
PlusAnonymousUserLongPrimitiveIterator(LongPrimitiveIterator delegate, long extraDatum) {
this.delegate = delegate;
this.extraDatum = extraDatum;
datumConsumed = false;
}
-
+
@Override
public long nextLong() {
if (datumConsumed) {
@@ -51,7 +51,7 @@
}
}
}
-
+
@Override
public long peek() {
if (datumConsumed) {
@@ -69,22 +69,22 @@
}
}
}
-
+
@Override
public boolean hasNext() {
return !datumConsumed || delegate.hasNext();
}
-
+
@Override
public void remove() {
throw new UnsupportedOperationException();
}
-
+
@Override
public void skip(int n) {
for (int i = 0; i < n; i++) {
nextLong();
}
}
-
+
}
Modified: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/file/FileDataModel.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/file/FileDataModel.java?rev=909912&r1=909911&r2=909912&view=diff
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/file/FileDataModel.java (original)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/file/FileDataModel.java Sat Feb 13 20:54:05 2010
@@ -17,77 +17,97 @@
package org.apache.mahout.cf.taste.impl.model.file;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReentrantLock;
+
import org.apache.mahout.cf.taste.common.Refreshable;
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.common.FastByIDMap;
import org.apache.mahout.cf.taste.impl.common.FastIDSet;
-import org.apache.mahout.cf.taste.impl.model.GenericUserPreferenceArray;
-import org.apache.mahout.common.FileLineIterator;
import org.apache.mahout.cf.taste.impl.common.LongPrimitiveIterator;
import org.apache.mahout.cf.taste.impl.model.GenericBooleanPrefDataModel;
import org.apache.mahout.cf.taste.impl.model.GenericDataModel;
import org.apache.mahout.cf.taste.impl.model.GenericPreference;
+import org.apache.mahout.cf.taste.impl.model.GenericUserPreferenceArray;
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.common.FileLineIterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.locks.ReentrantLock;
-
/**
- * <p>A {@link DataModel} backed by a comma-delimited file. This class typically expects a file where each line contains
- * a user ID, followed by item ID, followed by preferences value, separated by commas. You may also use tabs.</p>
- *
- * <p>The preference value is assumed to be parseable as a <code>double</code>. The user IDs and item IDs are read
- * parsed as <code>long</code>s.</p>
- *
- * <p>This class will reload data from the data file when {@link #refresh(Collection)} is called, unless the file has
- * been reloaded very recently already.</p>
- *
- * <p>This class will also look for update "delta" files in the same directory, with file names that start the same way
- * (up to the first period). These files should have the same format, and provide updated data that supersedes what is
- * in the main data file. This is a mechanism that allows an application to push updates to {@link FileDataModel}
- * without re-copying the entire data file.</p>
- *
- * <p>The line may contain a blank preference value (e.g. "123,456,"). This is interpreted to mean "delete preference",
- * and is only useful in the context of an update delta file (see above). Note that if the line is empty or begins with
- * '#' it will be ignored as a comment.</p>
- *
- * <p>It is also acceptable for the lines to contain additional fields. Fields beyond the third will be ignored.</p>
- *
- * <p>Finally, for application that have no notion of a preference value (that is, the user simply expresses a
- * preference for an item, but no degree of preference), the caller can simply omit the third token in each line
- * altogether -- for example, "123,456".</p>
- *
- * <p>Note that it's all-or-nothing -- all of the items in the file must express no preference, or the all must.
- * These cannot be mixed. Put another way there will always be the same number of delimiters on every line of the
- * file!</p>
- *
- * <p>This class is not intended for use with very large amounts of data (over, say, tens of millions of rows). For
- * that, a JDBC-backed {@link DataModel} and a database are more appropriate.</p>
- *
- * <p>It is possible and likely useful to subclass this class and customize its behavior to accommodate
+ * <p>
+ * A {@link DataModel} backed by a comma-delimited file. This class typically expects a file where each line
+ * contains a user ID, followed by item ID, followed by preferences value, separated by commas. You may also
+ * use tabs.
+ * </p>
+ *
+ * <p>
+ * The preference value is assumed to be parseable as a <code>double</code>. The user IDs and item IDs are
+ * read parsed as <code>long</code>s.
+ * </p>
+ *
+ * <p>
+ * This class will reload data from the data file when {@link #refresh(Collection)} is called, unless the file
+ * has been reloaded very recently already.
+ * </p>
+ *
+ * <p>
+ * This class will also look for update "delta" files in the same directory, with file names that start the
+ * same way (up to the first period). These files should have the same format, and provide updated data that
+ * supersedes what is in the main data file. This is a mechanism that allows an application to push updates to
+ * {@link FileDataModel} without re-copying the entire data file.
+ * </p>
+ *
+ * <p>
+ * The line may contain a blank preference value (e.g. "123,456,"). This is interpreted to mean
+ * "delete preference", and is only useful in the context of an update delta file (see above). Note that if
+ * the line is empty or begins with '#' it will be ignored as a comment.
+ * </p>
+ *
+ * <p>
+ * It is also acceptable for the lines to contain additional fields. Fields beyond the third will be ignored.
+ * </p>
+ *
+ * <p>
+ * Finally, for application that have no notion of a preference value (that is, the user simply expresses a
+ * preference for an item, but no degree of preference), the caller can simply omit the third token in each
+ * line altogether -- for example, "123,456".
+ * </p>
+ *
+ * <p>
+ * Note that it's all-or-nothing -- all of the items in the file must express no preference, or the all must.
+ * These cannot be mixed. Put another way there will always be the same number of delimiters on every line of
+ * the file!
+ * </p>
+ *
+ * <p>
+ * This class is not intended for use with very large amounts of data (over, say, tens of millions of rows).
+ * For that, a JDBC-backed {@link DataModel} and a database are more appropriate.
+ * </p>
+ *
+ * <p>
+ * It is possible and likely useful to subclass this class and customize its behavior to accommodate
* application-specific needs and input formats. See {@link #processLine(String, FastByIDMap)} and
* {@link #processLineWithoutID(String, FastByIDMap)}
*/
public class FileDataModel implements DataModel {
-
+
private static final Logger log = LoggerFactory.getLogger(FileDataModel.class);
-
+
private static final long MIN_RELOAD_INTERVAL_MS = 60 * 1000L; // 1 minute?
private static final char COMMENT_CHAR = '#';
-
+
private final File dataFile;
private long lastModified;
private long lastUpdateFileModified;
@@ -97,19 +117,23 @@
private DataModel delegate;
private final ReentrantLock reloadLock;
private final boolean transpose;
-
+
/**
- * @param dataFile file containing preferences data. If file is compressed (and name ends in .gz or .zip accordingly)
- * it will be decompressed as it is read)
- * @throws FileNotFoundException if dataFile does not exist
- * @throws IOException if file can't be read
+ * @param dataFile
+ * file containing preferences data. If file is compressed (and name ends in .gz or .zip
+ * accordingly) it will be decompressed as it is read)
+ * @throws FileNotFoundException
+ * if dataFile does not exist
+ * @throws IOException
+ * if file can't be read
*/
public FileDataModel(File dataFile) throws IOException {
this(dataFile, false);
}
-
+
/**
- * @param transpose transposes user IDs and item IDs -- convenient for 'flipping' the data model this way
+ * @param transpose
+ * transposes user IDs and item IDs -- convenient for 'flipping' the data model this way
* @see #FileDataModel(File)
*/
public FileDataModel(File dataFile, boolean transpose) throws IOException {
@@ -119,35 +143,35 @@
if (!dataFile.exists() || dataFile.isDirectory()) {
throw new FileNotFoundException(dataFile.toString());
}
-
- log.info("Creating FileDataModel for file {}", dataFile);
-
+
+ FileDataModel.log.info("Creating FileDataModel for file {}", dataFile);
+
this.dataFile = dataFile.getAbsoluteFile();
this.lastModified = dataFile.lastModified();
this.lastUpdateFileModified = readLastUpdateFileModified();
-
+
FileLineIterator iterator = new FileLineIterator(dataFile, false);
String firstLine = iterator.peek();
- while (firstLine.length() == 0 || firstLine.charAt(0) == COMMENT_CHAR) {
+ while ((firstLine.length() == 0) || (firstLine.charAt(0) == FileDataModel.COMMENT_CHAR)) {
iterator.next();
firstLine = iterator.peek();
}
iterator.close();
- delimiter = determineDelimiter(firstLine, 2);
+ delimiter = FileDataModel.determineDelimiter(firstLine, 2);
hasPrefValues = firstLine.indexOf(delimiter, firstLine.indexOf(delimiter) + 1) >= 0;
-
+
this.reloadLock = new ReentrantLock();
this.transpose = transpose;
}
-
+
public File getDataFile() {
return dataFile;
}
-
+
public char getDelimiter() {
return delimiter;
}
-
+
protected void reload() {
if (!reloadLock.isLocked()) {
reloadLock.lock();
@@ -155,82 +179,84 @@
delegate = buildModel();
loaded = true;
} catch (IOException ioe) {
- log.warn("Exception while reloading", ioe);
+ FileDataModel.log.warn("Exception while reloading", ioe);
} finally {
reloadLock.unlock();
}
}
}
-
+
protected DataModel buildModel() throws IOException {
-
+
long newLastModified = dataFile.lastModified();
long newLastUpdateFileModified = readLastUpdateFileModified();
-
- boolean loadFreshData = delegate == null || newLastModified > lastModified + MIN_RELOAD_INTERVAL_MS;
-
+
+ boolean loadFreshData = (delegate == null)
+ || (newLastModified > lastModified + FileDataModel.MIN_RELOAD_INTERVAL_MS);
+
lastModified = newLastModified;
lastUpdateFileModified = newLastUpdateFileModified;
-
+
if (hasPrefValues) {
if (loadFreshData) {
-
+
FastByIDMap<Collection<Preference>> data = new FastByIDMap<Collection<Preference>>();
FileLineIterator iterator = new FileLineIterator(dataFile, false);
processFile(iterator, data);
-
+
for (File updateFile : findUpdateFiles()) {
processFile(new FileLineIterator(updateFile, false), data);
}
-
+
return new GenericDataModel(GenericDataModel.toDataMap(data, true));
-
+
} else {
-
+
FastByIDMap<PreferenceArray> rawData = ((GenericDataModel) delegate).getRawUserData();
-
+
for (File updateFile : findUpdateFiles()) {
processFile(new FileLineIterator(updateFile, false), rawData);
}
-
+
return new GenericDataModel(rawData);
-
+
}
-
+
} else {
-
+
if (loadFreshData) {
-
+
FastByIDMap<FastIDSet> data = new FastByIDMap<FastIDSet>();
FileLineIterator iterator = new FileLineIterator(dataFile, false);
processFileWithoutID(iterator, data);
-
+
for (File updateFile : findUpdateFiles()) {
processFileWithoutID(new FileLineIterator(updateFile, false), data);
}
-
+
return new GenericBooleanPrefDataModel(data);
-
+
} else {
-
+
FastByIDMap<FastIDSet> rawData = ((GenericBooleanPrefDataModel) delegate).getRawUserData();
-
+
for (File updateFile : findUpdateFiles()) {
processFileWithoutID(new FileLineIterator(updateFile, false), rawData);
}
-
+
return new GenericBooleanPrefDataModel(rawData);
-
+
}
-
+
}
}
-
+
/**
- * Finds update delta files in the same directory as the data file. This finds any file whose name starts the same way
- * as the data file (up to first period) but isn't the data file itself. For example, if the data file is
- * /foo/data.txt.gz, you might place update files at /foo/data.1.txt.gz, /foo/data.2.txt.gz, etc.
+ * Finds update delta files in the same directory as the data file. This finds any file whose name starts
+ * the same way as the data file (up to first period) but isn't the data file itself. For example, if the
+ * data file is /foo/data.txt.gz, you might place update files at /foo/data.1.txt.gz, /foo/data.2.txt.gz,
+ * etc.
*/
private Iterable<File> findUpdateFiles() {
String dataFileName = dataFile.getName();
@@ -247,7 +273,7 @@
Collections.sort(updateFiles);
return updateFiles;
}
-
+
private long readLastUpdateFileModified() {
long mostRecentModification = Long.MIN_VALUE;
for (File updateFile : findUpdateFiles()) {
@@ -255,7 +281,7 @@
}
return mostRecentModification;
}
-
+
public static char determineDelimiter(String line, int maxDelimiters) {
char delimiter;
if (line.indexOf(',') >= 0) {
@@ -281,10 +307,9 @@
}
return delimiter;
}
-
- protected void processFile(FileLineIterator dataOrUpdateFileIterator,
- FastByIDMap<?> data) {
- log.info("Reading file info...");
+
+ protected void processFile(FileLineIterator dataOrUpdateFileIterator, FastByIDMap<?> data) {
+ FileDataModel.log.info("Reading file info...");
AtomicInteger count = new AtomicInteger();
while (dataOrUpdateFileIterator.hasNext()) {
String line = dataOrUpdateFileIterator.next();
@@ -292,41 +317,48 @@
processLine(line, data);
int currentCount = count.incrementAndGet();
if (currentCount % 1000000 == 0) {
- log.info("Processed {} lines", currentCount);
+ FileDataModel.log.info("Processed {} lines", currentCount);
}
}
}
- log.info("Read lines: {}", count.get());
+ FileDataModel.log.info("Read lines: {}", count.get());
}
-
+
/**
- * <p>Reads one line from the input file and adds the data to a {@link Map} data structure which maps user IDs to
- * preferences. This assumes that each line of the input file corresponds to one preference. After reading a line and
- * determining which user and item the preference pertains to, the method should look to see if the data contains a
- * mapping for the user ID already, and if not, add an empty {@link List} of {@link Preference}s to the data.</p>
- *
- * <p>Note that if the line is empty or begins with '#' it will be ignored as a comment.</p>
- *
- * @param line line from input data file
- * @param data all data read so far, as a mapping from user IDs to preferences
+ * <p>
+ * Reads one line from the input file and adds the data to a {@link Map} data structure which maps user IDs
+ * to preferences. This assumes that each line of the input file corresponds to one preference. After
+ * reading a line and determining which user and item the preference pertains to, the method should look to
+ * see if the data contains a mapping for the user ID already, and if not, add an empty {@link List} of
+ * {@link Preference}s to the data.
+ * </p>
+ *
+ * <p>
+ * Note that if the line is empty or begins with '#' it will be ignored as a comment.
+ * </p>
+ *
+ * @param line
+ * line from input data file
+ * @param data
+ * all data read so far, as a mapping from user IDs to preferences
*/
protected void processLine(String line, FastByIDMap<?> data) {
-
- if (line.length() == 0 || line.charAt(0) == COMMENT_CHAR) {
+
+ if ((line.length() == 0) || (line.charAt(0) == FileDataModel.COMMENT_CHAR)) {
return;
}
-
- int delimiterOne = line.indexOf((int) delimiter);
+
+ int delimiterOne = line.indexOf(delimiter);
if (delimiterOne < 0) {
throw new IllegalArgumentException("Bad line: " + line);
}
- int delimiterTwo = line.indexOf((int) delimiter, delimiterOne + 1);
+ int delimiterTwo = line.indexOf(delimiter, delimiterOne + 1);
if (delimiterTwo < 0) {
throw new IllegalArgumentException("Bad line: " + line);
}
// Look for beginning of additional, ignored fields:
- int delimiterThree = line.indexOf((int) delimiter, delimiterTwo + 1);
-
+ int delimiterThree = line.indexOf(delimiter, delimiterTwo + 1);
+
String userIDString = line.substring(0, delimiterOne);
String itemIDString = line.substring(delimiterOne + 1, delimiterTwo);
String preferenceValueString;
@@ -335,20 +367,20 @@
} else {
preferenceValueString = line.substring(delimiterTwo + 1);
}
-
+
long userID = readUserIDFromString(userIDString);
long itemID = readItemIDFromString(itemIDString);
-
+
if (transpose) {
long tmp = userID;
userID = itemID;
itemID = tmp;
}
-
+
// This is kind of gross but need to handle two types of storage
Object maybePrefs = data.get(userID);
if (maybePrefs instanceof PreferenceArray) {
-
+
PreferenceArray prefs = (PreferenceArray) maybePrefs;
if (preferenceValueString.length() == 0) {
if (prefs != null) {
@@ -375,11 +407,11 @@
}
}
}
-
+
} else {
-
+
float preferenceValue = Float.parseFloat(preferenceValueString);
-
+
boolean exists = false;
if (prefs != null) {
for (int i = 0; i < prefs.length(); i++) {
@@ -390,7 +422,7 @@
}
}
}
-
+
if (!exists) {
if (prefs == null) {
prefs = new GenericUserPreferenceArray(1);
@@ -406,11 +438,11 @@
prefs.setValue(0, preferenceValue);
}
}
-
+
} else {
-
+
Collection<Preference> prefs = (Collection<Preference>) maybePrefs;
-
+
if (preferenceValueString.length() == 0) {
if (prefs != null) {
// remove pref
@@ -424,9 +456,9 @@
}
}
} else {
-
+
float preferenceValue = Float.parseFloat(preferenceValueString);
-
+
boolean exists = false;
if (prefs != null) {
for (Preference pref : prefs) {
@@ -437,7 +469,7 @@
}
}
}
-
+
if (!exists) {
if (prefs == null) {
prefs = new ArrayList<Preference>(2);
@@ -446,13 +478,12 @@
prefs.add(new GenericPreference(userID, itemID, preferenceValue));
}
}
-
+
}
}
-
- protected void processFileWithoutID(FileLineIterator dataOrUpdateFileIterator,
- FastByIDMap<FastIDSet> data) {
- log.info("Reading file info...");
+
+ protected void processFileWithoutID(FileLineIterator dataOrUpdateFileIterator, FastByIDMap<FastIDSet> data) {
+ FileDataModel.log.info("Reading file info...");
AtomicInteger count = new AtomicInteger();
while (dataOrUpdateFileIterator.hasNext()) {
String line = dataOrUpdateFileIterator.next();
@@ -460,27 +491,27 @@
processLineWithoutID(line, data);
int currentCount = count.incrementAndGet();
if (currentCount % 100000 == 0) {
- log.info("Processed {} lines", currentCount);
+ FileDataModel.log.info("Processed {} lines", currentCount);
}
}
}
- log.info("Read lines: {}", count.get());
+ FileDataModel.log.info("Read lines: {}", count.get());
}
-
+
protected void processLineWithoutID(String line, FastByIDMap<FastIDSet> data) {
-
- if (line.length() == 0 || line.charAt(0) == COMMENT_CHAR) {
+
+ if ((line.length() == 0) || (line.charAt(0) == FileDataModel.COMMENT_CHAR)) {
return;
}
-
- int delimiterOne = line.indexOf((int) delimiter);
+
+ int delimiterOne = line.indexOf(delimiter);
if (delimiterOne < 0) {
throw new IllegalArgumentException("Bad line: " + line);
}
-
+
long userID = readUserIDFromString(line.substring(0, delimiterOne));
long itemID = readItemIDFromString(line.substring(delimiterOne + 1));
-
+
if (transpose) {
long tmp = userID;
userID = itemID;
@@ -493,114 +524,115 @@
}
itemIDs.add(itemID);
}
-
+
private void checkLoaded() {
if (!loaded) {
reload();
}
}
-
+
/**
- * Subclasses may wish to override this if ID values in the file are not numeric. This
- * provides a hook by which subclasses can inject an
- * {@link org.apache.mahout.cf.taste.model.IDMigrator} to perform translation.
+ * Subclasses may wish to override this if ID values in the file are not numeric. This provides a hook by
+ * which subclasses can inject an {@link org.apache.mahout.cf.taste.model.IDMigrator} to perform
+ * translation.
*/
protected long readUserIDFromString(String value) {
return Long.parseLong(value);
}
-
+
/**
- * Subclasses may wish to override this if ID values in the file are not numeric. This
- * provides a hook by which subclasses can inject an
- * {@link org.apache.mahout.cf.taste.model.IDMigrator} to perform translation.
+ * Subclasses may wish to override this if ID values in the file are not numeric. This provides a hook by
+ * which subclasses can inject an {@link org.apache.mahout.cf.taste.model.IDMigrator} to perform
+ * translation.
*/
protected long readItemIDFromString(String value) {
return Long.parseLong(value);
}
-
+
@Override
public LongPrimitiveIterator getUserIDs() throws TasteException {
checkLoaded();
return delegate.getUserIDs();
}
-
+
@Override
public PreferenceArray getPreferencesFromUser(long userID) throws TasteException {
checkLoaded();
return delegate.getPreferencesFromUser(userID);
}
-
+
@Override
public FastIDSet getItemIDsFromUser(long userID) throws TasteException {
- checkLoaded();
+ checkLoaded();
return delegate.getItemIDsFromUser(userID);
}
-
+
@Override
public LongPrimitiveIterator getItemIDs() throws TasteException {
checkLoaded();
return delegate.getItemIDs();
}
-
+
@Override
public PreferenceArray getPreferencesForItem(long itemID) throws TasteException {
checkLoaded();
return delegate.getPreferencesForItem(itemID);
}
-
+
@Override
public Float getPreferenceValue(long userID, long itemID) throws TasteException {
return delegate.getPreferenceValue(userID, itemID);
}
-
+
@Override
public int getNumItems() throws TasteException {
checkLoaded();
return delegate.getNumItems();
}
-
+
@Override
public int getNumUsers() throws TasteException {
checkLoaded();
return delegate.getNumUsers();
}
-
+
@Override
public int getNumUsersWithPreferenceFor(long... itemIDs) throws TasteException {
checkLoaded();
return delegate.getNumUsersWithPreferenceFor(itemIDs);
}
-
+
/**
- * Note that this method only updates the in-memory preference data that this {@link FileDataModel} maintains; it does
- * not modify any data on disk. Therefore any updates from this method are only temporary, and lost when data is
- * reloaded from a file. This method should also be considered relatively slow.
+ * Note that this method only updates the in-memory preference data that this {@link FileDataModel}
+ * maintains; it does not modify any data on disk. Therefore any updates from this method are only
+ * temporary, and lost when data is reloaded from a file. This method should also be considered relatively
+ * slow.
*/
@Override
public void setPreference(long userID, long itemID, float value) throws TasteException {
checkLoaded();
delegate.setPreference(userID, itemID, value);
}
-
+
/** See the warning at {@link #setPreference(long, long, float)}. */
@Override
public void removePreference(long userID, long itemID) throws TasteException {
checkLoaded();
delegate.removePreference(userID, itemID);
}
-
+
@Override
public void refresh(Collection<Refreshable> alreadyRefreshed) {
- if (dataFile.lastModified() > lastModified + MIN_RELOAD_INTERVAL_MS ||
- readLastUpdateFileModified() > lastUpdateFileModified + MIN_RELOAD_INTERVAL_MS) {
- log.debug("File has changed; reloading...");
+ if ((dataFile.lastModified() > lastModified + FileDataModel.MIN_RELOAD_INTERVAL_MS)
+ || (readLastUpdateFileModified() > lastUpdateFileModified + FileDataModel.MIN_RELOAD_INTERVAL_MS)) {
+ FileDataModel.log.debug("File has changed; reloading...");
reload();
}
}
-
+
@Override
public String toString() {
return "FileDataModel[dataFile:" + dataFile + ']';
}
-
+
}
Modified: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/AbstractBooleanPrefJDBCDataModel.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/AbstractBooleanPrefJDBCDataModel.java?rev=909912&r1=909911&r2=909912&view=diff
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/AbstractBooleanPrefJDBCDataModel.java (original)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/AbstractBooleanPrefJDBCDataModel.java Sat Feb 13 20:54:05 2010
@@ -17,25 +17,26 @@
package org.apache.mahout.cf.taste.impl.model.jdbc;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import javax.sql.DataSource;
+
import org.apache.mahout.cf.taste.common.TasteException;
-import org.apache.mahout.common.IOUtils;
import org.apache.mahout.cf.taste.impl.model.BooleanPreference;
import org.apache.mahout.cf.taste.model.Preference;
+import org.apache.mahout.common.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.sql.DataSource;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-
public abstract class AbstractBooleanPrefJDBCDataModel extends AbstractJDBCDataModel {
-
+
private static final Logger log = LoggerFactory.getLogger(AbstractBooleanPrefJDBCDataModel.class);
-
+
private final String setPreferenceSQL;
-
+
protected AbstractBooleanPrefJDBCDataModel(DataSource dataSource,
String preferenceTable,
String userIDColumn,
@@ -53,57 +54,43 @@
String getPrefsForItemSQL,
String getNumPreferenceForItemSQL,
String getNumPreferenceForItemsSQL) {
- super(dataSource,
- preferenceTable,
- userIDColumn,
- itemIDColumn,
- preferenceColumn,
- getPreferenceSQL,
- getUserSQL,
- getAllUsersSQL,
- getNumItemsSQL,
- getNumUsersSQL,
- setPreferenceSQL,
- removePreferenceSQL,
- getUsersSQL,
- getItemsSQL,
- getPrefsForItemSQL,
- getNumPreferenceForItemSQL,
- getNumPreferenceForItemsSQL);
+ super(dataSource, preferenceTable, userIDColumn, itemIDColumn, preferenceColumn, getPreferenceSQL,
+ getUserSQL, getAllUsersSQL, getNumItemsSQL, getNumUsersSQL, setPreferenceSQL, removePreferenceSQL,
+ getUsersSQL, getItemsSQL, getPrefsForItemSQL, getNumPreferenceForItemSQL, getNumPreferenceForItemsSQL);
this.setPreferenceSQL = setPreferenceSQL;
}
-
+
@Override
protected Preference buildPreference(ResultSet rs) throws SQLException {
return new BooleanPreference(getLongColumn(rs, 1), getLongColumn(rs, 2));
}
-
+
@Override
public void setPreference(long userID, long itemID, float value) throws TasteException {
if (!Float.isNaN(value)) {
throw new IllegalArgumentException("Invalid value: " + value);
}
-
- log.debug("Setting preference for user {}, item {}", userID, itemID);
-
+
+ AbstractBooleanPrefJDBCDataModel.log.debug("Setting preference for user {}, item {}", userID, itemID);
+
Connection conn = null;
PreparedStatement stmt = null;
-
+
try {
conn = getDataSource().getConnection();
stmt = conn.prepareStatement(setPreferenceSQL);
setLongParameter(stmt, 1, userID);
setLongParameter(stmt, 2, itemID);
-
- log.debug("Executing SQL update: {}", setPreferenceSQL);
+
+ AbstractBooleanPrefJDBCDataModel.log.debug("Executing SQL update: {}", setPreferenceSQL);
stmt.executeUpdate();
-
+
} catch (SQLException sqle) {
- log.warn("Exception while setting preference", sqle);
+ AbstractBooleanPrefJDBCDataModel.log.warn("Exception while setting preference", sqle);
throw new TasteException(sqle);
} finally {
IOUtils.quietClose(null, stmt, conn);
}
}
-
+
}
\ No newline at end of file
Modified: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/AbstractJDBCDataModel.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/AbstractJDBCDataModel.java?rev=909912&r1=909911&r2=909912&view=diff
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/AbstractJDBCDataModel.java (original)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/AbstractJDBCDataModel.java Sat Feb 13 20:54:05 2010
@@ -17,13 +17,24 @@
package org.apache.mahout.cf.taste.impl.model.jdbc;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import javax.sql.DataSource;
+
import org.apache.mahout.cf.taste.common.NoSuchUserException;
import org.apache.mahout.cf.taste.common.Refreshable;
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.common.Cache;
import org.apache.mahout.cf.taste.impl.common.FastByIDMap;
import org.apache.mahout.cf.taste.impl.common.FastIDSet;
-import org.apache.mahout.common.IOUtils;
import org.apache.mahout.cf.taste.impl.common.LongPrimitiveIterator;
import org.apache.mahout.cf.taste.impl.common.Retriever;
import org.apache.mahout.cf.taste.impl.common.jdbc.AbstractJDBCComponent;
@@ -34,42 +45,38 @@
import org.apache.mahout.cf.taste.model.JDBCDataModel;
import org.apache.mahout.cf.taste.model.Preference;
import org.apache.mahout.cf.taste.model.PreferenceArray;
+import org.apache.mahout.common.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.sql.DataSource;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.NoSuchElementException;
-
/**
- * <p>An abstract superclass for JDBC-related {@link DataModel} implementations, providing most of the common
- * functionality that any such implementation would need.</p>
- *
- * <p>Performance will be a concern with any JDBC-based {@link DataModel}. There are going to be lots of simultaneous
- * reads and some writes to one table. Make sure the table is set up optimally -- for example, you'll want to establish
- * indexes.</p>
- *
- * <p>You'll also want to use connection pooling of some kind. Most J2EE containers like Tomcat provide connection
- * pooling, so make sure the {@link DataSource} it exposes is using pooling. Outside a J2EE container, you can use
- * packages like Jakarta's <a href="http://jakarta.apache.org/commons/dbcp/">DBCP</a> to create a {@link DataSource} on
- * top of your database whose {@link Connection}s are pooled.</p>
+ * <p>
+ * An abstract superclass for JDBC-related {@link DataModel} implementations, providing most of the common
+ * functionality that any such implementation would need.
+ * </p>
+ *
+ * <p>
+ * Performance will be a concern with any JDBC-based {@link DataModel}. There are going to be lots of
+ * simultaneous reads and some writes to one table. Make sure the table is set up optimally -- for example,
+ * you'll want to establish indexes.
+ * </p>
+ *
+ * <p>
+ * You'll also want to use connection pooling of some kind. Most J2EE containers like Tomcat provide
+ * connection pooling, so make sure the {@link DataSource} it exposes is using pooling. Outside a J2EE
+ * container, you can use packages like Jakarta's <a href="http://jakarta.apache.org/commons/dbcp/">DBCP</a>
+ * to create a {@link DataSource} on top of your database whose {@link Connection}s are pooled.
+ * </p>
*/
public abstract class AbstractJDBCDataModel extends AbstractJDBCComponent implements JDBCDataModel {
-
+
private static final Logger log = LoggerFactory.getLogger(AbstractJDBCDataModel.class);
-
+
public static final String DEFAULT_PREFERENCE_TABLE = "taste_preferences";
public static final String DEFAULT_USER_ID_COLUMN = "user_id";
public static final String DEFAULT_ITEM_ID_COLUMN = "item_id";
public static final String DEFAULT_PREFERENCE_COLUMN = "preference";
-
+
private final DataSource dataSource;
private final String preferenceTable;
private final String userIDColumn;
@@ -89,8 +96,8 @@
private final String getNumPreferenceForItemsSQL;
private int cachedNumUsers;
private int cachedNumItems;
- private final Cache<Long, Integer> itemPrefCounts;
-
+ private final Cache<Long,Integer> itemPrefCounts;
+
protected AbstractJDBCDataModel(DataSource dataSource,
String getPreferenceSQL,
String getUserSQL,
@@ -104,25 +111,13 @@
String getPrefsForItemSQL,
String getNumPreferenceForItemSQL,
String getNumPreferenceForItemsSQL) {
- this(dataSource,
- DEFAULT_PREFERENCE_TABLE,
- DEFAULT_USER_ID_COLUMN,
- DEFAULT_ITEM_ID_COLUMN,
- DEFAULT_PREFERENCE_COLUMN,
- getPreferenceSQL,
- getUserSQL,
- getAllUsersSQL,
- getNumItemsSQL,
- getNumUsersSQL,
- setPreferenceSQL,
- removePreferenceSQL,
- getUsersSQL,
- getItemsSQL,
- getPrefsForItemSQL,
- getNumPreferenceForItemSQL,
- getNumPreferenceForItemsSQL);
+ this(dataSource, AbstractJDBCDataModel.DEFAULT_PREFERENCE_TABLE,
+ AbstractJDBCDataModel.DEFAULT_USER_ID_COLUMN, AbstractJDBCDataModel.DEFAULT_ITEM_ID_COLUMN,
+ AbstractJDBCDataModel.DEFAULT_PREFERENCE_COLUMN, getPreferenceSQL, getUserSQL, getAllUsersSQL,
+ getNumItemsSQL, getNumUsersSQL, setPreferenceSQL, removePreferenceSQL, getUsersSQL, getItemsSQL,
+ getPrefsForItemSQL, getNumPreferenceForItemSQL, getNumPreferenceForItemsSQL);
}
-
+
protected AbstractJDBCDataModel(DataSource dataSource,
String preferenceTable,
String userIDColumn,
@@ -140,38 +135,39 @@
String getPrefsForItemSQL,
String getNumPreferenceForItemSQL,
String getNumPreferenceForItemsSQL) {
-
- log.debug("Creating AbstractJDBCModel...");
-
- checkNotNullAndLog("preferenceTable", preferenceTable);
- checkNotNullAndLog("userIDColumn", userIDColumn);
- checkNotNullAndLog("itemIDColumn", itemIDColumn);
- checkNotNullAndLog("preferenceColumn", preferenceColumn);
-
- checkNotNullAndLog("dataSource", dataSource);
- checkNotNullAndLog("getUserSQL", getUserSQL);
- checkNotNullAndLog("getAllUsersSQL", getAllUsersSQL);
- checkNotNullAndLog("getPreferenceSQL", getPreferenceSQL);
- checkNotNullAndLog("getNumItemsSQL", getNumItemsSQL);
- checkNotNullAndLog("getNumUsersSQL", getNumUsersSQL);
- checkNotNullAndLog("setPreferenceSQL", setPreferenceSQL);
- checkNotNullAndLog("removePreferenceSQL", removePreferenceSQL);
- checkNotNullAndLog("getUsersSQL", getUsersSQL);
- checkNotNullAndLog("getItemsSQL", getItemsSQL);
- checkNotNullAndLog("getPrefsForItemSQL", getPrefsForItemSQL);
- checkNotNullAndLog("getNumPreferenceForItemSQL", getNumPreferenceForItemSQL);
- checkNotNullAndLog("getNumPreferenceForItemsSQL", getNumPreferenceForItemsSQL);
-
+
+ AbstractJDBCDataModel.log.debug("Creating AbstractJDBCModel...");
+
+ AbstractJDBCComponent.checkNotNullAndLog("preferenceTable", preferenceTable);
+ AbstractJDBCComponent.checkNotNullAndLog("userIDColumn", userIDColumn);
+ AbstractJDBCComponent.checkNotNullAndLog("itemIDColumn", itemIDColumn);
+ AbstractJDBCComponent.checkNotNullAndLog("preferenceColumn", preferenceColumn);
+
+ AbstractJDBCComponent.checkNotNullAndLog("dataSource", dataSource);
+ AbstractJDBCComponent.checkNotNullAndLog("getUserSQL", getUserSQL);
+ AbstractJDBCComponent.checkNotNullAndLog("getAllUsersSQL", getAllUsersSQL);
+ AbstractJDBCComponent.checkNotNullAndLog("getPreferenceSQL", getPreferenceSQL);
+ AbstractJDBCComponent.checkNotNullAndLog("getNumItemsSQL", getNumItemsSQL);
+ AbstractJDBCComponent.checkNotNullAndLog("getNumUsersSQL", getNumUsersSQL);
+ AbstractJDBCComponent.checkNotNullAndLog("setPreferenceSQL", setPreferenceSQL);
+ AbstractJDBCComponent.checkNotNullAndLog("removePreferenceSQL", removePreferenceSQL);
+ AbstractJDBCComponent.checkNotNullAndLog("getUsersSQL", getUsersSQL);
+ AbstractJDBCComponent.checkNotNullAndLog("getItemsSQL", getItemsSQL);
+ AbstractJDBCComponent.checkNotNullAndLog("getPrefsForItemSQL", getPrefsForItemSQL);
+ AbstractJDBCComponent.checkNotNullAndLog("getNumPreferenceForItemSQL", getNumPreferenceForItemSQL);
+ AbstractJDBCComponent.checkNotNullAndLog("getNumPreferenceForItemsSQL", getNumPreferenceForItemsSQL);
+
if (!(dataSource instanceof ConnectionPoolDataSource)) {
- log.warn("You are not using ConnectionPoolDataSource. Make sure your DataSource pools connections " +
- "to the database itself, or database performance will be severely reduced.");
+ AbstractJDBCDataModel.log
+ .warn("You are not using ConnectionPoolDataSource. Make sure your DataSource pools connections "
+ + "to the database itself, or database performance will be severely reduced.");
}
-
+
this.preferenceTable = preferenceTable;
this.userIDColumn = userIDColumn;
this.itemIDColumn = itemIDColumn;
this.preferenceColumn = preferenceColumn;
-
+
this.dataSource = dataSource;
this.getPreferenceSQL = getPreferenceSQL;
this.getUserSQL = getUserSQL;
@@ -185,105 +181,108 @@
this.getPrefsForItemSQL = getPrefsForItemSQL;
this.getNumPreferenceForItemSQL = getNumPreferenceForItemSQL;
this.getNumPreferenceForItemsSQL = getNumPreferenceForItemsSQL;
-
+
this.cachedNumUsers = -1;
this.cachedNumItems = -1;
- this.itemPrefCounts = new Cache<Long, Integer>(new ItemPrefCountRetriever(getNumPreferenceForItemSQL));
-
+ this.itemPrefCounts = new Cache<Long,Integer>(new ItemPrefCountRetriever(getNumPreferenceForItemSQL));
+
}
-
+
/** @return the {@link DataSource} that this instance is using */
@Override
public DataSource getDataSource() {
return dataSource;
}
-
+
public String getPreferenceTable() {
return preferenceTable;
}
-
+
public String getUserIDColumn() {
return userIDColumn;
}
-
+
public String getItemIDColumn() {
return itemIDColumn;
}
-
+
public String getPreferenceColumn() {
return preferenceColumn;
}
-
+
@Override
public LongPrimitiveIterator getUserIDs() throws TasteException {
- log.debug("Retrieving all users...");
+ AbstractJDBCDataModel.log.debug("Retrieving all users...");
return new ResultSetIDIterator(getUsersSQL);
}
-
- /** @throws NoSuchUserException if there is no such user */
+
+ /**
+ * @throws NoSuchUserException
+ * if there is no such user
+ */
@Override
public PreferenceArray getPreferencesFromUser(long id) throws TasteException {
-
- log.debug("Retrieving user ID '{}'", id);
-
+
+ AbstractJDBCDataModel.log.debug("Retrieving user ID '{}'", id);
+
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
-
+
try {
conn = dataSource.getConnection();
stmt = conn.prepareStatement(getUserSQL, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
stmt.setFetchDirection(ResultSet.FETCH_FORWARD);
stmt.setFetchSize(getFetchSize());
setLongParameter(stmt, 1, id);
-
- log.debug("Executing SQL query: {}", getUserSQL);
+
+ AbstractJDBCDataModel.log.debug("Executing SQL query: {}", getUserSQL);
rs = stmt.executeQuery();
-
+
List<Preference> prefs = new ArrayList<Preference>();
while (rs.next()) {
prefs.add(buildPreference(rs));
}
-
+
if (prefs.isEmpty()) {
throw new NoSuchUserException();
}
-
+
return new GenericUserPreferenceArray(prefs);
-
+
} catch (SQLException sqle) {
- log.warn("Exception while retrieving user", sqle);
+ AbstractJDBCDataModel.log.warn("Exception while retrieving user", sqle);
throw new TasteException(sqle);
} finally {
IOUtils.quietClose(rs, stmt, conn);
}
-
+
}
-
+
@Override
public FastByIDMap<PreferenceArray> exportWithPrefs() throws TasteException {
- log.debug("Exporting all data");
-
+ AbstractJDBCDataModel.log.debug("Exporting all data");
+
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
-
+
FastByIDMap<PreferenceArray> result = new FastByIDMap<PreferenceArray>();
-
+
try {
conn = dataSource.getConnection();
stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
stmt.setFetchDirection(ResultSet.FETCH_FORWARD);
stmt.setFetchSize(getFetchSize());
-
- log.debug("Executing SQL query: {}", getAllUsersSQL);
+
+ AbstractJDBCDataModel.log.debug("Executing SQL query: {}", getAllUsersSQL);
rs = stmt.executeQuery(getAllUsersSQL);
-
+
Long currentUserID = null;
List<Preference> currentPrefs = new ArrayList<Preference>();
while (rs.next()) {
long nextUserID = getLongColumn(rs, 1);
- if (currentUserID != null && !currentUserID.equals(nextUserID)) {
+ if ((currentUserID != null) && !currentUserID.equals(nextUserID)) {
if (!currentPrefs.isEmpty()) {
result.put(currentUserID, new GenericUserPreferenceArray(currentPrefs));
currentPrefs.clear();
@@ -296,43 +295,43 @@
if (!currentPrefs.isEmpty()) {
result.put(currentUserID, new GenericUserPreferenceArray(currentPrefs));
}
-
+
return result;
-
+
} catch (SQLException sqle) {
- log.warn("Exception while exporting all data", sqle);
+ AbstractJDBCDataModel.log.warn("Exception while exporting all data", sqle);
throw new TasteException(sqle);
} finally {
IOUtils.quietClose(rs, stmt, conn);
-
+
}
}
-
+
@Override
public FastByIDMap<FastIDSet> exportWithIDsOnly() throws TasteException {
- log.debug("Exporting all data");
-
+ AbstractJDBCDataModel.log.debug("Exporting all data");
+
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
-
+
FastByIDMap<FastIDSet> result = new FastByIDMap<FastIDSet>();
-
+
try {
conn = dataSource.getConnection();
stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
stmt.setFetchDirection(ResultSet.FETCH_FORWARD);
stmt.setFetchSize(getFetchSize());
-
- log.debug("Executing SQL query: {}", getAllUsersSQL);
+
+ AbstractJDBCDataModel.log.debug("Executing SQL query: {}", getAllUsersSQL);
rs = stmt.executeQuery(getAllUsersSQL);
-
+
boolean currentUserIDSet = false;
long currentUserID = 0L; // value isn't used
FastIDSet currentItemIDs = new FastIDSet(2);
while (rs.next()) {
long nextUserID = getLongColumn(rs, 1);
- if (currentUserIDSet && currentUserID != nextUserID) {
+ if (currentUserIDSet && (currentUserID != nextUserID)) {
if (!currentItemIDs.isEmpty()) {
result.put(currentUserID, currentItemIDs);
currentItemIDs = new FastIDSet(2);
@@ -346,61 +345,64 @@
if (!currentItemIDs.isEmpty()) {
result.put(currentUserID, currentItemIDs);
}
-
+
return result;
-
+
} catch (SQLException sqle) {
- log.warn("Exception while exporting all data", sqle);
+ AbstractJDBCDataModel.log.warn("Exception while exporting all data", sqle);
throw new TasteException(sqle);
} finally {
IOUtils.quietClose(rs, stmt, conn);
-
+
}
}
-
- /** @throws NoSuchUserException if there is no such user */
+
+ /**
+ * @throws NoSuchUserException
+ * if there is no such user
+ */
@Override
public FastIDSet getItemIDsFromUser(long id) throws TasteException {
-
- log.debug("Retrieving items for user ID '{}'", id);
-
+
+ AbstractJDBCDataModel.log.debug("Retrieving items for user ID '{}'", id);
+
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
-
+
try {
conn = dataSource.getConnection();
stmt = conn.prepareStatement(getUserSQL, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
stmt.setFetchDirection(ResultSet.FETCH_FORWARD);
stmt.setFetchSize(getFetchSize());
setLongParameter(stmt, 1, id);
-
- log.debug("Executing SQL query: {}", getUserSQL);
+
+ AbstractJDBCDataModel.log.debug("Executing SQL query: {}", getUserSQL);
rs = stmt.executeQuery();
-
+
FastIDSet result = new FastIDSet();
while (rs.next()) {
result.add(getLongColumn(rs, 2));
}
-
+
if (result.isEmpty()) {
throw new NoSuchUserException();
}
-
+
return result;
-
+
} catch (SQLException sqle) {
- log.warn("Exception while retrieving item s", sqle);
+ AbstractJDBCDataModel.log.warn("Exception while retrieving item s", sqle);
throw new TasteException(sqle);
} finally {
IOUtils.quietClose(rs, stmt, conn);
}
-
+
}
-
+
@Override
public Float getPreferenceValue(long userID, long itemID) throws TasteException {
- log.debug("Retrieving preferences for item ID '{}'", itemID);
+ AbstractJDBCDataModel.log.debug("Retrieving preferences for item ID '{}'", itemID);
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
@@ -411,8 +413,8 @@
stmt.setFetchSize(1);
setLongParameter(stmt, 1, userID);
setLongParameter(stmt, 2, itemID);
-
- log.debug("Executing SQL query: {}", getPreferenceSQL);
+
+ AbstractJDBCDataModel.log.debug("Executing SQL query: {}", getPreferenceSQL);
rs = stmt.executeQuery();
if (rs.next()) {
return rs.getFloat(1);
@@ -420,38 +422,39 @@
return null;
}
} catch (SQLException sqle) {
- log.warn("Exception while retrieving prefs for item", sqle);
+ AbstractJDBCDataModel.log.warn("Exception while retrieving prefs for item", sqle);
throw new TasteException(sqle);
} finally {
IOUtils.quietClose(rs, stmt, conn);
}
}
-
+
@Override
public LongPrimitiveIterator getItemIDs() throws TasteException {
- log.debug("Retrieving all items...");
+ AbstractJDBCDataModel.log.debug("Retrieving all items...");
return new ResultSetIDIterator(getItemsSQL);
}
-
+
@Override
public PreferenceArray getPreferencesForItem(long itemID) throws TasteException {
List<Preference> list = doGetPreferencesForItem(itemID);
return new GenericItemPreferenceArray(list);
}
-
+
protected List<Preference> doGetPreferencesForItem(long itemID) throws TasteException {
- log.debug("Retrieving preferences for item ID '{}'", itemID);
+ AbstractJDBCDataModel.log.debug("Retrieving preferences for item ID '{}'", itemID);
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
conn = dataSource.getConnection();
- stmt = conn.prepareStatement(getPrefsForItemSQL, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
+ stmt = conn.prepareStatement(getPrefsForItemSQL, ResultSet.TYPE_FORWARD_ONLY,
+ ResultSet.CONCUR_READ_ONLY);
stmt.setFetchDirection(ResultSet.FETCH_FORWARD);
stmt.setFetchSize(getFetchSize());
setLongParameter(stmt, 1, itemID);
-
- log.debug("Executing SQL query: {}", getPrefsForItemSQL);
+
+ AbstractJDBCDataModel.log.debug("Executing SQL query: {}", getPrefsForItemSQL);
rs = stmt.executeQuery();
List<Preference> prefs = new ArrayList<Preference>();
while (rs.next()) {
@@ -459,13 +462,13 @@
}
return prefs;
} catch (SQLException sqle) {
- log.warn("Exception while retrieving prefs for item", sqle);
+ AbstractJDBCDataModel.log.warn("Exception while retrieving prefs for item", sqle);
throw new TasteException(sqle);
} finally {
IOUtils.quietClose(rs, stmt, conn);
}
}
-
+
@Override
public int getNumItems() throws TasteException {
if (cachedNumItems < 0) {
@@ -473,7 +476,7 @@
}
return cachedNumItems;
}
-
+
@Override
public int getNumUsers() throws TasteException {
if (cachedNumUsers < 0) {
@@ -481,24 +484,22 @@
}
return cachedNumUsers;
}
-
+
@Override
public int getNumUsersWithPreferenceFor(long... itemIDs) throws TasteException {
if (itemIDs == null) {
throw new IllegalArgumentException("itemIDs is null");
}
int length = itemIDs.length;
- if (length == 0 || length > 2) {
+ if ((length == 0) || (length > 2)) {
throw new IllegalArgumentException("Illegal number of item IDs: " + length);
}
- return length == 1 ?
- itemPrefCounts.get(itemIDs[0]) :
- getNumThings("user preferring items", getNumPreferenceForItemsSQL, itemIDs);
+ return length == 1 ? itemPrefCounts.get(itemIDs[0]) : getNumThings("user preferring items",
+ getNumPreferenceForItemsSQL, itemIDs);
}
-
-
+
private int getNumThings(String name, String sql, long... args) throws TasteException {
- log.debug("Retrieving number of {} in model", name);
+ AbstractJDBCDataModel.log.debug("Retrieving number of {} in model", name);
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
@@ -509,32 +510,32 @@
stmt.setFetchSize(getFetchSize());
if (args != null) {
for (int i = 1; i <= args.length; i++) {
- setLongParameter(stmt, i, args[i-1]);
+ setLongParameter(stmt, i, args[i - 1]);
}
}
- log.debug("Executing SQL query: {}", sql);
+ AbstractJDBCDataModel.log.debug("Executing SQL query: {}", sql);
rs = stmt.executeQuery();
rs.next();
return rs.getInt(1);
} catch (SQLException sqle) {
- log.warn("Exception while retrieving number of {}", name, sqle);
+ AbstractJDBCDataModel.log.warn("Exception while retrieving number of {}", name, sqle);
throw new TasteException(sqle);
} finally {
IOUtils.quietClose(rs, stmt, conn);
}
}
-
+
@Override
public void setPreference(long userID, long itemID, float value) throws TasteException {
if (Float.isNaN(value)) {
throw new IllegalArgumentException("Invalid value: " + value);
}
-
- log.debug("Setting preference for user {}, item {}", userID, itemID);
-
+
+ AbstractJDBCDataModel.log.debug("Setting preference for user {}, item {}", userID, itemID);
+
Connection conn = null;
PreparedStatement stmt = null;
-
+
try {
conn = dataSource.getConnection();
stmt = conn.prepareStatement(setPreferenceSQL);
@@ -542,95 +543,97 @@
setLongParameter(stmt, 2, itemID);
stmt.setDouble(3, value);
stmt.setDouble(4, value);
-
- log.debug("Executing SQL update: {}", setPreferenceSQL);
+
+ AbstractJDBCDataModel.log.debug("Executing SQL update: {}", setPreferenceSQL);
stmt.executeUpdate();
-
+
} catch (SQLException sqle) {
- log.warn("Exception while setting preference", sqle);
+ AbstractJDBCDataModel.log.warn("Exception while setting preference", sqle);
throw new TasteException(sqle);
} finally {
IOUtils.quietClose(null, stmt, conn);
}
}
-
+
@Override
public void removePreference(long userID, long itemID) throws TasteException {
-
- log.debug("Removing preference for user '{}', item '{}'", userID, itemID);
-
+
+ AbstractJDBCDataModel.log.debug("Removing preference for user '{}', item '{}'", userID, itemID);
+
Connection conn = null;
PreparedStatement stmt = null;
-
+
try {
conn = dataSource.getConnection();
stmt = conn.prepareStatement(removePreferenceSQL);
setLongParameter(stmt, 1, userID);
setLongParameter(stmt, 2, itemID);
-
- log.debug("Executing SQL update: {}", removePreferenceSQL);
+
+ AbstractJDBCDataModel.log.debug("Executing SQL update: {}", removePreferenceSQL);
stmt.executeUpdate();
-
+
} catch (SQLException sqle) {
- log.warn("Exception while removing preference", sqle);
+ AbstractJDBCDataModel.log.warn("Exception while removing preference", sqle);
throw new TasteException(sqle);
} finally {
IOUtils.quietClose(null, stmt, conn);
}
}
-
+
@Override
public void refresh(Collection<Refreshable> alreadyRefreshed) {
cachedNumUsers = -1;
cachedNumItems = -1;
itemPrefCounts.clear();
}
-
+
// Some overrideable methods to customize the class behavior:
-
+
protected Preference buildPreference(ResultSet rs) throws SQLException {
return new GenericPreference(getLongColumn(rs, 1), getLongColumn(rs, 2), rs.getFloat(3));
}
-
+
/**
- * Subclasses may wish to override this if ID values in the file are not numeric. This
- * provides a hook by which subclasses can inject an
- * {@link org.apache.mahout.cf.taste.model.IDMigrator} to perform translation.
+ * Subclasses may wish to override this if ID values in the file are not numeric. This provides a hook by
+ * which subclasses can inject an {@link org.apache.mahout.cf.taste.model.IDMigrator} to perform
+ * translation.
*/
protected long getLongColumn(ResultSet rs, int position) throws SQLException {
return rs.getLong(position);
}
-
+
/**
- * Subclasses may wish to override this if ID values in the file are not numeric. This
- * provides a hook by which subclasses can inject an
- * {@link org.apache.mahout.cf.taste.model.IDMigrator} to perform translation.
+ * Subclasses may wish to override this if ID values in the file are not numeric. This provides a hook by
+ * which subclasses can inject an {@link org.apache.mahout.cf.taste.model.IDMigrator} to perform
+ * translation.
*/
protected void setLongParameter(PreparedStatement stmt, int position, long value) throws SQLException {
stmt.setLong(position, value);
}
-
+
/**
- * <p>An {@link java.util.Iterator} which returns items from a {@link ResultSet}.
- * This is a useful way to iterate over all user data since it does not require all data to be
- * read into memory at once. It does however require that the DB connection be held open. Note that this class will
- * only release database resources after {@link #hasNext()} has been called and has returned <code>false</code>;
- * callers should make sure to "drain" the entire set of data to avoid tying up database resources.</p>
+ * <p>
+ * An {@link java.util.Iterator} which returns items from a {@link ResultSet}. This is a useful way to
+ * iterate over all user data since it does not require all data to be read into memory at once. It does
+ * however require that the DB connection be held open. Note that this class will only release database
+ * resources after {@link #hasNext()} has been called and has returned <code>false</code>; callers should
+ * make sure to "drain" the entire set of data to avoid tying up database resources.
+ * </p>
*/
private final class ResultSetIDIterator implements LongPrimitiveIterator {
-
+
private final Connection connection;
private final Statement statement;
private final ResultSet resultSet;
private boolean closed;
-
+
private ResultSetIDIterator(String sql) throws TasteException {
try {
connection = dataSource.getConnection();
statement = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
statement.setFetchDirection(ResultSet.FETCH_FORWARD);
statement.setFetchSize(getFetchSize());
- log.debug("Executing SQL query: {}", sql);
+ AbstractJDBCDataModel.log.debug("Executing SQL query: {}", sql);
resultSet = statement.executeQuery(sql);
boolean anyResults = resultSet.next();
if (!anyResults) {
@@ -641,7 +644,7 @@
throw new TasteException(sqle);
}
}
-
+
@Override
public boolean hasNext() {
boolean nextExists = false;
@@ -653,38 +656,39 @@
nextExists = true;
}
} catch (SQLException sqle) {
- log.warn("Unexpected exception while accessing ResultSet; continuing...", sqle);
+ AbstractJDBCDataModel.log.warn("Unexpected exception while accessing ResultSet; continuing...",
+ sqle);
close();
}
}
return nextExists;
}
-
+
@Override
public Long next() {
return nextLong();
}
-
+
@Override
public long nextLong() {
-
+
if (!hasNext()) {
throw new NoSuchElementException();
}
-
+
try {
long ID = getLongColumn(resultSet, 1);
resultSet.next();
return ID;
} catch (SQLException sqle) {
// No good way to handle this since we can't throw an exception
- log.warn("Exception while iterating", sqle);
+ AbstractJDBCDataModel.log.warn("Exception while iterating", sqle);
close();
throw new NoSuchElementException("Can't retrieve more due to exception: " + sqle);
}
-
+
}
-
+
@Override
public long peek() {
if (!hasNext()) {
@@ -694,13 +698,13 @@
return getLongColumn(resultSet, 1);
} catch (SQLException sqle) {
// No good way to handle this since we can't throw an exception
- log.warn("Exception while iterating", sqle);
+ AbstractJDBCDataModel.log.warn("Exception while iterating", sqle);
close();
throw new NoSuchElementException("Can't retrieve more due to exception: " + sqle);
}
-
+
}
-
+
/**
* @throws UnsupportedOperationException
*/
@@ -708,26 +712,26 @@
public void remove() {
throw new UnsupportedOperationException();
}
-
+
private void close() {
if (!closed) {
closed = true;
IOUtils.quietClose(resultSet, statement, connection);
}
}
-
+
@Override
public void skip(int n) {
if (n >= 1) {
try {
advanceResultSet(resultSet, n);
} catch (SQLException sqle) {
- log.warn("Exception while iterating over items", sqle);
+ AbstractJDBCDataModel.log.warn("Exception while iterating over items", sqle);
close();
}
}
}
-
+
@Override
protected void finalize() throws Throwable {
try {
@@ -736,16 +740,16 @@
super.finalize();
}
}
-
+
}
-
- private class ItemPrefCountRetriever implements Retriever<Long, Integer> {
+
+ private class ItemPrefCountRetriever implements Retriever<Long,Integer> {
private final String getNumPreferenceForItemSQL;
-
+
private ItemPrefCountRetriever(String getNumPreferenceForItemSQL) {
this.getNumPreferenceForItemSQL = getNumPreferenceForItemSQL;
}
-
+
@Override
public Integer get(Long key) throws TasteException {
return getNumThings("user preferring item", getNumPreferenceForItemSQL, key);
Modified: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/ConnectionPoolDataSource.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/ConnectionPoolDataSource.java?rev=909912&r1=909911&r2=909912&view=diff
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/ConnectionPoolDataSource.java (original)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/ConnectionPoolDataSource.java Sat Feb 13 20:54:05 2010
@@ -17,23 +17,28 @@
package org.apache.mahout.cf.taste.impl.model.jdbc;
-import org.apache.commons.dbcp.ConnectionFactory;
-import org.apache.commons.dbcp.PoolableConnectionFactory;
-import org.apache.commons.dbcp.PoolingDataSource;
-import org.apache.commons.pool.impl.GenericObjectPool;
-import org.apache.commons.pool.PoolableObjectFactory;
-
-import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
-/** <p>A wrapper {@link DataSource} which pools connections.</p> */
-public final class ConnectionPoolDataSource implements DataSource {
+import javax.sql.DataSource;
- private final DataSource delegate;
+import org.apache.commons.dbcp.ConnectionFactory;
+import org.apache.commons.dbcp.PoolableConnectionFactory;
+import org.apache.commons.dbcp.PoolingDataSource;
+import org.apache.commons.pool.PoolableObjectFactory;
+import org.apache.commons.pool.impl.GenericObjectPool;
+/**
+ * <p>
+ * A wrapper {@link DataSource} which pools connections.
+ * </p>
+ */
+public final class ConnectionPoolDataSource implements DataSource {
+
+ private final DataSource delegate;
+
public ConnectionPoolDataSource(DataSource underlyingDataSource) {
if (underlyingDataSource == null) {
throw new IllegalArgumentException("underlyingDataSource is null");
@@ -44,60 +49,60 @@
objectPool.setTestOnReturn(false);
objectPool.setTestWhileIdle(true);
objectPool.setTimeBetweenEvictionRunsMillis(60 * 1000L);
- PoolableObjectFactory factory =
- new PoolableConnectionFactory(connectionFactory, objectPool, null, "SELECT 1", false, false);
+ PoolableObjectFactory factory = new PoolableConnectionFactory(connectionFactory, objectPool, null,
+ "SELECT 1", false, false);
objectPool.setFactory(factory);
delegate = new PoolingDataSource(objectPool);
}
-
+
@Override
public Connection getConnection() throws SQLException {
return delegate.getConnection();
}
-
+
@Override
public Connection getConnection(String username, String password) throws SQLException {
return delegate.getConnection(username, password);
}
-
+
@Override
public PrintWriter getLogWriter() throws SQLException {
return delegate.getLogWriter();
}
-
+
@Override
public void setLogWriter(PrintWriter printWriter) throws SQLException {
delegate.setLogWriter(printWriter);
}
-
+
@Override
public void setLoginTimeout(int timeout) throws SQLException {
delegate.setLoginTimeout(timeout);
}
-
+
@Override
public int getLoginTimeout() throws SQLException {
return delegate.getLoginTimeout();
}
-
+
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return delegate.unwrap(iface);
}
-
+
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return delegate.isWrapperFor(iface);
}
-
+
private static class ConfiguringConnectionFactory implements ConnectionFactory {
-
+
private final DataSource underlyingDataSource;
-
+
ConfiguringConnectionFactory(DataSource underlyingDataSource) {
this.underlyingDataSource = underlyingDataSource;
}
-
+
@Override
public Connection createConnection() throws SQLException {
Connection connection = underlyingDataSource.getConnection();
@@ -106,5 +111,5 @@
return connection;
}
}
-
+
}
Modified: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/GenericJDBCDataModel.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/GenericJDBCDataModel.java?rev=909912&r1=909911&r2=909912&view=diff
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/GenericJDBCDataModel.java (original)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/GenericJDBCDataModel.java Sat Feb 13 20:54:05 2010
@@ -17,9 +17,6 @@
package org.apache.mahout.cf.taste.impl.model.jdbc;
-import org.apache.mahout.cf.taste.common.TasteException;
-import org.apache.mahout.common.IOUtils;
-
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -27,16 +24,22 @@
import java.io.InputStream;
import java.util.Properties;
+import org.apache.mahout.cf.taste.common.TasteException;
+import org.apache.mahout.cf.taste.impl.common.jdbc.AbstractJDBCComponent;
+import org.apache.mahout.common.IOUtils;
+
/**
- * <p>A generic {@link org.apache.mahout.cf.taste.model.DataModel} designed for use with other JDBC data sources; one
- * just specifies all necessary SQL queries to the constructor here. Optionally, the queries can be specified from a
- * {@link Properties} object, {@link File}, or {@link InputStream}. This class is most appropriate when other existing
- * implementations of {@link AbstractJDBCDataModel} are not suitable. If you are using this class to support a major
- * database, consider contributing a specialized implementation of {@link AbstractJDBCDataModel} to the project for this
- * database.</p>
+ * <p>
+ * A generic {@link org.apache.mahout.cf.taste.model.DataModel} designed for use with other JDBC data sources;
+ * one just specifies all necessary SQL queries to the constructor here. Optionally, the queries can be
+ * specified from a {@link Properties} object, {@link File}, or {@link InputStream}. This class is most
+ * appropriate when other existing implementations of {@link AbstractJDBCDataModel} are not suitable. If you
+ * are using this class to support a major database, consider contributing a specialized implementation of
+ * {@link AbstractJDBCDataModel} to the project for this database.
+ * </p>
*/
public final class GenericJDBCDataModel extends AbstractJDBCDataModel {
-
+
public static final String DATA_SOURCE_KEY = "dataSource";
public static final String GET_PREFERENCE_SQL_KEY = "getPreferenceSQL";
public static final String GET_USER_SQL_KEY = "getUserSQL";
@@ -50,62 +53,76 @@
public static final String GET_PREFS_FOR_ITEM_SQL_KEY = "getPrefsForItemSQL";
public static final String GET_NUM_PREFERENCE_FOR_ITEM_KEY = "getNumPreferenceForItemSQL";
public static final String GET_NUM_PREFERENCE_FOR_ITEMS_KEY = "getNumPreferenceForItemsSQL";
-
+
/**
- * <p>Specifies all SQL queries in a {@link Properties} object. See the <code>*_KEY</code> constants in this class
- * (e.g. {@link #GET_USER_SQL_KEY}) for a list of all keys which must map to a value in this object.</p>
- *
- * @param props {@link Properties} object containing values
- * @throws TasteException if anything goes wrong during initialization
+ * <p>
+ * Specifies all SQL queries in a {@link Properties} object. See the <code>*_KEY</code> constants in this
+ * class (e.g. {@link #GET_USER_SQL_KEY}) for a list of all keys which must map to a value in this object.
+ * </p>
+ *
+ * @param props
+ * {@link Properties} object containing values
+ * @throws TasteException
+ * if anything goes wrong during initialization
*/
public GenericJDBCDataModel(Properties props) throws TasteException {
- super(lookupDataSource(props.getProperty(DATA_SOURCE_KEY)),
- props.getProperty(GET_PREFERENCE_SQL_KEY),
- props.getProperty(GET_USER_SQL_KEY),
- props.getProperty(GET_ALL_USERS_SQL_KEY),
- props.getProperty(GET_NUM_USERS_SQL_KEY),
- props.getProperty(GET_NUM_ITEMS_SQL_KEY),
- props.getProperty(SET_PREFERENCE_SQL_KEY),
- props.getProperty(REMOVE_PREFERENCE_SQL_KEY),
- props.getProperty(GET_USERS_SQL_KEY),
- props.getProperty(GET_ITEMS_SQL_KEY),
- props.getProperty(GET_PREFS_FOR_ITEM_SQL_KEY),
- props.getProperty(GET_NUM_PREFERENCE_FOR_ITEM_KEY),
- props.getProperty(GET_NUM_PREFERENCE_FOR_ITEMS_KEY));
+ super(AbstractJDBCComponent.lookupDataSource(props.getProperty(GenericJDBCDataModel.DATA_SOURCE_KEY)),
+ props.getProperty(GenericJDBCDataModel.GET_PREFERENCE_SQL_KEY), props
+ .getProperty(GenericJDBCDataModel.GET_USER_SQL_KEY), props
+ .getProperty(GenericJDBCDataModel.GET_ALL_USERS_SQL_KEY), props
+ .getProperty(GenericJDBCDataModel.GET_NUM_USERS_SQL_KEY), props
+ .getProperty(GenericJDBCDataModel.GET_NUM_ITEMS_SQL_KEY), props
+ .getProperty(GenericJDBCDataModel.SET_PREFERENCE_SQL_KEY), props
+ .getProperty(GenericJDBCDataModel.REMOVE_PREFERENCE_SQL_KEY), props
+ .getProperty(GenericJDBCDataModel.GET_USERS_SQL_KEY), props
+ .getProperty(GenericJDBCDataModel.GET_ITEMS_SQL_KEY), props
+ .getProperty(GenericJDBCDataModel.GET_PREFS_FOR_ITEM_SQL_KEY), props
+ .getProperty(GenericJDBCDataModel.GET_NUM_PREFERENCE_FOR_ITEM_KEY), props
+ .getProperty(GenericJDBCDataModel.GET_NUM_PREFERENCE_FOR_ITEMS_KEY));
}
-
+
/**
- * <p>See {@link #GenericJDBCDataModel(java.util.Properties)}. This constructor reads values from a file instead, as
- * if with {@link Properties#load(InputStream)}. So, the file should be in standard Java properties file format --
- * containing <code>key=value</code> pairs, one per line.</p>
- *
- * @param propertiesFile properties file
- * @throws TasteException if anything goes wrong during initialization
+ * <p>
+ * See {@link #GenericJDBCDataModel(java.util.Properties)}. This constructor reads values from a file
+ * instead, as if with {@link Properties#load(InputStream)}. So, the file should be in standard Java
+ * properties file format -- containing <code>key=value</code> pairs, one per line.
+ * </p>
+ *
+ * @param propertiesFile
+ * properties file
+ * @throws TasteException
+ * if anything goes wrong during initialization
*/
public GenericJDBCDataModel(File propertiesFile) throws TasteException {
- this(getPropertiesFromFile(propertiesFile));
+ this(GenericJDBCDataModel.getPropertiesFromFile(propertiesFile));
}
-
+
/**
- * <p>See {@link #GenericJDBCDataModel(Properties)}. This constructor reads values from a resource available in the
- * classpath, as if with {@link Class#getResourceAsStream(String)} and {@link Properties#load(InputStream)}. This is
- * useful if your configuration file is, for example, packaged in a JAR file that is in the classpath.</p>
- *
- * @param resourcePath path to resource in classpath (e.g. "/com/foo/TasteSQLQueries.properties")
- * @throws TasteException if anything goes wrong during initialization
+ * <p>
+ * See {@link #GenericJDBCDataModel(Properties)}. This constructor reads values from a resource available in
+ * the classpath, as if with {@link Class#getResourceAsStream(String)} and
+ * {@link Properties#load(InputStream)}. This is useful if your configuration file is, for example, packaged
+ * in a JAR file that is in the classpath.
+ * </p>
+ *
+ * @param resourcePath
+ * path to resource in classpath (e.g. "/com/foo/TasteSQLQueries.properties")
+ * @throws TasteException
+ * if anything goes wrong during initialization
*/
public GenericJDBCDataModel(String resourcePath) throws TasteException {
- this(getPropertiesFromStream(GenericJDBCDataModel.class.getResourceAsStream(resourcePath)));
+ this(GenericJDBCDataModel.getPropertiesFromStream(GenericJDBCDataModel.class
+ .getResourceAsStream(resourcePath)));
}
-
+
private static Properties getPropertiesFromFile(File file) throws TasteException {
try {
- return getPropertiesFromStream(new FileInputStream(file));
+ return GenericJDBCDataModel.getPropertiesFromStream(new FileInputStream(file));
} catch (FileNotFoundException fnfe) {
throw new TasteException(fnfe);
}
}
-
+
private static Properties getPropertiesFromStream(InputStream is) throws TasteException {
try {
try {
@@ -119,5 +136,5 @@
throw new TasteException(ioe);
}
}
-
+
}