65.9K
CodeProject 正在变化。 阅读更多。
Home

利用 MySQL 存储引擎和 API 构建, 使用 SQL 查询语言进行复杂分析: 第三部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (6投票s)

2014年5月4日

CPOL

8分钟阅读

viewsIcon

20776

downloadIcon

382

引言

在本系列文章的第 2 部分中,我介绍了从 Win32 API 中检索数据并将其提供给 MYSQL 的 mysql 存储引擎。具体来说,我展示了如何在 mysql 控制台上获取有关系统中运行进程的最新信息。本系列文章将继续深入,这次将在 mysql 控制台上展示 Facebook 好友,以便您进行复杂的分析。这不仅可以对您的好友进行复杂分析,还可以对您好友的好友,以及他们的好友等等进行分析,即您完整的社交圈。您还可以将分析范围限制在少数几位好友。

FaceBookFriendsSpaceEngine

这个 mysql 存储引擎与第 2 部分介绍的存储引擎不同,因为它从 facebook Graph API 检索数据并将其提供给 mysql。一个名为 Friends 的表,使用 FaceBookFriendsSpaceEngine 作为引擎被创建。

create table Friends
(
  person_id varchar(1024), 
  first_name varchar(1024), 
  gender varchar(1024), 
  last_name  varchar(1024), 
  link  varchar(1024), 
  locale  varchar(1024), 
  name  varchar(1024), 
  updated_time  varchar(1024), 
  username  varchar(1024),
  friend_id varchar(1024), 
  friend_first_name varchar(1024), 
  friend_gender varchar(1024), 
  friend_last_name varchar(1024), 
  friend_link varchar(1024), 
  friend_locale varchar(1024), 
  friend_name varchar(1024), 
  friend_updated_time varchar(1024), 
  friend_username varchar(1024),
  access_token  varchar(2048)
)ENGINE= FaceBookFriendsSpaceEngine;

当用户在 Friends 表上执行 select 查询时,控制权会传递到 FaceBookFriendsSpaceEngine 的 rnd_init 方法。该方法检查表名是否确实是“Friends”。然后,它会检查 where 子句的解析树是否存在。如果存在,则遍历 where 子句的解析树,将其转换为 facebook Graph API 调用。否则,不会向 MySQL 提供任何记录。

在继续 rnd_init 之前,需要解释以下数据结构。

vector<Friends> m_friendsSpace; //(belongs to ha_example class and maintains all friends space for the query)

int m_friendsSpaceIndex; //(belongs to ha_example class and maintains the index to m_friendsSpace)

struct Person
{
    string id; 
    string first_name; 
    string gender; 
    string last_name;
    string link; 
    string locale; 
    string name; 
    string updated_time; 
    string username;

};
struct Friends
{
    Person A;
    Person B;
    string access_token;
};



int ha_example::rnd_init(bool scan)
{
    DBUG_ENTER("ha_example::rnd_init");
    
    if(_stricmp(table_share->table_name.str,"Friends")==0)
    {
        THD *thd=this->ha_thd();
        SELECT_LEX *select_lex=&thd->lex->select_lex;
        m_friendsSpaceIndex=0;

        if(select_lex->where==0)
        {
        }
        else
        {
            stack<ParseStackElement> parseStack;
            select_lex->where->traverse_cond(My_Cond_traverser,(void*)&parseStack,Item::traverse_order::POSTFIX);
            if(!parseStack.empty()&&parseStack.size()==1)
            {
                ParseStackElement topElem=parseStack.top();
                GetFaceBookFriendSpace(topElem.faceBookInputColumnValuesVector,m_friendsSpace);
            }
            else
            {
            }
        }
    }
    DBUG_RETURN(0);
}

在 where 子句解析树的后缀遍历期间,创建并维护 ParseStackElement 结构体堆栈。

struct ParseStackElement
{
    vector<FaceBookInputColumnValue> faceBookInputColumnValuesVector;
    Item *item;
};

ParseStackElement 包含 FaceBookInputColumnValue 类型的向量

struct FaceBookInputColumnValue
{
    string mAccessToken;
    string mUserId;
};

FaceBookInputColumnValue 包含 facebook Graph API 访问令牌和个人 ID。

rnd_init 调用 traverse_cond 来遍历 where 子句解析树的根节点,以便按后缀顺序进行遍历。My_Cond_traverser 函数在遍历过程中被调用。parse 堆栈被传递给此函数。此函数检查以下内容:

In 运算符:在这种情况下,会调用 Item_func_in_case 来获取“in 子句”中指定的访问令牌或个人 ID 集合,并将其推送到堆栈。

Equality 运算符:在这种情况下,会调用 Item_func_eq_case 来获取指定的访问令牌或个人 ID,并将其推送到堆栈。

And/Or 运算符:在这种情况下,会弹出堆栈,直到出现 item(My_Cond_traverser 的第一个参数)的子项。在弹出过程中,FaceBookInputColumnValue 类型的向量会通过 And 和 Or 操作合并,以获得结果集。

void My_Cond_traverser (const Item *item, void *arg)
{
    stack<ParseStackElement> *parseStack=(stack<ParseStackElement> *)arg;
    vector<facebookinputcolumnvalue> faceBookInputColumnValuesVector;
    if(dynamic_cast<const  Item_func_in*>(item))
    {
        faceBookInputColumnValuesVector=Item_func_in_case(item);
        ParseStackElement elem;
        elem.faceBookInputColumnValuesVector=faceBookInputColumnValuesVector;
        elem.item=(Item *)item;
        parseStack->push(elem);
    }
    else if(dynamic_cast<const Item_func_eq*>(item))
    {
        faceBookInputColumnValuesVector=Item_func_eq_case(item);
        ParseStackElement elem;
        elem.faceBookInputColumnValuesVector=faceBookInputColumnValuesVector;
        elem.item=(Item *)item;
        parseStack->push(elem);
    }
    else if(dynamic_cast<const Item_cond*>(item))
    {
        const Item_cond *itemCondC=dynamic_cast<const>(item);
        Item_cond *itemCond=(Item_cond *)itemCondC;
        
        vector<facebookinputcolumnvalue> result;
        bool isAnd=false;
        if(dynamic_cast<const Item_cond_and*>(item))
        {
            isAnd=true;
        }
        if (!parseStack->empty()&&isChildOf((Item_cond*)item,parseStack->top().item))
        {
            result=parseStack->top().faceBookInputColumnValuesVector;
            parseStack->pop();
        }
        while (!parseStack->empty()&&isChildOf((Item_cond*)item,parseStack->top().item))    
        {
            if(isAnd)
            {
                result=And(result,parseStack->top().faceBookInputColumnValuesVector);
            }
            else
            {
                result=Or(result,parseStack->top().faceBookInputColumnValuesVector);
            }
            parseStack->pop();            
        }
        ParseStackElement elem;
        elem.faceBookInputColumnValuesVector=result;
        elem.item=(Item *)item;
        parseStack->push(elem);
    }
}

以下函数提取用户在等号子句中输入的访问令牌或个人 ID。代码会检查 item 是否为 Item_func 类型。然后,它会检查第一个参数是字段,第二个参数不是字段。然后,它会检查字段是 access_token 还是 person_id。

vector<FaceBookInputColumnValue> Item_func_eq_case(const Item *item)
{
    vector<FaceBookInputColumnValue> faceBookInputColumnValuesVector;
    const Item_func * itemFunction=dynamic_cast<const Item_func*>(item);
    Item **arguments=0;
    if(itemFunction)
    {
        arguments=itemFunction->arguments();
    }
    else
    {
        return faceBookInputColumnValuesVector;
    }
    Item *field=0;
    Item *value=0;
    if(dynamic_cast <Item_field*>(arguments[0]))
    {
        field = arguments[0];
    }
    else
    {
        return faceBookInputColumnValuesVector;
    }
    if(dynamic_cast <Item_field*>(arguments[1]))
    {
        return faceBookInputColumnValuesVector;
    }
    else
    {
        value = arguments[1];
    }
    
    if(field&&field->item_name.ptr()&&_stricmp(field->item_name.ptr(),"access_token")==0)
    {
        FaceBookInputColumnValue myFaceBookInputColumns;
        myFaceBookInputColumns.mAccessToken=value->item_name.ptr();
        myFaceBookInputColumns.mUserId="";
        faceBookInputColumnValuesVector.push_back(myFaceBookInputColumns);
    }
    else if(field&&field->item_name.ptr()&&_stricmp(field->item_name.ptr(),"person_id")==0)
    {
        FaceBookInputColumnValue myFaceBookInputColumns;
        myFaceBookInputColumns.mAccessToken="";
        myFaceBookInputColumns.mUserId=value->item_name.ptr();
        faceBookInputColumnValuesVector.push_back(myFaceBookInputColumns);
    }
    return faceBookInputColumnValuesVector;
}

以下函数提取“in 子句”中指定的访问令牌或个人 ID 集合。首先检查 item 是否为 Item_func 类型。然后检查第一个参数是字段,后面的参数都不是字段类型。

vector<FaceBookInputColumnValue> Item_func_in_case(const Item *item)
{
    vector<FaceBookInputColumnValue> faceBookInputColumnValuesVector;
    const Item_func * itemFunction=dynamic_cast<const Item_func*>(item);
    Item **arguments=0;
    int inArgcount=0;
    if(itemFunction)
    {
        arguments=itemFunction->arguments();
        inArgcount=itemFunction->arg_count;
    }
    else
    {
        return faceBookInputColumnValuesVector;
    }
    Item *field=0;
    Item *value=0;
    if(dynamic_cast <Item_field*>(arguments[0]))
    {
        field = arguments[0];
    }
    else
    {
        return faceBookInputColumnValuesVector;
    }
    if(field&&field->item_name.ptr()&&_stricmp(field->item_name.ptr(),"access_token")==0)
    {
        LONG index;
        for (index = 1; index < inArgcount; index++)
        {
            if(dynamic_cast <Item_field*>(arguments[index]))
            {
                continue;
            }
            else
            {
                value = arguments[index];
            }
            FaceBookInputColumnValue myFaceBookInputColumns;
            myFaceBookInputColumns.mAccessToken=value->item_name.ptr();
            myFaceBookInputColumns.mUserId="";
            faceBookInputColumnValuesVector.push_back(myFaceBookInputColumns);
        }
    }
    else if(field&&field->item_name.ptr()&&_stricmp(field->item_name.ptr(),"person_id")==0)
    {
        LONG index;
        for (index = 1; index < inArgcount; index++)
        {
            if(dynamic_cast <Item_field*>(arguments[index]))
            {
                continue;
            }
            else
            {
                value = arguments[index];
            }
            FaceBookInputColumnValue myFaceBookInputColumns;
            myFaceBookInputColumns.mAccessToken="";
            myFaceBookInputColumns.mUserId=value->item_name.ptr();
            faceBookInputColumnValuesVector.push_back(myFaceBookInputColumns);
        }
    }
    return faceBookInputColumnValuesVector;
}

以下是 My_Cond_traverser 用来检查父子关系的一个辅助函数。

bool isChildOf(Item_cond *parent,Item *child)
{
    List_iterator<Item> li(*(parent->argument_list()));
    Item *it= NULL;
    while ((it= li++))    
    {
        if(child==it)
            return true;
    }
    return false;
}

以下函数对 FaceBookInputColumnValue 的向量 A 和 FaceBookInputColumnValue 的向量 B 进行 AND 操作。在此函数中,两个向量都在嵌套循环中遍历。如果 A 中的 FaceBookInputColumnValue 结构与 B 中的相同类型的结构匹配,则将此 FaceBookInputColumnValue 添加到结果向量中。通过检查 A 和 B 中的 FaceBookInputColumnValue 结构的访问令牌和个人 ID 来找到匹配。如果两个访问令牌都不是空的,那么它们必须匹配,否则跳过它们。同样,如果两个个人 ID 都不是空的,那么它们必须匹配,否则跳过它们。如果其中一个访问令牌为空而另一个不为空,则公共访问令牌等于非空的一个。同样,如果其中一个个人 ID 为空而另一个不为空,则公共个人 ID 等于非空的一个。公共值插入到结果向量中。

vector<FaceBookInputColumnValue> And(vector<FaceBookInputColumnValue> A,vector<FaceBookInputColumnValue> B)
{
    vector<FaceBookInputColumnValue> result;

    for(vector<FaceBookInputColumnValue>::iterator iter1=A.begin();iter1!=A.end();iter1++)
    {
        for(vector<FaceBookInputColumnValue>::iterator iter2=B.begin();iter2!=B.end();iter2++)
        {
            string accessToken="";
            if(iter1->mAccessToken!=""&&iter2->mAccessToken!="")
            {
                if(iter1->mAccessToken==iter2->mAccessToken)
                {
                    accessToken=iter1->mAccessToken;
                }
                else
                {
                    continue;
                }
            }
            else if(iter1->mAccessToken!=""&&iter2->mAccessToken=="")
            {
                accessToken=iter1->mAccessToken;
            }
            else if(iter1->mAccessToken==""&&iter2->mAccessToken!="")
            {
                accessToken=iter2->mAccessToken;
            }
            else if(iter1->mAccessToken==""&&iter2->mAccessToken=="")
            {
            }

            string userId="";
            if(iter1->mUserId!=""&&iter2->mUserId!="")
            {
                if(iter1->mUserId==iter2->mUserId)
                {
                    userId=iter1->mUserId;
                }
                else
                {
                    continue;
                }
            }
            else if(iter1->mUserId!=""&&iter2->mUserId=="")
            {
                userId=iter1->mUserId;
            }
            else if(iter1->mUserId==""&&iter2->mUserId!="")
            {
                userId=iter2->mUserId;
            }
            else if(iter1->mUserId==""&&iter2->mUserId=="")
            {
            }
            FaceBookInputColumnValue myFaceBookInputColumns;
            myFaceBookInputColumns.mAccessToken=accessToken;
            myFaceBookInputColumns.mUserId=userId;
            result.push_back(myFaceBookInputColumns);
        }
    }
    return result;
}

以下函数对 FaceBookInputColumnValue 的向量 A 和 FaceBookInputColumnValue 的向量 B 进行 OR 操作。在此函数中,两个向量都在嵌套循环中遍历。如果 A 中的 FaceBookInputColumnValue 结构与 B 中的相同类型的结构具有公共部分,则将公共值添加到结果向量中。通过匹配 A 和 B 中的 FaceBookInputColumnValue 结构的访问令牌和个人 ID 来找到公共部分。如果两个访问令牌都不是空的,那么它们必须匹配,否则跳过它们。同样,如果两个个人 ID 都不是空的,那么它们必须匹配,否则跳过它们。如果其中一个访问令牌为空而另一个不为空,则公共访问令牌为空。同样,如果其中一个个人 ID 为空而另一个不为空,则公共个人 ID 为空。公共值插入到结果向量中,并从向量 A 和 B 中删除。嵌套循环完成后,遍历向量 A,并将所有元素添加到结果向量中。同样,遍历向量 B,并将所有元素添加到结果向量中。

vector<FaceBookInputColumnValue> Or(vector<FaceBookInputColumnValue> A,vector<FaceBookInputColumnValue> B)
{
    vector<FaceBookInputColumnValue> result;

    for(vector<FaceBookInputColumnValue>::iterator iter1=A.begin();iter1!=A.end();iter1++)
    {
        for(vector<FaceBookInputColumnValue>::iterator iter2=B.begin();iter2!=B.end();iter2++)
        {
            bool commonFound=false;
            string accessToken="";
            if(iter1->mAccessToken!=""&&iter2->mAccessToken!="")
            {
                if(iter1->mAccessToken==iter2->mAccessToken)
                {
                    accessToken=iter1->mAccessToken;
                    commonFound=true;
                }
                else
                {
                    continue;
                }
            }
            string userId="";
            if(iter1->mUserId!=""&&iter2->mUserId!="")
            {
                if(iter1->mUserId==iter2->mUserId)
                {
                    userId=iter1->mUserId;
                    commonFound=true;
                }
                else
                {
                    continue;
                }
            }
            if(commonFound==true)
            {
                FaceBookInputColumnValue myFaceBookInputColumns;
                myFaceBookInputColumns.mAccessToken=accessToken;
                myFaceBookInputColumns.mUserId=userId;
                result.push_back(myFaceBookInputColumns);
                A.erase(iter1);
                B.erase(iter2);
                iter1=A.begin();
                iter2=B.begin();
            }
        }
    }
    for(vector<FaceBookInputColumnValue>::iterator iter1=A.begin();iter1!=A.end();iter1++)
    {
        result.push_back(*iter1);
    }
    for(vector<FaceBookInputColumnValue>::iterator iter2=B.begin();iter2!=B.end();iter2++)
    {
        result.push_back(*iter2);
    }
    return result;
}

当 traverse_cond 函数完成时,会进行检查,以确保解析堆栈只包含一个 ParseStackElement 类型的元素。ParseStackElement 中存在的 faceBookInputColumnValuesVector 向量被传递给 GetFaceBookFriendSpace 函数,该函数遍历该向量并为每个元素调用 FaceBookAccessTokenFriendSpace。

void GetFaceBookFriendSpace(vector<FaceBookInputColumnValue> faceBookInputColumnValuesVector,    vector<Friends> &friendsSpace)
{
    vector<FaceBookInputColumnValue>::iterator iter=faceBookInputColumnValuesVector.begin();
    for(;iter!=faceBookInputColumnValuesVector.end();iter++)
    {
        vector<Friends> friendsVec=FaceBookAccessTokenFriendSpace(*iter);
        for(vector<Friends>::iterator iter2=friendsVec.begin();iter2!=friendsVec.end();iter2++)
        {
            friendsSpace.push_back(*iter2);
        }
    }
}

FaceBookAccessTokenFriendSpace 函数会检查,如果 myFaceBookInputColumns 中的访问令牌和用户 ID 都不是空的,则它会直接调用 GetSingleUserFriends,否则,如果访问令牌不是空的,它会获取与访问令牌关联的用户 ID。之后,它会调用 GetSingleUserFriends 并传递检索到的用户 ID。该函数维护两个 string 类型的 hash_set,分别名为 alreadyBrowsedFriends 和 currentFriends。alreadyBrowsedFriends 存储已经遍历过好友的用户 ID。currentFriends 存储需要遍历好友的用户 ID。while 循环会一直迭代,直到 currentFriends 为空。在 while 循环内部,它从 currentFriends 中获取起始用户 ID。它检查该用户 ID 的好友列表是否已经被遍历过。如果是,则从 currentFriends 中删除它。否则,它会调用 GetSingleUserFriends 来处理该用户 ID。在下面的 for 循环中,它将好友添加到 friendsSpace。之后,它会将该用户 ID 添加到 alreadyBrowsedFriends。最后,函数返回 friendsSpace。

vector<Friends> FaceBookAccessTokenFriendSpace(FaceBookInputColumnValue myFaceBookInputColumns)
{
    hash_set<string> alreadyBrowsedFriends;

    hash_set<string> currentFriends;

    vector<Friends> friendsSpace;
    if(myFaceBookInputColumns.mUserId!=""&&myFaceBookInputColumns.mAccessToken!="")
    {
        friendsSpace=GetSingleUserFriends(myFaceBookInputColumns.mUserId,myFaceBookInputColumns.mAccessToken);
    }
    else if(myFaceBookInputColumns.mAccessToken!="")
    {
        string userId;
        userId=GetAccessTokenAssociatedUserId(myFaceBookInputColumns.mAccessToken);
        vector<Friends> myFriends=GetSingleUserFriends(userId,myFaceBookInputColumns.mAccessToken);
        for(vector<Friends>::iterator iter=myFriends.begin();iter!=myFriends.end();iter++)
        {
            currentFriends.insert(iter->B.id);
            friendsSpace.push_back(*iter);
        }
        alreadyBrowsedFriends.insert(userId);
        while(!currentFriends.empty())
        {
            userId=*currentFriends.begin();
            if(alreadyBrowsedFriends.find(userId)==alreadyBrowsedFriends.end())
            {
                myFriends=GetSingleUserFriends(userId,myFaceBookInputColumns.mAccessToken);
                for(vector<Friends>::iterator iter=myFriends.begin();iter!=myFriends.end();iter++)
                {
                    if(currentFriends.find(iter->B.id)==currentFriends.end())
                    {
                        currentFriends.insert(iter->B.id);
                    }
                    friendsSpace.push_back(*iter);
                }
                alreadyBrowsedFriends.insert(userId);
            }
            else
            {
                currentFriends.erase(userId);
            }
        }
    }
    return friendsSpace;
}

GetSingleUserFriends 函数接收用户 ID 和访问令牌,并返回用户的好友。此函数调用 GetUserDetails 来处理传递的 user_id 参数。之后,它从请求 URL 获取用户的好友。结果是一个包含好友列表的 JSON。使用 JSON 路径遍历 JSON。遍历完成直到没有更多好友。在 while 循环中,它调用 GetUserDetails 来获取好友的详细信息。

string commonUrl="https://graph.facebook.com/";
vector<Friends> GetSingleUserFriends(string userId,string accessToken)
{
    vector<Friends> myfriends;
    Person A=GetUserDetails(userId,accessToken);
    string url=commonUrl+userId+"/friends?access_token=";
    url=url+accessToken;
    string friends=WinHttpClientResponse(url);
    int index=0;
    char num[100];
    itoa(index,num,10);
    string count=num;
    string friendId=CallJsonPath(friends,"$.data["+count+"].id");
    index++;
    while(friendId!="")
    {
        Friends f;
        f.A=A;
        f.B=GetUserDetails(friendId,accessToken);
        f.access_token=accessToken;
        myfriends.push_back(f);
        itoa(index,num,10);
        count=num;
        friendId=CallJsonPath(friends,"$.data["+count+"].id");
        index++;
    }
    return myfriends;
}

GetUserDetails 函数接收用户 ID 和访问令牌,并返回用户详细信息。它构建 URL 以获取用户详细信息。在 URL 上发起请求。结果是用户详细信息的 JSON。使用 JSON 路径遍历 JSON 并提取用户详细信息。

Person GetUserDetails(string userId,string accessToken)
{
    Person A;
    string url=commonUrl+userId+"?access_token=";
    url=url+accessToken;
    string details=WinHttpClientResponse(url);
    A.id=CallJsonPath(details,"$.id");
    A.first_name=CallJsonPath(details,"$.first_name");
    A.gender=CallJsonPath(details,"$.gender");
    A.last_name=CallJsonPath(details,"$.last_name");
    A.link=CallJsonPath(details,"$.link");
    A.locale=CallJsonPath(details,"$.locale");
    A.name=CallJsonPath(details,"$.name");
    return A;
}

GetAccessTokenAssociatedUserId 函数接收访问令牌并返回关联的用户 ID。该函数构建 URL 以检索有关我的详细信息。在 URL 上发起请求。结果 JSON 被 JSON 路径遍历以提取用户 ID。

string GetAccessTokenAssociatedUserId(string accessToken)
{
    string url=commonUrl+"me?access_token=";
    url=url+accessToken;
    string me=WinHttpClientResponse(url);
    string userId;
    userId=CallJsonPath(me,"$.id");
    return userId;
}

WinHttpClientResponse 函数接收 URL 并返回 HTTP 响应内容。

string WinHttpClientResponse(string url)
{
    wstring wUrl=StringToWString(url);
    WinHttpClient client(wUrl);
    client.SendHttpRequest();
    wstring httpResponseHeader = client.GetResponseHeader();
    wstring httpResponseContent = client.GetResponseContent();
    return WStringToString(httpResponseContent);
}

CallJsonPath 函数接收 JSON 和路径。它返回路径的值。

string CallJsonPath(string json,string path)
{
    string result="";
    json_t *root;
    json_error_t error;
    root = json_loads(json.c_str(), 0, &error);
    if(root)
    {
        json_t *pathVal=json_path_get(root,path.c_str());
        if(pathVal)
        {
            result=json_string_value(pathVal);
        }
        json_decref(root);
    }
    
    return result;
}

以下是字符串转换函数

wstring StringToWString(string str)
{
    CA2W pszW(str.c_str());
    wstring wStr=pszW;
    return wStr;
}
string WStringToString(wstring wStr)
{
    CW2A pszA(wStr.c_str());
    string str=pszA;
    return str;
}

MySQL 调用 rnd_next 来提供下一行。如果 m_friendsSpaceIndex 小于 m_friendsSpace 的大小,则表示还有要提供的行,则调用 feed_person 为 friend 结构中的 person A 提供数据。之后,调用 feed_person 为 friend 结构中的 person B 提供数据。之后,提供访问令牌。然后,增加 m_friendsSpaceIndex。如果没有更多好友要提供,则返回 HA_ERR_END_OF_FILE,否则返回 0。

int ha_example::rnd_next(uchar *buf)
{
    int rc;
    DBUG_ENTER("ha_example::rnd_next");
    MYSQL_READ_ROW_START(table_share->db.str, table_share->table_name.str,
                    TRUE);
    if(m_friendsSpaceIndex<m_friendsSpace.size())
    {
        Field **field=table->field;      
        feed_person(m_friendsSpace[m_friendsSpaceIndex].A,field);
        feed_person(m_friendsSpace[m_friendsSpaceIndex].B,field);
        feed_person_fields(m_friendsSpace[m_friendsSpaceIndex].access_token,field);
        m_friendsSpaceIndex++;
        rc=0;
    }
    else
    {
        rc= HA_ERR_END_OF_FILE;
    }
    MYSQL_READ_ROW_DONE(rc);
    DBUG_RETURN(rc);
}

feed_person_fields 方法接收字段值和 MySQL 数据结构 Field,并将值存储到字段中。

void ha_example::feed_person_fields(string fieldStr,Field **&field)
{
        bitmap_set_bit(table->write_set, (*field)->field_index);
        (*field)->set_notnull();
        (*field)->store(fieldStr.c_str(),strlen(fieldStr.c_str()), system_charset_info);
        field++;
}

feed_person 函数接收 person 和 MySQL 结构 Field,并将 person 的所有成员存储到字段中。

void ha_example::feed_person(Person p,Field **&field)
{
    feed_person_fields(p.id,field);
    feed_person_fields(p.first_name,field);
    feed_person_fields(p.gender,field);
    feed_person_fields(p.last_name,field);
    feed_person_fields(p.link,field);
    feed_person_fields(p.locale,field);
    feed_person_fields(p.name,field);
    feed_person_fields(p.updated_time,field);
    feed_person_fields(p.username,field);
}

如何运行

 

  • 从以下链接安装 MySQL 5.6.17
    • https://dev.mysqlserver.cn/downloads/installer/5.6.html
  • 确保在安装过程中,选择 32 位版本。
  • 从 FaceBookFriendsSpaceEngineBinaries.zip 复制 FaceBookFriendsSpaceEngine.dll
  • 转到安装了 MYSQL 的目录。
  • 找到子目录 lib 并打开它。
  • 找到并打开里面的 plugin 目录。
  • 粘贴 FaceBookFriendsSpaceEngine.dll。
  • 转到 MySQL 控制台并运行以下命令。
  • Install plugin FaceBookFriendsSpaceEngine soname 'FaceBookFriendsSpaceEngine.dll';
  • 这将安装 FaceBookFriendsSpaceEngine。
  • 创建一个名为 test 的数据库。
  • 现在创建一个名为 Friends 的表,并将 FaceBookFriendsSpaceEngine 指定为存储引擎。

 

create table Friends
(
  person_id varchar(1024), 
  first_name varchar(1024), 
  gender varchar(1024), 
  last_name  varchar(1024), 
  link  varchar(1024), 
  locale  varchar(1024), 
  name  varchar(1024), 
  updated_time  varchar(1024), 
  username  varchar(1024),
  friend_id varchar(1024), 
  friend_first_name varchar(1024), 
  friend_gender varchar(1024), 
  friend_last_name varchar(1024), 
  friend_link varchar(1024), 
  friend_locale varchar(1024), 
  friend_name varchar(1024), 
  friend_updated_time varchar(1024), 
  friend_username varchar(1024),
  access_token  varchar(2048)
)ENGINE= FaceBookFriendsSpaceEngine;

输入以下查询

select * from friends where access_token="Type your access token here";

这是另一个查询

select count(friend_gender) from friends where access_token=" Type your access token here " and person_id="facebook id of a person";

现在输入您选择的任何查询,然后查看结果。

接下来,我们将演示如何使用 Tableau 对您的好友进行复杂分析。

在上面的 GUI 中,点击 Connect to data。

在 On a server 列表中选择 MySQL。

在 MySQL Connection 对话框中输入服务器名称、用户名和密码,然后点击 Connect。

在服务器上选择 test 数据库。

选择 Friends 表。

选择 Connect live。

将 access_token 拖到 Filters,选择 Custom Value List,然后输入 facebook access_token。

点击 + 按钮。

同样,将 person_id 拖到 Filters,然后选择 custom value list。

输入任何 facebook 用户 ID,然后点击 + 按钮。

将 friends_id 拖到 Rows。

将 friend_name 拖到 rows。

将 friend_link 拖到 Rows。

在哪里获取源代码

  • 从以下链接下载 MYSQL 源代码。
    • https://dev.mysqlserver.cn/downloads/mysql/
  • 将源代码提取到 C:/mysql-5.6.17。

如何构建源代码

 

  • 请按照以下地址提供的说明进行操作:
    • https://dev.mysqlserver.cn/doc/refman/5.6/en/installing-source-distribution.html
  • 我使用 Visual Studio 2012 按照以下说明构建了源代码:
    • cmake . -G "Visual Studio 11"
  • 下载本文附件中名为 FaceBookFriendsSpaceEngineSourceCode.zip 的文件。
  • 它在 example 文件夹中包含 FaceBookFriendsSpaceEngine 的源代码。
  • 将 example 文件夹复制到 mysql-5.6.17\storage。

结论

您应该明白为什么需要解析 where 子句,而不是直接提供所有数据。下一部分将增强好友分析功能。

© . All rights reserved.