mahout-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sro...@apache.org
Subject svn commit: r1245615 - in /mahout/trunk/core/src: main/java/org/apache/mahout/cf/taste/impl/model/ test/java/org/apache/mahout/cf/taste/impl/model/
Date Fri, 17 Feb 2012 15:34:19 GMT
Author: srowen
Date: Fri Feb 17 15:34:17 2012
New Revision: 1245615

URL: http://svn.apache.org/viewvc?rev=1245615&view=rev
Log:
MAHOUT-977 add multiple anonymous user support to DataModel

Added:
    mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousConcurrentUserDataModel.java
    mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousConcurrentUserDataModelTest.java
Modified:
    mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousUserDataModel.java

Added: mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousConcurrentUserDataModel.java
URL: http://svn.apache.org/viewvc/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousConcurrentUserDataModel.java?rev=1245615&view=auto
==============================================================================
--- mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousConcurrentUserDataModel.java
(added)
+++ mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousConcurrentUserDataModel.java
Fri Feb 17 15:34:17 2012
@@ -0,0 +1,344 @@
+/*
+ * 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;
+
+import com.google.common.base.Preconditions;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import org.apache.mahout.cf.taste.common.NoSuchItemException;
+import org.apache.mahout.cf.taste.common.TasteException;
+import org.apache.mahout.cf.taste.impl.common.FastIDSet;
+import org.apache.mahout.cf.taste.impl.common.LongPrimitiveIterator;
+import org.apache.mahout.cf.taste.model.DataModel;
+import org.apache.mahout.cf.taste.model.Preference;
+import org.apache.mahout.cf.taste.model.PreferenceArray;
+
+/**
+ * <p>
+ * This is a special thread-safe version of {@link PlusAnonymousUserDataModel}
+ * which allow multiple concurrent anonymous requests.
+ * </p>
+ *
+ * <p>
+ * To use it, you have to estimate the number of concurrent anonymous users of your application.
+ * The pool of users with the given size will be created. For each anonymous recommendations
request,
+ * a user has to be taken from the pool and returned back immediately afterwars.
+ * </p>
+ *
+ * <p>
+ * If no more users are available in the pool, anonymous recommendations cannot be produced.
+ * </p>
+ *
+ * </p>
+ *
+ * Setup:
+ * <pre>
+ * int concurrentUsers = 100;
+ * DataModel realModel = ..
+ * PlusAnonymousConcurrentUserDataModel plusModel =
+ *   new PlusAnonymousConcurrentUserDataModel(realModel, concurrentUsers);
+ * Recommender recommender = ...;
+ * </pre>
+ *
+ * Real-time recommendation:
+ * <pre>
+ * PlusAnonymousConcurrentUserDataModel plusModel =
+ *   (PlusAnonymousConcurrentUserDataModel) recommender.getDataModel();
+ *
+ * // Take the next available anonymous user from the pool
+ * Long anonymousUserID = plusModel.takeAvailableUser();
+ *
+ * PreferenceArray tempPrefs = ..
+ * tempPrefs.setUserID(0, anonymousUserID);
+ * tempPrefs.setItemID(0, itemID);
+ * plusModel.setTempPrefs(tempPrefs);
+ *
+ * // Produce recommendations
+ * recommender.recommend(anonymousUserID, howMany);
+ *
+ * // It is very IMPORTANT to release user back to the pool
+ * plusModel.releaseUser(anonymousUserID);
+ * </pre>
+ *
+ * </p>
+ */
+public final class PlusAnonymousConcurrentUserDataModel extends PlusAnonymousUserDataModel
{
+
+  /** Preferences for all anonymous users */
+  private final Map<Long,PreferenceArray> tempPrefs;
+  /** Item IDs set for all anonymous users */
+  private final Map<Long,FastIDSet> prefItemIDs;
+  /** Pool of the users (FIFO) */
+  private Queue<Long> usersPool;
+
+  /**
+   * @param delegate Real model where anonymous users will be added to
+   * @param maxConcurrentUsers Maximum allowed number of concurrent anonymous users
+   */
+  public PlusAnonymousConcurrentUserDataModel(DataModel delegate, int maxConcurrentUsers)
{
+    super(delegate);
+
+    tempPrefs = new ConcurrentHashMap<Long, PreferenceArray>();
+    prefItemIDs = new ConcurrentHashMap<Long, FastIDSet>();
+
+    initializeUsersPools(maxConcurrentUsers);
+  }
+
+  /**
+   * Initialize the pool of concurrent anonymous users.
+   *
+   * @param usersPoolSize Maximum allowed number of concurrent anonymous user. Depends on
the consumer system.
+   */
+  private void initializeUsersPools(int usersPoolSize) {
+    usersPool = new ConcurrentLinkedQueue<Long>();
+    for (int i = 0; i < usersPoolSize; i++) {
+      usersPool.add(TEMP_USER_ID + i);
+    }
+  }
+
+  /**
+   * Take the next available concurrent anonymous users from the pool.
+   *
+   * @return User ID or null if no more users are available
+   */
+  public Long takeAvailableUser() {
+    Long takenUserID = usersPool.poll();
+    if (takenUserID != null) {
+      // Initialize the preferences array to indicate that the user is taken.
+      tempPrefs.put(takenUserID, new GenericUserPreferenceArray(0));
+      return takenUserID;
+    }
+    return null;
+  }
+
+  /**
+   * Release previously taken anonymous user and return it to the pool.
+   *
+   * @param userID ID of a previously taken anonymous user
+   * @return true if the user was previously taken, false otherwise
+   */
+  public boolean releaseUser(Long userID) {
+    if (tempPrefs.containsKey(userID)) {
+      this.clearTempPrefs(userID);
+      // Return previously taken user to the pool
+      usersPool.offer(userID);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Checks whether a given user is a valid previously acquired anonymous user.
+   */
+  private boolean isAnonymousUser(long userID) {
+    return tempPrefs.containsKey(userID);
+  }
+
+  /**
+   * Sets temporary preferences for a given anonymous user.
+   */
+  public void setTempPrefs(PreferenceArray prefs, long anonymousUserID) {
+    Preconditions.checkArgument(prefs != null && prefs.length() > 0, "prefs is
null or empty");
+
+    this.tempPrefs.put(anonymousUserID, prefs);
+    FastIDSet userPrefItemIDs = new FastIDSet();
+
+    for (int i = 0; i < prefs.length(); i++) {
+      userPrefItemIDs.add(prefs.getItemID(i));
+    }
+
+    this.prefItemIDs.put(anonymousUserID, userPrefItemIDs);
+  }
+
+  /**
+   * Clears temporary preferences for a given anonymous user.
+   */
+  public void clearTempPrefs(long anonymousUserID) {
+    this.tempPrefs.remove(anonymousUserID);
+    this.prefItemIDs.remove(anonymousUserID);
+  }
+
+  @Override
+  public LongPrimitiveIterator getUserIDs() throws TasteException {
+    // Anonymous users have short lifetime and should not be included into the neighbohoods
of the real users.
+    // Thus exclude them from the universe.
+    return getDelegate().getUserIDs();
+  }
+
+  @Override
+  public PreferenceArray getPreferencesFromUser(long userID) throws TasteException {
+    if (isAnonymousUser(userID)) {
+      return tempPrefs.get(userID);
+    }
+    return getDelegate().getPreferencesFromUser(userID);
+  }
+
+  @Override
+  public FastIDSet getItemIDsFromUser(long userID) throws TasteException {
+    if (isAnonymousUser(userID)) {
+      return prefItemIDs.get(userID);
+    }
+    return getDelegate().getItemIDsFromUser(userID);
+  }
+
+  @Override
+  public PreferenceArray getPreferencesForItem(long itemID) throws TasteException {
+    if (tempPrefs.isEmpty()) {
+      return getDelegate().getPreferencesForItem(itemID);
+    }
+
+    PreferenceArray delegatePrefs = null;
+
+    try {
+      delegatePrefs = getDelegate().getPreferencesForItem(itemID);
+    } catch (NoSuchItemException nsie) {
+      // OK. Probably an item that only the anonymous user has
+    }
+
+    List<Preference> anonymousPreferences = new ArrayList<Preference>();
+
+    for (Map.Entry<Long, PreferenceArray> prefsMap : tempPrefs.entrySet()) {
+      PreferenceArray singleUserTempPrefs = prefsMap.getValue();
+      for (int i = 0; i < singleUserTempPrefs.length(); i++) {
+        if (singleUserTempPrefs.getItemID(i) == itemID) {
+          anonymousPreferences.add(singleUserTempPrefs.get(i));
+        }
+      }
+    }
+
+    int delegateLength = delegatePrefs == null ? 0 : delegatePrefs.length();
+    int anonymousPrefsLength = anonymousPreferences.size();
+    int prefsCounter = 0;
+
+    // Merge the delegate and anonymous preferences into a single array
+    PreferenceArray newPreferenceArray = new GenericItemPreferenceArray(delegateLength +
anonymousPrefsLength);
+
+    for (int i = 0; i < delegateLength; i++) {
+      newPreferenceArray.set(prefsCounter++, delegatePrefs.get(i));
+    }
+
+    for (Preference anonymousPreference : anonymousPreferences) {
+      newPreferenceArray.set(prefsCounter++, anonymousPreference);
+    }
+
+    if (newPreferenceArray.length() == 0) {
+      // No, didn't find it among the anonymous user prefs
+      throw new NoSuchItemException(itemID);
+    }
+
+    return newPreferenceArray;
+  }
+
+  @Override
+  public Float getPreferenceValue(long userID, long itemID) throws TasteException {
+    if (isAnonymousUser(userID)) {
+      PreferenceArray singleUserTempPrefs = tempPrefs.get(userID);
+      for (int i = 0; i < singleUserTempPrefs.length(); i++) {
+        if (singleUserTempPrefs.getItemID(i) == itemID) {
+          return singleUserTempPrefs.getValue(i);
+        }
+      }
+      return null;
+    }
+    return getDelegate().getPreferenceValue(userID, itemID);
+  }
+
+  @Override
+  public Long getPreferenceTime(long userID, long itemID) throws TasteException {
+    if (isAnonymousUser(userID)) {
+      // Timestamps are not saved for anonymous preferences
+      return null;
+    }
+    return getDelegate().getPreferenceTime(userID, itemID);
+  }
+
+  @Override
+  public int getNumUsers() throws TasteException {
+    // Anonymous users have short lifetime and should not be included into the neighbohoods
of the real users.
+    // Thus exclude them from the universe.
+    return getDelegate().getNumUsers();
+  }
+
+  @Override
+  public int getNumUsersWithPreferenceFor(long itemID) throws TasteException {
+    if (tempPrefs.isEmpty()) {
+      return getDelegate().getNumUsersWithPreferenceFor(itemID);
+    }
+
+    int countAnonymousUsersWithPreferenceFor = 0;
+
+    for (Map.Entry<Long, PreferenceArray> singleUserTempPrefs : tempPrefs.entrySet())
{
+      for (int i = 0; i < singleUserTempPrefs.getValue().length(); i++) {
+        if (singleUserTempPrefs.getValue().getItemID(i) == itemID) {
+          countAnonymousUsersWithPreferenceFor++;
+          break;
+        }
+      }
+    }
+    return getDelegate().getNumUsersWithPreferenceFor(itemID) + countAnonymousUsersWithPreferenceFor;
+  }
+
+  @Override
+  public int getNumUsersWithPreferenceFor(long itemID1, long itemID2) throws TasteException
{
+    if (tempPrefs.isEmpty()) {
+      return getDelegate().getNumUsersWithPreferenceFor(itemID1, itemID2);
+    }
+
+    int countAnonymousUsersWithPreferenceFor = 0;
+
+    for (Map.Entry<Long, PreferenceArray> singleUserTempPrefs : tempPrefs.entrySet())
{
+      boolean found1 = false;
+      boolean found2 = false;
+      for (int i = 0; i < singleUserTempPrefs.getValue().length() && !(found1
&& found2); i++) {
+        long itemID = singleUserTempPrefs.getValue().getItemID(i);
+        if (itemID == itemID1) {
+          found1 = true;
+        }
+        if (itemID == itemID2) {
+          found2 = true;
+        }
+      }
+
+      if (found1 && found2) {
+        countAnonymousUsersWithPreferenceFor++;
+      }
+    }
+
+    return getDelegate().getNumUsersWithPreferenceFor(itemID1, itemID2) + countAnonymousUsersWithPreferenceFor;
+  }
+
+  @Override
+  public void setPreference(long userID, long itemID, float value) throws TasteException
{
+    if (isAnonymousUser(userID)) {
+      throw new UnsupportedOperationException();
+    }
+    getDelegate().setPreference(userID, itemID, value);
+  }
+
+  @Override
+  public void removePreference(long userID, long itemID) throws TasteException {
+    if (isAnonymousUser(userID)) {
+      throw new UnsupportedOperationException();
+    }
+    getDelegate().removePreference(userID, itemID);
+  }
+}

Modified: mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousUserDataModel.java
URL: http://svn.apache.org/viewvc/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousUserDataModel.java?rev=1245615&r1=1245614&r2=1245615&view=diff
==============================================================================
--- mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousUserDataModel.java
(original)
+++ mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousUserDataModel.java
Fri Feb 17 15:34:17 2012
@@ -76,7 +76,7 @@ import com.google.common.base.Preconditi
  *
  * </p>
  */
-public final class PlusAnonymousUserDataModel implements DataModel {
+public class PlusAnonymousUserDataModel implements DataModel {
 
   public static final long TEMP_USER_ID = Long.MIN_VALUE;
   
@@ -88,6 +88,10 @@ public final class PlusAnonymousUserData
     this.delegate = delegate;
     this.prefItemIDs = new FastIDSet();
   }
+
+  protected DataModel getDelegate() {
+    return delegate;
+  }
   
   public void setTempPrefs(PreferenceArray prefs) {
     Preconditions.checkArgument(prefs != null && prefs.length() > 0, "prefs is
null or empty");

Added: mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousConcurrentUserDataModelTest.java
URL: http://svn.apache.org/viewvc/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousConcurrentUserDataModelTest.java?rev=1245615&view=auto
==============================================================================
--- mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousConcurrentUserDataModelTest.java
(added)
+++ mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousConcurrentUserDataModelTest.java
Fri Feb 17 15:34:17 2012
@@ -0,0 +1,313 @@
+/*
+ * 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;
+
+import java.util.Iterator;
+import org.apache.mahout.cf.taste.common.NoSuchUserException;
+import org.apache.mahout.cf.taste.common.TasteException;
+import org.apache.mahout.cf.taste.model.PreferenceArray;
+import org.apache.mahout.cf.taste.impl.common.FastByIDMap;
+import org.apache.mahout.common.MahoutTestCase;
+import org.junit.Test;
+
+public final class PlusAnonymousConcurrentUserDataModelTest extends MahoutTestCase {
+
+	/**
+	 * Prepares a testable object without delegate data
+	 */
+	private static PlusAnonymousConcurrentUserDataModel getTestableWithoutDelegateData(int maxConcurrentUsers)
{
+		FastByIDMap<PreferenceArray> delegatePreferences = new FastByIDMap<PreferenceArray>();
+		return new PlusAnonymousConcurrentUserDataModel(new GenericDataModel(delegatePreferences),
maxConcurrentUsers);
+	}
+
+	/**
+	 * Prepares a testable object with delegate data
+	 */
+  private static PlusAnonymousConcurrentUserDataModel getTestableWithDelegateData(
+        int maxConcurrentUsers, FastByIDMap<PreferenceArray> delegatePreferences) {
+		return new PlusAnonymousConcurrentUserDataModel(new GenericDataModel(delegatePreferences),
maxConcurrentUsers);
+	}
+
+	/**
+	 * Test taking the first available user
+	 */
+	@Test
+	public void testTakeFirstAvailableUser() {
+		PlusAnonymousConcurrentUserDataModel instance = getTestableWithoutDelegateData(10);
+		Long expResult = PlusAnonymousUserDataModel.TEMP_USER_ID;
+		Long result = instance.takeAvailableUser();
+		assertEquals(expResult, result);
+	}
+
+	/**
+	 * Test taking the next available user
+	 */
+	@Test
+	public void testTakeNextAvailableUser() {
+		PlusAnonymousConcurrentUserDataModel instance = getTestableWithoutDelegateData(10);
+    // Skip first user
+    instance.takeAvailableUser();
+		Long result = instance.takeAvailableUser();
+    Long expResult = PlusAnonymousUserDataModel.TEMP_USER_ID + 1;
+    assertEquals(expResult, result);
+	}
+
+	/**
+	 * Test taking an unavailable user
+	 */
+	@Test
+	public void testTakeUnavailableUser() {
+		PlusAnonymousConcurrentUserDataModel instance = getTestableWithoutDelegateData(1);
+		// Take the only available user
+		instance.takeAvailableUser();
+		// There are no more users available
+		assertNull(instance.takeAvailableUser());
+	}
+
+	/**
+	 * Test releasing a valid previously taken user
+	 */
+	@Test
+	public void testReleaseValidUser() {
+		PlusAnonymousConcurrentUserDataModel instance = getTestableWithoutDelegateData(10);
+		Long takenUserID = instance.takeAvailableUser();
+		assertTrue(instance.releaseUser(takenUserID));
+	}
+
+	/**
+	 * Test releasing an invalid user
+	 */
+	@Test
+	public void testReleaseInvalidUser() {
+		PlusAnonymousConcurrentUserDataModel instance = getTestableWithoutDelegateData(10);
+		assertFalse(instance.releaseUser(Long.MAX_VALUE));
+	}
+
+	/**
+	 * Test releasing a user which had been released earlier
+	 */
+	@Test
+	public void testReleasePreviouslyReleasedUser() {
+		PlusAnonymousConcurrentUserDataModel instance = getTestableWithoutDelegateData(10);
+		Long takenUserID = instance.takeAvailableUser();
+		assertTrue(instance.releaseUser(takenUserID));
+		assertFalse(instance.releaseUser(takenUserID));
+	}
+
+	/**
+	 * Test setting anonymous user preferences
+	 */
+	@Test
+	public void testSetAndGetTempPreferences() throws TasteException {
+		PlusAnonymousConcurrentUserDataModel instance = getTestableWithoutDelegateData(10);
+		Long anonymousUserID = instance.takeAvailableUser();
+		PreferenceArray tempPrefs = new GenericUserPreferenceArray(1);
+		tempPrefs.setUserID(0, anonymousUserID);
+		tempPrefs.setItemID(0, 1);
+		instance.setTempPrefs(tempPrefs, anonymousUserID);
+		assertEquals(tempPrefs, instance.getPreferencesFromUser(anonymousUserID));
+		instance.releaseUser(anonymousUserID);
+	}
+
+	/**
+	 * Test setting and getting preferences from several concurrent anonymous users
+	 */
+	@Test
+	public void testSetMultipleTempPreferences() throws TasteException {
+		PlusAnonymousConcurrentUserDataModel instance = getTestableWithoutDelegateData(10);
+
+		Long anonymousUserID1 = instance.takeAvailableUser();
+		Long anonymousUserID2 = instance.takeAvailableUser();
+
+		PreferenceArray tempPrefs1 = new GenericUserPreferenceArray(1);
+		tempPrefs1.setUserID(0, anonymousUserID1);
+		tempPrefs1.setItemID(0, 1);
+
+		PreferenceArray tempPrefs2 = new GenericUserPreferenceArray(2);
+		tempPrefs2.setUserID(0, anonymousUserID2);
+		tempPrefs2.setItemID(0, 2);
+		tempPrefs2.setUserID(1, anonymousUserID2);
+		tempPrefs2.setItemID(1, 3);
+
+		instance.setTempPrefs(tempPrefs1, anonymousUserID1);
+		instance.setTempPrefs(tempPrefs2, anonymousUserID2);
+
+		assertEquals(tempPrefs1, instance.getPreferencesFromUser(anonymousUserID1));
+		assertEquals(tempPrefs2, instance.getPreferencesFromUser(anonymousUserID2));
+	}
+
+	/**
+	 * Test counting the number of delegate users
+	 */
+	@Test
+	public void testGetNumUsersWithDelegateUsersOnly() throws TasteException {
+    PreferenceArray prefs = new GenericUserPreferenceArray(1);
+    long sampleUserID = 1;
+		prefs.setUserID(0, sampleUserID);
+    long sampleItemID = 11;
+    prefs.setItemID(0, sampleItemID);
+
+		FastByIDMap<PreferenceArray> delegatePreferences = new FastByIDMap<PreferenceArray>();
+		delegatePreferences.put(sampleUserID, prefs);
+
+		PlusAnonymousConcurrentUserDataModel instance = getTestableWithDelegateData(10, delegatePreferences);
+
+		assertEquals(1, instance.getNumUsers());
+	}
+
+	/**
+	 * Test counting the number of anonymous users
+	 */
+	@Test
+	public void testGetNumAnonymousUsers() throws TasteException {
+		PlusAnonymousConcurrentUserDataModel instance = getTestableWithoutDelegateData(10);
+
+		Long anonymousUserID1 = instance.takeAvailableUser();
+
+		PreferenceArray tempPrefs1 = new GenericUserPreferenceArray(1);
+		tempPrefs1.setUserID(0, anonymousUserID1);
+		tempPrefs1.setItemID(0, 1);
+
+		instance.setTempPrefs(tempPrefs1, anonymousUserID1);
+
+		// Anonymous users should not be included into the universe.
+		assertEquals(0, instance.getNumUsers());
+	}
+
+	/**
+	 * Test retrieve a single preference value of an anonymous user
+	 */
+	@Test
+	public void testGetPreferenceValue() throws TasteException {
+		PlusAnonymousConcurrentUserDataModel instance = getTestableWithoutDelegateData(10);
+
+		Long anonymousUserID = instance.takeAvailableUser();
+
+		PreferenceArray tempPrefs = new GenericUserPreferenceArray(1);
+    tempPrefs.setUserID(0, anonymousUserID);
+    long sampleItemID = 1;
+    tempPrefs.setItemID(0, sampleItemID);
+    tempPrefs.setValue(0, Float.MAX_VALUE);
+
+		instance.setTempPrefs(tempPrefs, anonymousUserID);
+
+		assertEquals(Float.MAX_VALUE, instance.getPreferenceValue(anonymousUserID, sampleItemID),
EPSILON);
+	}
+
+	/**
+	 * Test retrieve preferences for existing non-anonymous user
+	 */
+	@Test
+	public void testGetPreferencesForNonAnonymousUser() throws TasteException {
+    PreferenceArray prefs = new GenericUserPreferenceArray(1);
+    long sampleUserID = 1;
+		prefs.setUserID(0, sampleUserID);
+    long sampleItemID = 11;
+    prefs.setItemID(0, sampleItemID);
+
+		FastByIDMap<PreferenceArray> delegatePreferences = new FastByIDMap<PreferenceArray>();
+		delegatePreferences.put(sampleUserID, prefs);
+
+		PlusAnonymousConcurrentUserDataModel instance = getTestableWithDelegateData(10, delegatePreferences);
+
+		assertEquals(prefs, instance.getPreferencesFromUser(sampleUserID));
+	}
+
+	/**
+	 * Test retrieve preferences for non-anonymous and non-existing user
+	 */
+	@Test(expected=NoSuchUserException.class)
+	public void testGetPreferencesForNonExistingUser() throws TasteException {
+		PlusAnonymousConcurrentUserDataModel instance = getTestableWithoutDelegateData(10);
+		// Exception is expected since such user does not exist
+		instance.getPreferencesFromUser(1);
+	}
+
+	/**
+	 * Test retrieving the user IDs and verifying that anonymous ones are not included
+	 */
+	@Test
+	public void testGetUserIDs() throws TasteException {
+    PreferenceArray prefs = new GenericUserPreferenceArray(1);
+    long sampleUserID = 1;
+		prefs.setUserID(0, sampleUserID);
+    long sampleItemID = 11;
+    prefs.setItemID(0, sampleItemID);
+
+		FastByIDMap<PreferenceArray> delegatePreferences = new FastByIDMap<PreferenceArray>();
+		delegatePreferences.put(sampleUserID, prefs);
+
+		PlusAnonymousConcurrentUserDataModel instance = getTestableWithDelegateData(10, delegatePreferences);
+
+		Long anonymousUserID = instance.takeAvailableUser();
+
+		PreferenceArray tempPrefs = new GenericUserPreferenceArray(1);
+		tempPrefs.setUserID(0, anonymousUserID);
+		tempPrefs.setItemID(0, 22);
+
+		instance.setTempPrefs(tempPrefs, anonymousUserID);
+
+		Iterator<Long> userIDs = instance.getUserIDs();
+
+		assertSame(sampleUserID, userIDs.next());
+		assertFalse(userIDs.hasNext());
+	}
+
+	/**
+	 * Test getting preferences for an item.
+	 *
+	 * @throws TasteException
+	 */
+	@Test
+	public void testGetPreferencesForItem() throws TasteException {
+    PreferenceArray prefs = new GenericUserPreferenceArray(2);
+    long sampleUserID = 4;
+		prefs.setUserID(0, sampleUserID);
+    long sampleItemID = 11;
+    prefs.setItemID(0, sampleItemID);
+		prefs.setUserID(1, sampleUserID);
+    long sampleItemID2 = 22;
+    prefs.setItemID(1, sampleItemID2);
+
+		FastByIDMap<PreferenceArray> delegatePreferences = new FastByIDMap<PreferenceArray>();
+		delegatePreferences.put(sampleUserID, prefs);
+
+		PlusAnonymousConcurrentUserDataModel instance = getTestableWithDelegateData(10, delegatePreferences);
+
+		Long anonymousUserID = instance.takeAvailableUser();
+
+		PreferenceArray tempPrefs = new GenericUserPreferenceArray(2);
+		tempPrefs.setUserID(0, anonymousUserID);
+		tempPrefs.setItemID(0, sampleItemID);
+		tempPrefs.setUserID(1, anonymousUserID);
+    long sampleItemID3 = 33;
+    tempPrefs.setItemID(1, sampleItemID3);
+
+		instance.setTempPrefs(tempPrefs, anonymousUserID);
+
+		assertEquals(sampleUserID, instance.getPreferencesForItem(sampleItemID).get(0).getUserID());
+		assertEquals(2, instance.getPreferencesForItem(sampleItemID).length());
+		assertEquals(1, instance.getPreferencesForItem(sampleItemID2).length());
+		assertEquals(1, instance.getPreferencesForItem(sampleItemID3).length());
+
+		assertEquals(2, instance.getNumUsersWithPreferenceFor(sampleItemID));
+		assertEquals(1, instance.getNumUsersWithPreferenceFor(sampleItemID, sampleItemID2));
+		assertEquals(1, instance.getNumUsersWithPreferenceFor(sampleItemID, sampleItemID3));
+	}
+
+}



Mime
View raw message