예측할 수 없는 행동로그 분석하기: 게임과 같은 서비스에서 사용자들의 패턴을 분석하고, 모든 행동을 기록한다.
초기 모델링
유저의 대한 데이터를 생성하고 유저의 로그를 배열로 저장한다
db.users.insertOne({
_id: "tom",
joinDate: new Date(),
server: "Sevilla",
job: "merchant",
logInfo: [],
});
log = {
loginTime: new Date(),
visits: [],
sails: [],
trades: [],
battles: [],
quests: [],
fishings: [],
gambles: [],
castings: [],
farmings: [],
};
log.visits.push({
location: "Barcelona",
time: new Date(),
});
log.visits.push({
location: "Sevilla",
time: new Date(),
});
log.visits.push({
location: "London",
time: new Date(),
});
log.trades.push({
item: "Musket",
qty: 50,
price: 1800,
});
log.trades.push({
item: "Musket",
qty: -50,
price: 2300,
});
log.quests.push({
name: "Cave Invenstigation",
reward: 50000,
});
db.users.updateOne(
{ _id: "tom" },
{
$addToSet: {
logInfo: log,
},
}
);
위와 같은 모델링은 하나의 컬렉션에 너무 큰 데이터가 들어가게 된다. (로그가 많아지게 되면 도큐먼트의 최대 크기인 16MB를 초과한다)

개선1
데이터를 분리한다.
// 어느유저에 대한 로그인지 필드를 추가한다.
log.user = "tom";
log.logoutTime = new Date();
db.logs.insertOne(log);
배열 크기가 문제는 어느정도 해결되었지만, 로그가 추가될 수록 관리가 복잡해지고 인덱스가 많아진다.

개선2
key-value 형태로 데이터를 묶고 하나의 인덱스만 생성하여 데이터를 관리한다.
(어떤형태의 행동이 나오더라도 action의 타입만 정해주고 값과 추가적인 데이터를 추가해주면 더 쉽게 관리할 수 있다)
// tom이라는 유저의 행동패턴을 actions라는 배열안에 action이라는 key-value 형태로 저장한다.
date = new Date();
log = {
user: "tom",
loginTime: date,
logoutTime: new Date(),
actions: [
// 액션에 대한 형태를 키밸류로 정의함
{ action: "visit", value: "Barcelona", time: date },
{ action: "visit", value: "Sevilla", time: date },
{ action: "visit", value: "London", time: date },
{ action: "trade", value: "Musket", type: "buy", qty: 50, price: 1800 },
{ action: "trade", value: "Musket", type: "sell", qty: 50, price: 2300 },
{
action: "quest",
value: "Cave Investigation",
reward: 50000,
status: "In Progress",
},
],
};
위와 같은 방식은 복합인덱스 형태로 최소한의 인덱스만 설정할 수 있다.
// 복합인덱스 형태로 인덱스의 개수가 한개로 줄어든다.
// 특정할 수 있는 필드가 다 제각각(필드가 다양함)인 경우 action이라는 키로 행동을 정의하고 밸류에 데이터를 넣는다.
db.logs.createIndex({ "actions.action": 1, "actions.value": 1 });
결론