blob: 8b278a223e538d8591eafb79e5dfd28a5c3d0bc1 [file] [log] [blame]
/** Copyright 2014 TappingStone, Inc.
*
* Licensed 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 io.prediction.data.storage
import com.github.nscala_time.time.Imports._
import org.specs2._
import org.specs2.specification.Step
class U2IActionsSpec extends Specification {
def is = s2"""
PredictionIO Storage User-to-item Actions Specification
U2IActions can be implemented by:
- ESU2IActions ${esU2IActions}
- MongoU2IActions ${mongoU2IActions}
"""
def esU2IActions = s2"""
ESU2IActions should" ^
- behave like any U2IActions implementation ${u2iActions(esDO)}
- (index cleanup) ${Step(StorageTestUtils.dropElasticsearchIndex(dbName))}
"""
def mongoU2IActions = s2"""
MongoU2IActions should" ^
- behave like any U2IActions implementation ${u2iActions(mongoDO)}
- (database cleanup) ${Step(StorageTestUtils.dropMongoDatabase(dbName))}
"""
def u2iActions(u2iActions: U2IActions) = s2"""
inserting and getting 3 U2IAction's ${insert(u2iActions)}
getting U2IActions by App ID, User ID, and Item IDs ${getAllByAppidAndUidAndIids(u2iActions)}
getting U2IActions by App ID, Item ${getAllByAppidAndIid(u2iActions)}
delete U2IActions by appid ${deleteByAppid(u2iActions)}
count U2IActions by appid ${countByAppid(u2iActions)}
"""
val dbName = "test_pio_storage_u2iactions_" + hashCode
val esDO = Storage.getDataObject[U2IActions](
StorageTestUtils.elasticsearchSourceName,
dbName)
val mongoDO = Storage.getDataObject[U2IActions](
StorageTestUtils.mongodbSourceName,
dbName)
def insert(u2iActions: U2IActions) = {
val appid = 0
val actions = List(U2IAction(
appid = appid,
action = "rate",
uid = "dead",
iid = "meat",
t = DateTime.now,
latlng = None,
v = Some(3),
price = None
), U2IAction(
appid = appid,
action = "view",
uid = "avatar",
iid = "creeper",
t = DateTime.now,
latlng = Some((94.3904, -29.4839)),
v = None,
price = None
), U2IAction(
appid = appid,
action = "like",
uid = "pub",
iid = "sub",
t = DateTime.now,
latlng = None,
v = Some(1),
price = Some(49.40)
))
actions foreach { u2iActions.insert(_) }
Thread.sleep(1000)
val results = u2iActions.getAllByAppid(appid)
val r1 = results.next
val r2 = results.next
val r3 = results.next
val r = Seq(r1, r2, r3)
results.hasNext must beFalse and
(r must containTheSameElementsAs(actions))
}
def getAllByAppidAndUidAndIids(u2iActions: U2IActions) = {
val appid = 1
val actions = List(U2IAction(
appid = appid,
action = "rate",
uid = "dead",
iid = "meat",
t = DateTime.now,
latlng = None,
v = Some(3),
price = None
), U2IAction(
appid = appid,
action = "view",
uid = "dead",
iid = "creeper",
t = DateTime.now,
latlng = Some((94.3904, -29.4839)),
v = None,
price = None
), U2IAction(
appid = appid,
action = "like",
uid = "dead",
iid = "sub",
t = DateTime.now,
latlng = None,
v = Some(1),
price = Some(49.40)
))
actions foreach { u2iActions.insert(_) }
Thread.sleep(1000)
val results = u2iActions.getAllByAppidAndUidAndIids(
appid, "dead", List("sub", "meat")).toList.sortWith((s, t) =>
s.iid < t.iid)
results.size must beEqualTo(2) and
(results must containTheSameElementsAs(Seq(actions(0), actions(2))))
}
def getAllByAppidAndIid(u2iActions: U2IActions) = {
val appid = 109
val actions = List(U2IAction(
appid = appid,
action = "rate",
uid = "dead",
iid = "meat",
t = DateTime.now,
latlng = None,
v = Some(3),
price = None
), U2IAction(
appid = appid,
action = "view",
uid = "dead",
iid = "creeper",
t = DateTime.now,
latlng = Some((94.3904, -29.4839)),
v = None,
price = None
), U2IAction(
appid = appid,
action = "like",
uid = "dead",
iid = "sub",
t = DateTime.now,
latlng = None,
v = Some(1),
price = Some(49.40)
), U2IAction(
appid = appid,
action = "rate",
uid = "dead2",
iid = "meat",
t = DateTime.now,
latlng = None,
v = Some(2),
price = None
), U2IAction(
appid = appid,
action = "rate",
uid = "dead3",
iid = "meat",
t = DateTime.now,
latlng = None,
v = Some(5),
price = None
), U2IAction(
appid = appid,
action = "rate",
uid = "dead4",
iid = "meat",
t = DateTime.now,
latlng = None,
v = Some(1),
price = None
))
actions foreach { u2iActions.insert(_) }
Thread.sleep(1000)
val results = u2iActions.getAllByAppidAndIid(
appid, "meat", sortedByUid = true).toList
val resultsNoSort = u2iActions.getAllByAppidAndIid(
appid, "meat", sortedByUid = false).toList.sortWith((s, t) =>
s.uid < t.uid)
results.size must beEqualTo(4) and
(results(0) must beEqualTo(actions(0))) and
(results(1) must beEqualTo(actions(3))) and
(results(2) must beEqualTo(actions(4))) and
(results(3) must beEqualTo(actions(5))) and
(resultsNoSort(0) must beEqualTo(actions(0))) and
(resultsNoSort(1) must beEqualTo(actions(3))) and
(resultsNoSort(2) must beEqualTo(actions(4))) and
(resultsNoSort(3) must beEqualTo(actions(5)))
}
def deleteByAppid(u2iActions: U2IActions) = {
// insert a few u2iActions with appid1 and a few u2iActions with appid2.
// delete all u2iActions of appid1.
// u2iActions of appid1 should be deleted and u2iActions of appid2 should still exist.
// delete all u2iActions of appid2
// u2iActions of appid2 should be deleted
val appid1 = 10
val appid2 = 11
val ida = "deleteByAppid-ida"
val idb = "deleteByAppid-idb"
val idc = "deleteByAppid-idc"
val u2iAction1a = U2IAction(
appid = appid1,
action = "rate",
uid = "dead",
iid = "meat",
t = DateTime.now,
latlng = None,
v = Some(3),
price = None
)
val u2iActionsApp1 = List(
u2iAction1a,
u2iAction1a.copy(
v = Some(1)
),
u2iAction1a.copy(
v = Some(2)
))
val u2iActionsApp2 = u2iActionsApp1 map (x => x.copy(appid = appid2))
u2iActionsApp1 foreach { u2iActions.insert(_) }
u2iActionsApp2 foreach { u2iActions.insert(_) }
Thread.sleep(1000)
// NOTE: Call toList to retrieve all results first.
// If call toList after delete, the data is gone because getAllByAppid returns
// iterator which doesn't actually retrieve the result yet.
val g1_App1 = u2iActions.getAllByAppid(appid1).toList
val g1_App2 = u2iActions.getAllByAppid(appid2).toList
u2iActions.deleteByAppid(appid1)
Thread.sleep(1000)
val g2_App1 = u2iActions.getAllByAppid(appid1).toList
val g2_App2 = u2iActions.getAllByAppid(appid2).toList
u2iActions.deleteByAppid(appid2)
Thread.sleep(1000)
val g3_App2 = u2iActions.getAllByAppid(appid2).toList
g1_App1 must containTheSameElementsAs(u2iActionsApp1) and
(g1_App2 must containTheSameElementsAs(u2iActionsApp2)) and
(g2_App1 must be empty) and
(g2_App2 must containTheSameElementsAs(u2iActionsApp2)) and
(g3_App2 must be empty)
}
def countByAppid(u2iActions: U2IActions) = {
val appid1 = 20
val appid2 = 21
val appid3 = 22
val u2iAction1a = U2IAction(
appid = appid1,
action = "rate",
uid = "dead",
iid = "meat",
t = DateTime.now,
latlng = None,
v = Some(3),
price = None
)
val u2iAction1b = u2iAction1a.copy(
appid = appid1
)
val u2iAction2a = u2iAction1a.copy(
appid = appid2
)
u2iActions.insert(u2iAction1a)
u2iActions.insert(u2iAction1b)
u2iActions.insert(u2iAction2a)
Thread.sleep(1000)
u2iActions.countByAppid(appid1) must be_==(2) and
(u2iActions.countByAppid(appid2) must be_==(1)) and
(u2iActions.countByAppid(appid3) must be_==(0))
}
}