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






4.89/5 (6投票s)
引言
在本系列文章的第 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;
- 转到以下 URL
- 点击 Get Access Token 按钮。在 User Data Permissions 下选择 user_friends,然后点击 Get Access Token 按钮。
输入以下查询
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 子句,而不是直接提供所有数据。下一部分将增强好友分析功能。