Хорошо, я думаю, вам нужно разбить это на основные «разновидности».
У вас есть два объекта в стиле «сущность»:
User
Campaign
У вас есть одно «отображение» - объект стиля:
UserCampaign
У вас есть один объект «транзакционного» стиля:
Click
Шаг 1: сущность
Начнем с простых: User
& amp; Campaign
. Это действительно два отдельных объекта, и ни один из них не зависит от своего существования. Также между ними нет явной иерархии: пользователи не принадлежат к кампаниям, а кампании не принадлежат пользователям.
Если у вас есть два таких объекта верхнего уровня, они обычно получают свою собственную коллекцию. Итак, вам понадобится коллекция Users
и коллекция Camapaigns
.
Шаг 2: отображение
UserCampaign
в настоящее время используется для представления отображения N-to-M. Теперь, в общем, когда у вас есть отображение N-в-1, вы можете поместить N внутри 1. Однако, с отображением N-в-M, вам обычно приходится «выбирать сторону».
Теоретически, вы можете сделать одно из следующего:
Campaign ID
внутри каждого User
Users ID
s внутри каждого Campaign
Лично я бы сделал # 1. Вероятно, у вас гораздо больше пользователей, которые проводят кампании, и вы, вероятно, хотите разместить массив там, где он будет короче.
Шаг 3: Транзакционные
Клики - это действительно совершенно другой зверь. В терминах объекта можно подумать следующее: Clicks
«принадлежат» a User
, Clicks
«принадлежат» a Campaign
. Таким образом, теоретически вы можете просто хранить клики, являющиеся частью любого из этих объектов. Легко представить, что Клики принадлежат в Пользователи или Кампании.
Но если вы действительно копаете глубже, приведенное выше упрощение действительно ошибочно. В вашей системе Clicks
действительно является центральным объектом. На самом деле, вы можете даже сказать, что пользователи & amp; Кампании действительно просто «связаны» с кликом.
Посмотрите на вопросы / вопросы, которые вы задаете. Все эти вопросы на самом деле сосредоточены вокруг кликов. Users & amp; Кампании не являются центральным объектом в ваших данных, клики.
Кроме того, клики будут самыми многочисленными данными в вашей системе. У вас будет намного больше кликов, чем что-либо еще.
Это самая большая проблема при разработке схемы для таких данных. Иногда вам нужно оттолкнуть «родительские» объекты, когда они не самая важная вещь. Представьте себе создание простой системы электронной коммерции. Ясно, что orders
будет «принадлежать» users
, но orders
настолько важен для системы, что станет объектом «верхнего уровня».
Завершение
Возможно, вам понадобятся три коллекции:
Это должно удовлетворить все ваши запросы:
Смотрите информацию из каждого нажмите как IP, Referer, OS и т. д.
db.clicks.find()
Посмотрите, как часто клики поступают с X IP, X Referer, X OS
db.clicks.group()
или запустите Map-Reduce .
Свяжите каждый клик с пользователем и кампанией.
db.clicks.find({user_id : blah})
Также можно вставлять идентификаторы кликов в пользователей и кампании (если это имеет смысл).
Обратите внимание, что если у вас много кликов, вам действительно придется анализировать запросы, которые вы выполняете чаще всего. Вы не можете индексировать каждое поле, поэтому вам часто нужно запускать Map-Reduces, чтобы «свернуть» данные для этих запросов.
Лучше использовать класс Stopwatch:
using System.Diagnostics;
// ...
Stopwatch sw = new Stopwatch();
sw.Start();
// ...
sw.Stop();
Console.WriteLine("Elapsed={0}",sw.Elapsed);
Как уже говорили другие, Секундомер
- хороший класс для использования здесь. Вы можете обернуть это полезным методом:
public static TimeSpan Time(Action action)
{
Stopwatch stopwatch = Stopwatch.StartNew();
action();
stopwatch.Stop();
return stopwatch.Elapsed;
}
(Обратите внимание на использование Stopwatch.StartNew ()
. Я предпочитаю это создавать секундомер и затем вызывать Start ()
в терминах о простоте.) Очевидно, это вызывает удар по вызову делегата, но в подавляющем большинстве случаев это не имеет значения. Затем вы должны написать:
TimeSpan time = StopwatchUtil.Time(() =>
{
// Do some work
});
Вы даже можете создать для этого интерфейс ITimer
с реализациями StopwatchTimer,
CpuTimer
и т. Д., Где они доступны.
Start ()
с точки зрения простоты.) Очевидно, это влечет за собой попадание вызова делегата, но в подавляющем большинстве случаев это не имеет значения. Затем вы должны написать:
TimeSpan time = StopwatchUtil.Time(() =>
{
// Do some work
});
Вы даже можете создать для этого интерфейс ITimer
с реализациями StopwatchTimer,
CpuTimer
и т. Д., Где они доступны.
Start ()
с точки зрения простоты.) Очевидно, это влечет за собой попадание вызова делегата, но в подавляющем большинстве случаев это не имеет значения. Затем вы должны написать:
TimeSpan time = StopwatchUtil.Time(() =>
{
// Do some work
});
Вы даже можете создать для этого интерфейс ITimer
с реализациями StopwatchTimer,
CpuTimer
и т. Д., Где они доступны.
System.Diagnostics. Секундомер предназначен для этой задачи.
Да, в ядре Windows есть некоторые функции
[System.Runtime.InteropServices.DllImport("KERNEL32")]
private static extern bool QueryPerformanceCounter(ref long lpPerformanceCount);
[System.Runtime.InteropServices.DllImport("KERNEL32")]
private static extern bool QueryPerformanceFrequency(ref long lpFrequency);
public static float CurrentSecond
{
get
{
long current = 0;
QueryPerformanceCounter(ref current);
long frequency = 0;
QueryPerformanceFrequency(ref frequency);
return (float) current / (float) frequency;
}
}
Секундомер в порядке, но зациклите работу 10 ^ 6 раз, а затем разделите на 10 ^ 6. Вы получите гораздо больше точности.