【问题标题】:Defining a webservice for usage analytics (dekstop application)为使用分析定义 Web 服务(桌面应用程序)
【发布时间】:2012-10-09 10:56:05
【问题描述】:

现状
我有一个桌面应用程序 (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。

免责声明: 有一个similar post,它引起了我对DeskMetricsTracker bird 等服务的关注,或者尝试将谷歌分析嵌入到C++ 本机应用程序中,但我更愿意自己使用该服务,并且更好地了解如何设计这种模型。

谢谢!

【问题讨论】:

    标签: mysql database analytics desktop-application database-normalization


    【解决方案1】:

    这似乎是一个database normalization 问题。

    我还将假设您还有一个名为 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                |
    +-------------------+----------------+-------------------+
    

    【讨论】:

      【解决方案2】:

      编辑:笨蛋!我读过 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 文本传输。

      【讨论】:

      • 谢谢,但序列化不是问题(即使在 C++ 中)。问题是使用 SQL 在数据库中查询和查找数据。
      【解决方案3】:

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

      Name                   Param1
      button_A_Click    WindowTitle
      

      因此您可以将您的活动分组或仅选择一个活动..

      这就是我要解决的方法。

      【讨论】:

      • 1 个参数不足以满足所有事件类型(某些事件需要多个参数)。我可以添加 5 个(或 10 个)参数,但它和我之前说的 2 个替代方案相同,但似乎太浪费了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多