書接上回,之前有一篇文章提到了标簽雲系統的建構:Python3.7+jieba(結巴分詞)配合Wordcloud2.js來構造網站标簽雲(關鍵詞集合),但是這篇隻是淺顯的說明了一下如何進行切詞以及前端如何使用wordcloud2.js進行前端展示,本次主要讨論下标簽分詞切出來之後,如何進行存儲。
假設我們目前文章-标簽體系的需求是這樣:
每篇文章都具有唯一的标題、描述以及 URL。
每篇文章都具有一個或多個标簽。
每篇文章都具有作者的名稱,以及喜歡
每篇文章都有使用者的評論,使用者名、消息、日期時間以及評論的喜歡度。
每篇文章都可以有 0 個或多個評論。
那麼如果使用關系型資料庫來設計,比較簡單的設計方案可以是這樣:
可以注意到,标簽和文章的對應關系還是簡單的一對多,如果做成比較靈活的多對多還需要增加一張關系表,這樣就是四張表了。
如果使用nosql比如Mongodb來說,隻需要一張表(聚合)就可以實作:
{
_id: POST_ID
title: TITLE_OF_POST,
description: POST_DESCRIPTION,
by: POST_BY,
url: URL_OF_POST,
tags: [TAG1, TAG2, TAG3],
likes: TOTAL_LIKES,
comments: [
{
user:'COMMENT_BY',
message: TEXT,
dateCreated: DATE_TIME,
like: LIKES
},
{
user:'COMMENT_BY',
message: TEXT,
dateCreated: DATE_TIME,
like: LIKES
}
]
}
可以看到标簽是由數組實作的,那麼關系型資料庫mysql和非關系型資料庫mongodb在标簽實作中本質上有什麼差別呢?
關系資料庫如mysql中标簽雲的實作是簡單的,标簽和文章分别在不同的表中,通過join可以比較簡單的查詢出标簽的統計資料。 而MongoDB為快速水準擴張以及極高的性能而優化,在MongoDB中沒有join,傾向于使用embedding來代替linking關系。
假設我們的需求又有了變化,普通部落格變身成為具有數百萬篇文章的小說站.每個小說都有許多布爾屬性,大約一萬個可能的屬性,每篇小說都有十幾個章節,假設我希望能夠實時(幾毫秒)請求給出的前n項任何屬性組合的标簽。
你會選擇推薦什麼解決方案?毫無疑問,如果你在尋找極具擴充性的方案,Mongodb無疑更好。
而且從業務角度上來講,無論是通過标簽查文章,還是文章查标簽這樣的需求,都非常靈活,當然了根據文章查标簽一般沒問題,一般都是根據标簽查文章的時候有性能問題,如果是純關系資料庫比如mysql很難解決性能問題,是以要借助 es 索引解決。es 索引的時候可以将 tagid 用逗号分隔,可以很快的根據一個 tagid,或者多個 tagid 查詢到關聯的文章 id,一般文章清單都是分頁的,有這些文章 id 了,再去關系資料庫裡面取文章就行了,但是es又是另外一件事了,回頭我們再讨論。
随後使用Django2.0.4來實作,首先安裝好python的mongodb操作庫pymongo
pip3 install pymongo
值得一提的是,它會有一個相對應bson子產品 也就是說 PyMongo子產品的實作是基于和它一起的bson子產品的。
bson是一種類json的一種二進制形式的存儲格式,簡稱Binary JSON,它和JSON一樣,支援内嵌的文檔對象和數組對象,但是BSON有JSON沒有的一些資料類型,如Date和BinData類型;BSON有三個特點:輕量性、可周遊性、高效性,但是空間使用率不是很理想。
基于Django插入标簽的視圖:
import pymongo
from bson import json_util as jsonb
mongo_client = pymongo.MongoClient(host='localhost', port=27017)
from django.http import HttpResponse,HttpResponseRedirect,JsonResponse
from django.views import View
class InsertTagsHandler(View):
def get(self,request):
db = mongo_client.test12
table = db.test12
res = table.find({"title":'123'}).count()
print(res)
if res > 0:
result = '重複資料'
return HttpResponse(json.dumps({'result':result},ensure_ascii=False))
else:
table.insert({'title':'123','desc':['123','123']})
return HttpResponse(json.dumps({'result':'添加成功'},ensure_ascii=False))
基于django通過文章查詢标簽
class FindArticleHandler(View):
def get(self,request):
db = mongo_client.test12
table = db.test12
res = table.find_one({"title":'123'},{"desc":1})
return HttpResponse(jsonb.dumps(res,ensure_ascii=False))
基于django分組查詢擷取所有标簽以及标簽出現次數的統計
class TagsStatHandler(View):
def get(self,request):
db = mongo_client.test12
table = db.test12
pipeline = [{'$unwind':"$tags"},{'$group': {'_id': "$tags", 'count': {'$sum': 1}}},]
res = table.aggregate(pipeline)
return HttpResponse(jsonb.dumps(res,ensure_ascii=False))
基于django通過标簽反查文章
class Tags2ArticleHandler(View):
def get(self,request):
db = mongo_client.test12
table = db.test12
res = table.find({"tags":{'$in':["123"]}})
return HttpResponse(jsonb.dumps(res,ensure_ascii=False))
結語:經此一役,Mongodb的特點躍然紙上:結構靈活,表結構更改相對自由,不用每次alter的時候付出代價,适合業務快速疊代,而且json原生和大多數的語言有天然的契合。還支援數組,嵌套文檔等資料類型。