/*
* Copyright 2011-2013 the original author or authors.
*
* 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 kr.debop4j.data.mongodb.dao;
import com.google.common.collect.Lists;
import kr.debop4j.core.Action1;
import kr.debop4j.core.parallelism.Parallels;
import kr.debop4j.data.hibernate.unitofwork.IUnitOfWork;
import kr.debop4j.data.hibernate.unitofwork.UnitOfWorkNestingOptions;
import kr.debop4j.data.hibernate.unitofwork.UnitOfWorks;
import kr.debop4j.data.mongodb.MongoGridDatastoreTestBase;
import kr.debop4j.data.mongodb.model.Player;
import kr.debop4j.data.mongodb.model.Tournament;
import kr.debop4j.data.ogm.dao.IHibernateOgmDao;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.Query;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.joda.time.DateTime;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Date;
import java.util.List;
import java.util.Random;
import static org.fest.assertions.Assertions.assertThat;
/**
* MongoDB를 저장소로 사용하는 Ogm 용 Dao인 MongoOgmDao 에 대한 테스트입니다.
*
* @author 배성혁 ( sunghyouk.bae@gmail.com )
* @since 13. 4. 16. 오후 3:14
*/
@Slf4j
public class MongoOgmDaoTest extends MongoGridDatastoreTestBase {
private static final Random rnd = new Random();
private static final Date BIRTH = DateTime.now().withTimeAtStartOfDay().withDate(1968, 10, 14).toDate();
private static final int REPEAT_COUNT = 10;
private static final int PLAYER_COUNT = 1000;
private static final int TOURNAMENT_COUNT = 16;
@Autowired
MongoOgmDao dao;
private Player createPlayer() {
Player player = new Player();
player.setName("성혁");
player.setSurname("배");
player.setAge(45);
player.setBirth(BIRTH);
return player;
}
private Tournament createTournament(String name) {
Tournament tournament = new Tournament();
tournament.setTournament(name);
return tournament;
}
private List<Player> createTestPlayers(int count) {
List<Player> players = Lists.newArrayListWithCapacity(count);
for (int i = 0; i < count; i++) {
Player player = createPlayer();
player.setName("이름-" + i);
player.setAge(i);
players.add(player);
}
List<Tournament> tournaments = Lists.newArrayListWithCapacity(TOURNAMENT_COUNT);
for (int i = 0; i < TOURNAMENT_COUNT; i++) {
Tournament tournament = createTournament("토너먼트-" + i);
for (int j = 0; j < 22; j++) {
Player player = players.get(rnd.nextInt(count));
tournament.getPlayers().add(player);
player.getTournaments().add(tournament);
}
tournaments.add(tournament);
}
return players;
}
@Test
public void crud() throws Exception {
Player player = createPlayer();
dao.saveOrUpdate(player);
UnitOfWorks.getCurrent().flushSession();
UnitOfWorks.getCurrent().clearSession();
Player loaded = dao.get(Player.class, player.getId());
assertThat(loaded).isNotNull();
log.debug("loaded=[{}]", loaded);
assertThat(loaded.getName()).isEqualTo(player.getName());
loaded.setAge(46);
dao.saveOrUpdate(loaded);
UnitOfWorks.getCurrent().flushSession();
UnitOfWorks.getCurrent().clearSession();
loaded = dao.get(Player.class, player.getId());
assertThat(loaded).isNotNull();
assertThat(loaded.getName()).isEqualTo(player.getName());
dao.delete(loaded);
UnitOfWorks.getCurrent().flushSession();
UnitOfWorks.getCurrent().clearSession();
loaded = dao.get(Player.class, player.getId());
assertThat(loaded).isNull();
}
@Test
public void deleteByIdTest() throws Exception {
Player player = createPlayer();
dao.saveOrUpdate(player);
UnitOfWorks.getCurrent().flushSession();
UnitOfWorks.getCurrent().clearSession();
dao.deleteById(Player.class, player.getId());
UnitOfWorks.getCurrent().flushSession();
UnitOfWorks.getCurrent().clearSession();
Player loaded = dao.get(Player.class, player.getId());
assertThat(loaded).isNull();
}
@Test
public void searchQueryTest() throws Exception {
daoInParallel(new Action1<IHibernateOgmDao>() {
@Override
public void perform(IHibernateOgmDao dao) {
// findAll
List<Player> loadedPlayers = dao.find(Player.class);
assertThat(loadedPlayers).isNotNull();
assertThat(loadedPlayers.size()).isGreaterThan(0);
log.debug("findAll seach result = [{}]", loadedPlayers.size());
// 일자로 검색 (equal)
Query luceneQuery = dao.getQueryBuilder(Player.class)
.keyword().onField("birth").matching(BIRTH)
.createQuery();
loadedPlayers = dao.find(Player.class, luceneQuery);
assertThat(loadedPlayers).isNotNull();
assertThat(loadedPlayers.size()).isGreaterThan(0);
log.debug("birth seach result = [{}]", loadedPlayers.size());
QueryBuilder queryBuilder = dao.getQueryBuilder(Player.class);
// AND 검색 ( name like xxx% and surname = xxx)
Query nameQuery = queryBuilder.keyword().wildcard().onField("name").matching("이름*").createQuery();
Query surnameQuery = queryBuilder.keyword().onField("surname").matching("배").createQuery();
luceneQuery = queryBuilder
.bool()
.must(nameQuery)
.must(surnameQuery)
.createQuery();
loadedPlayers = dao.find(Player.class, luceneQuery);
assertThat(loadedPlayers).isNotNull();
assertThat(loadedPlayers.size()).isGreaterThan(0);
log.debug("AND Seach result = [{}]", loadedPlayers.size());
// RANGE 25 < x <= 28 (excludeLimit)
luceneQuery = queryBuilder.range().onField("age").from(25).to(28).excludeLimit().createQuery();
loadedPlayers = dao.find(Player.class, luceneQuery, null);
assertThat(loadedPlayers).isNotNull();
assertThat(loadedPlayers.size()).isGreaterThan(1);
log.debug("range seach result = [{}]", loadedPlayers.size());
for (Player player : loadedPlayers)
log.debug("Player=[{}]", player);
}
});
}
public void daoInSerial(Action1<IHibernateOgmDao> action) throws Exception {
for (int i = 0; i < REPEAT_COUNT; i++) {
List<Player> players = createTestPlayers(PLAYER_COUNT);
for (Player player : players) {
dao.persist(player);
}
dao.flushToIndexes();
dao.getSession().flush();
dao.getSession().clear();
log.debug("Player [{}]명을 추가했습니다.", players.size());
}
try {
action.perform(dao);
} finally {
log.debug("Player 엔티티를 삭제합니다...");
List<Player> players = dao.find(Player.class);
assertThat(players.size()).isGreaterThan(0);
dao.deleteAll(players);
UnitOfWorks.getCurrent().flushSession();
UnitOfWorks.getCurrent().clearSession();
}
assertThat(dao.count(Player.class)).isEqualTo(0);
}
public void daoInParallel(Action1<IHibernateOgmDao> action) throws Exception {
Parallels.run(REPEAT_COUNT, new Action1<Integer>() {
@Override
public void perform(Integer arg) {
try (IUnitOfWork unitOfWork = UnitOfWorks.start(UnitOfWorkNestingOptions.CreateNewOrNestUnitOfWork)) {
List<Player> players = createTestPlayers(PLAYER_COUNT);
for (Player player : players) {
dao.saveOrUpdate(player);
}
dao.getSession().flush();
try {
Thread.sleep(10);
} catch (InterruptedException ignored) {}
/**
* 병렬 작업 시에는 flushToIndexes() 메소드를 호출하여,
* session이 닫히거나 스레드가 중단되기 전에 인덱싱을 마무리하도록 한다.
*/
dao.flushToIndexes();
dao.getSession().close();
log.debug("Player [{}]명을 추가했습니다.", players.size());
} catch (Exception e) {
log.error("예외 발생", e);
}
}
});
try {
action.perform(dao);
} finally {
log.debug("Player 엔티티를 삭제합니다...");
List<Player> players = dao.find(Player.class);
assertThat(players.size()).isGreaterThan(0);
dao.deleteAll(players);
dao.getFullTextSession().flush();
dao.flushToIndexes();
dao.getFullTextSession().close();
}
assertThat(dao.count(Player.class)).isEqualTo(0);
}
}