6

当前情况
我有一个桌面应用程序 (C++ Win32),我希望匿名跟踪用户的使用分析(操作、点击、使用时间等)
。跟踪是通过指定的 Web 服务完成的特定操作(安装、卸载、单击) 并且所有内容都由我的团队编写并存储在我们的数据库中。

需求
现在我们正在添加更多的使用类型和事件以及各种数据,因此我们需要定义服务。
我不想为每个操作提供大量不同的 Web 服务,而是希望为所有使用类型提供一个通用服务,它能够接收不同的数据类型。
例如:

  • “button_A_click”事件,具有 1 个字段的数据:{window_name (string)}
  • “show_notification”事件,有 3 个字段的数据:{source_id (int), user_action (int), index (int)}

问题
我正在寻找一种优雅便捷的方式来存储这种多样化的数据,以便稍后我可以轻松地查询它。
我能想到的替代方案:

  • 将每种使用类型的不同数据存储为 JSON/XML 对象的一个​​字段,但提取数据并为这些字段编写查询将非常困难

  • 每条记录都有额外的N个数据字段,但这似乎很浪费。

对这种模型有什么想法吗?也许像谷歌分析?请指教...

技术:数据库是在 phpMyAdmin 下运行的 MySQL。

免责声明: 有一篇类似的帖子引起了我的注意,例如DeskMetricsTracker bird等服务,或者尝试将谷歌分析嵌入到 C++ 原生应用程序中,但我更愿意自己使用该服务,并更好地了解如何设计它样的模型。

谢谢!

4

3 回答 3

2

这似乎是一个数据库规范化问题。

我还将假设您还有一个名为events所有事件的存储表。

此外,我将假设您必须具有以下数据属性(为简单起见):window_name, source_id, user_action, index

为了实现标准化,我们需要以下表格:

events
data_attributes
attribute_types

这是每个表的结构方式:

mysql> describe events;
+------------+------------------+------+-----+---------+----------------+
| Field      | Type             | Null | Key | Default | Extra          |
+------------+------------------+------+-----+---------+----------------+
| id         | int(11) unsigned | NO   | PRI | NULL    | auto_increment |
| event_type | varchar(255)     | YES  |     | NULL    |                |
+------------+------------------+------+-----+---------+----------------+

mysql> describe data_attributes;
+-----------------+------------------+------+-----+---------+----------------+
| Field           | Type             | Null | Key | Default | Extra          |
+-----------------+------------------+------+-----+---------+----------------+
| id              | int(11) unsigned | NO   | PRI | NULL    | auto_increment |
| event_id        | int(11)          | YES  |     | NULL    |                |
| attribute_type  | int(11)          | YES  |     | NULL    |                |
| attribute_name  | varchar(255)     | YES  |     | NULL    |                |
| attribute_value | int(11)          | YES  |     | NULL    |                |
+-----------------+------------------+------+-----+---------+----------------+

mysql> describe attribute_types;
+-------+------------------+------+-----+---------+----------------+
| Field | Type             | Null | Key | Default | Extra          |
+-------+------------------+------+-----+---------+----------------+
| id    | int(11) unsigned | NO   | PRI | NULL    | auto_increment |
| type  | varchar(255)     | YES  |     | NULL    |                |
+-------+------------------+------+-----+---------+----------------+

这个想法是你必须attribute_types用你可以拥有的所有可能的类型来填充。然后,对于每个新事件,您将在表中添加一个条目并在events表中添加相应条目,data_attributes以将该事件映射到具有适当值的一个或多个属性类型。

例子:

"button_A_click" event, has data with 1 field: {window_name "Dummy Window Name"}
"show_notification" event, has data with 3 fields: {source_id: 99, user_action: 44, index: 78}

将表示为:

mysql> select * from attribute_types;
+----+-------------+
| id | type        |
+----+-------------+
|  1 | window_name |
|  2 | source_id   |
|  3 | user_action |
|  4 | index       |
+----+-------------+

mysql> select * from events;
+----+-------------------+
| id | event_type        |
+----+-------------------+
|  1 | button_A_click    |
|  2 | show_notification |
+----+-------------------+

mysql> select * from data_attributes;
+----+----------+----------------+-------------------+-----------------+
| id | event_id | attribute_type | attribute_name    | attribute_value |
+----+----------+----------------+-------------------+-----------------+
|  1 |        1 |              1 | Dummy Window Name |            NULL |
|  2 |        2 |              2 | NULL              |              99 |
|  3 |        2 |              3 | NULL              |              44 |
|  4 |        2 |              4 | NULL              |              78 |
+----+----------+----------------+-------------------+-----------------+

要为此数据编写查询,您可以使用 MySQL 中的COALESCE函数为您获取值,而无需检查哪些列是NULL.

这是我破解的一个简单示例:

SELECT  events.event_type as `event_type`, 
        attribute_types.type as `attribute_type`, 
        COALESCE(data_attributes.attribute_name, data_attributes.attribute_value) as `value`
FROM    data_attributes,
        events,
        attribute_types
WHERE   data_attributes.event_id = events.id
AND     data_attributes.attribute_type = attribute_types.id

产生以下输出:

+-------------------+----------------+-------------------+
| event_type        | attribute_type | value             |
+-------------------+----------------+-------------------+
| button_A_click    | window_name    | Dummy Window Name |
| show_notification | source_id      | 99                |
| show_notification | user_action    | 44                |
| show_notification | index          | 78                |
+-------------------+----------------+-------------------+
于 2012-10-21T16:30:25.453 回答
1

编辑:臭虫!我读过 C#,但我看到你正在使用 C++。对于那个很抱歉。我将答案保留原样,因为它的原理仍然有用。请将示例视为伪代码。

您可以定义与数组一起使用的自定义类/结构。然后序列化这些数据并发送到 WebService。例如:

[Serializable()]
public class ActionDefinition {
    public string ID;
    public ActionType Action; // define an Enum with possible actions
    public List[] Fields;  //Or a list of 'some class' if you need more complex fields
}

List AnalyticsCollection = new List(Of, Actiondefinition);

// ...
SendToWS(Serialize(AnalyticsCollection));

现在,您可以根据需要灵活地动态添加任意数量的事件。

在服务器端,您可以简单地解析数据:

List[of, ActionDefinition] AnalyticsCollection = Deserialize(GetWS());

foreach (ActionDefinition ad in AnalyticsCollection) {
    switch (ad.Action) {
        //.. check for each action type
    }
}

我建议添加诸如校验和之类的安全机制。我想 de/serializer 在 C++ 中是非常自定义的,所以也许简单的 Base64 编码就可以解决问题,并且可以作为 ascii 文本传输。

于 2012-10-21T15:05:32.047 回答
0

您可以为每个事件制作一个表格,在其中声明什么参数意味着什么。然后你有一个主表,你只输入事件名称和 param1 等。管理工具将非常简单,你浏览所有事件,并使用声明每个事件的表来描述它们。例如,对于您的事件 button_A_click 您插入到描述表中:

Name                   Param1
button_A_Click    WindowTitle

因此,您可以对事件进行分组或仅选择一个事件..

这就是我将如何解决它。

于 2012-10-21T14:59:52.530 回答