電商網(wǎng)站技術(shù)要點剖析
商業(yè)模式
- B2B - 商家對商家址貌,交易雙方都是企業(yè)(商家)脚翘,最典型的案例就是阿里巴巴。
- C2C - 個人對個人炎咖,例如:淘寶赃泡、人人車寒波。
- B2C - 商家對個人,例如:唯品會升熊,聚美優(yōu)品俄烁。
- C2B - 個人對商家,先有消費者提出需求级野,后有商家按需求組織生產(chǎn)页屠,例如: 尚品宅配。
- O2O - 線上到線下勺阐,將線下的商務(wù)機會與互聯(lián)網(wǎng)結(jié)合卷中,讓互聯(lián)網(wǎng)成為線下交易的平臺矛双,例如:美團外賣渊抽、餓了么。
- B2B2C - 商家對商家對個人议忽,例如:天貓懒闷、京東。
需求要點
- 用戶端
首頁(商品分類栈幸、廣告輪播愤估、滾動快訊、瀑布加載速址、推薦玩焰、折扣、熱銷芍锚、……)
用戶(登錄(第三方登錄)昔园、注冊、注銷并炮、自服務(wù)(個人信息默刚、瀏覽歷史、收貨地址逃魄、……))
商品(分類荤西、列表、詳情伍俘、搜索邪锌、熱門搜索、搜索歷史癌瘾、添加到購物車觅丰、收藏、關(guān)注柳弄、……)
購物車(查看舶胀、編輯(修改數(shù)量概说、刪除商品、清空))
訂單(提交訂單(支付)嚣伐、歷史訂單糖赔、訂單詳情、訂單評價轩端、……)
- 管理端
- 核心業(yè)務(wù)實體的CRUD
- 定時任務(wù)(周期性和非周期性)
- 報表功能(Excel放典、PDF、ECharts)
- 權(quán)限控制(RBAC)
- 業(yè)務(wù)流轉(zhuǎn)(Activity基茵、Airflow奋构、Spiff、自定義)
- 三方服務(wù)(地圖拱层、短信弥臼、物流、支付根灯、實名認證径缅、天氣、監(jiān)控烙肺、……)
提示:可以通過思維導(dǎo)圖來進行需求的整理纳猪,思維導(dǎo)圖上的每個葉子節(jié)點都是不可再拆分的功能,而且都是動詞桃笙。
物理模型設(shè)計
兩個概念:SPU(Standard Product Unit)和SKU(Stock Keeping Unit)氏堤。
- SPU:iPhone 6s
- SKU:iPhone 6s 64G 土豪金
第三方登錄
第三方登錄是指利用第三方網(wǎng)站(通常是知名社交網(wǎng)站)的賬號進行登錄驗證,比如國內(nèi)的 QQ搏明、微博鼠锈,國外的Google、Facebook等熏瞄,第三方登錄大部分都是使用OAuth脚祟,它是一個關(guān)于授權(quán)的開放網(wǎng)絡(luò)標準,得到了廣泛的應(yīng)用强饮,目前通常使用的是2.0版本由桌。關(guān)于OAuth的基礎(chǔ)知識,可以閱讀阮一峰老師的《理解OAuth 2.0》邮丰。
OAuth 2.0授權(quán)流程
- 用戶打開客戶端以后行您,客戶端要求用戶(資源所有者)給予授權(quán)。
- 用戶(資源所有者)同意給予客戶端授權(quán)剪廉。
- 客戶端使用上一步獲得的授權(quán)娃循,向認證服務(wù)器申請訪問令牌。
- 認證服務(wù)器對客戶端進行認證以后斗蒋,發(fā)放訪問令牌捌斧。
- 客戶端使用訪問令牌向資源服務(wù)器申請獲取資源笛质。
- 資源服務(wù)器確認訪問令牌無誤,同意向客戶端開放資源捞蚂。
如果使用微博登錄進行接入妇押,其具體步驟可以參考微博開放平臺上的“微博登錄接入”文檔。使用QQ登錄進行接入姓迅,需要首先注冊成為QQ互聯(lián)開發(fā)者并通過審核敲霍,具體的步驟可以參考QQ互聯(lián)上的“接入指南”,具體的步驟可以參考“網(wǎng)站開發(fā)流程”丁存。
提示:在Gitbook上面有一本名為《Django博客入門》的書以Github為例介紹了第三方賬號登錄肩杈,有興趣的可以自行閱讀。
通常電商網(wǎng)站在使用第三方登錄時解寝,會要求與網(wǎng)站賬號進行綁定或者根據(jù)獲取到的第三方賬號信息(如:手機號)自動完成賬號綁定扩然。
緩存預(yù)熱和查詢緩存
緩存預(yù)熱
所謂緩存預(yù)熱,是指在啟動服務(wù)器時將數(shù)據(jù)提前加載到緩存中编丘,為此可以在Django應(yīng)用的apps.py
模塊中編寫AppConfig
的子類并重寫ready()
方法与学,代碼如下所示。
import pymysql
from django.apps import AppConfig
from django.core.cache import cache
SELECT_PROVINCE_SQL = 'select distid, name from tb_district where pid is null'
class CommonConfig(AppConfig):
name = 'common'
def ready(self):
conn = pymysql.connect(host='1.2.3.4', port=3306,
user='root', password='pass',
database='db', charset='utf8',
cursorclass=pymysql.cursors.DictCursor)
try:
with conn.cursor() as cursor:
cursor.execute(SELECT_PROVINCE_SQL)
provinces = cursor.fetchall()
cache.set('provinces', provinces)
finally:
conn.close()
接下來嘉抓,還需要在應(yīng)用的__init__.py
中編寫下面的代碼。
default_app_config = 'common.apps.CommonConfig'
或者在項目的settings.py
文件中注冊應(yīng)用晕窑。
INSTALLED_APPS = [
...
'common.apps.CommonConfig',
...
]
查詢緩存
自定義裝飾器實現(xiàn)查詢結(jié)果的緩存抑片。
from pickle import dumps
from pickle import loads
from django.core.cache import caches
MODEL_CACHE_KEY = 'project:modelcache:%s'
def my_model_cache(key, section='default', timeout=None):
"""實現(xiàn)模型緩存的裝飾器"""
def wrapper1(func):
def wrapper2(*args, **kwargs):
real_key = '%s:%s' % (MODEL_CACHE_KEY % key, ':'.join(map(str, args)))
serialized_data = caches[section].get(real_key)
if serialized_data:
data = loads(serialized_data)
else:
data = func(*args, **kwargs)
cache.set(real_key, dumps(data), timeout=timeout)
return data
return wrapper2
return wrapper1
@my_model_cache(key='provinces')
def get_all_provinces():
return list(Province.objects.all())
購物車實現(xiàn)
問題一:已登錄用戶的購物車放在哪里?未登錄用戶的購物車放在哪里杨赤?
class CartItem(object):
"""購物車中的商品項"""
def __init__(self, sku, amount=1, selected=False):
self.sku = sku
self.amount = amount
self.selected = selected
@property
def total(self):
return self.sku.price * self.amount
class ShoppingCart(object):
"""購物車"""
def __init__(self):
self.items = {}
self.index = 0
def add_item(self, item):
if item.sku.id in self.items:
self.items[item.sku.id].amount += item.amount
else:
self.items[item.sku.id] = item
def remove_item(self, sku_id):
if sku_id in self.items:
self.items.remove(sku_id)
def clear_all_items(self):
self.items.clear()
@property
def cart_items(self):
return self.items.values()
@property
def cart_total(self):
total = 0
for item in self.items.values():
total += item.total
return total
已登錄用戶的購物車可以放在數(shù)據(jù)庫中(可以先在Redis中緩存)敞斋;未登錄用戶的購物車可以保存在Cookie、localStorage或sessionStorage中(減少服務(wù)器端內(nèi)存開銷)疾牲。
{
'1001': {sku: {...}, 'amount': 1, 'selected': True},
'1002': {sku: {...}, 'amount': 2, 'selected': False},
'1003': {sku: {...}, 'amount': 3, 'selected': True},
}
request.get_signed_cookie('cart')
cart_base64 = base64.base64encode(pickle.dumps(cart))
response.set_signed_cookie('cart', cart_base64)
問題二:用戶登錄之后植捎,如何合并購物車?(目前電商應(yīng)用的購物車幾乎都做了持久化處理阳柔,主要是方便在多個終端之間共享數(shù)據(jù))
集成支付功能
問題一:支付信息如何持久化焰枢?(必須保證每筆交易都有記錄)
問題二:如何接入支付寶?(接入其他平臺基本類似)
[圖片上傳失敗...(image-6e4e8c-1554952013122)]
配置文件:
ALIPAY_APPID = '......'
ALIPAY_URL = 'https://openapi.alipaydev.com/gateway.do'
ALIPAY_DEBUG = False
獲得支付鏈接(發(fā)起支付):
# 創(chuàng)建調(diào)用支付寶的對象
alipay = AliPay(
# 在線創(chuàng)建應(yīng)用時分配的ID
appid=settings.ALIPAY_APPID,
app_notify_url=None,
# 自己應(yīng)用的私鑰
app_private_key_path=os.path.join(
os.path.dirname(os.path.abspath(__file__)),
'keys/app_private_key.pem'),
# 支付寶的公鑰
alipay_public_key_path=os.path.join(
os.path.dirname(os.path.abspath(__file__)),
'keys/alipay_public_key.pem'),
sign_type='RSA2',
debug=settings.ALIPAY_DEBUG
)
# 調(diào)用獲取支付頁面操作
order_info = alipay.api_alipay_trade_page_pay(
out_trade_no='...',
total_amount='...',
subject='...',
return_url='http://...'
)
# 生成完整的支付頁面URL
alipay_url = settings.ALIPAY_URL + '?' + order_info
return JsonResponse({'alipay_url': alipay_url})
通過上面返回的鏈接可以進入支付頁面避消,支付完成后會自動跳轉(zhuǎn)回上面代碼中設(shè)定好的項目頁面低滩,在該頁面中可以獲得訂單號(out_trade_no)召夹、支付流水號(trade_no)、交易金額(total_amount)和對應(yīng)的簽名(sign)并請求后端驗證和保存交易結(jié)果恕沫,代碼如下所示:
# 創(chuàng)建調(diào)用支付寶的對象
alipay = AliPay(
# 在線創(chuàng)建應(yīng)用時分配的ID
appid=settings.ALIPAY_APPID,
app_notify_url=None,
# 自己應(yīng)用的私鑰
app_private_key_path=os.path.join(
os.path.dirname(os.path.abspath(__file__)),
'keys/app_private_key.pem'),
# 支付寶的公鑰
alipay_public_key_path=os.path.join(
os.path.dirname(os.path.abspath(__file__)),
'keys/alipay_public_key.pem'),
sign_type='RSA2',
debug=settings.ALIPAY_DEBUG
)
# 請求參數(shù)(假設(shè)是POST請求)中包括訂單號戳鹅、支付流水號、交易金額和簽名
params = request.POST.dict()
# 調(diào)用驗證操作
if alipay.verify(params, params.pop('sign')):
# 對交易進行持久化操作
支付寶的支付API還提供了交易查詢昏兆、交易結(jié)算枫虏、退款、退款查詢等一系列的接口爬虱,可以根據(jù)業(yè)務(wù)需要進行調(diào)用隶债,此處不再進行贅述。
秒殺和超賣
- 秒殺:秒殺是通常意味著要在很短的時間處理極高的并發(fā)跑筝,系統(tǒng)在短時間需要承受平時百倍以上的流量死讹,因此秒殺架構(gòu)是一個比較復(fù)雜的問題,其核心思路是流量控制和性能優(yōu)化曲梗,需要從前端(通過JavaScript實現(xiàn)倒計時赞警、避免重復(fù)提交和限制頻繁刷新)到后臺各個環(huán)節(jié)的配合。流量控制主要是限制只有少部分流量進入服務(wù)后端(畢竟最終只有少部分用戶能夠秒殺成功)虏两,同時在物理架構(gòu)上使用緩存(一方面是因為讀操作多寫操作少愧旦;另外可以將庫存放在Redis中,利用DECR原語實現(xiàn)減庫存定罢;同時也可以利用Redis來進行限流笤虫,道理跟限制頻繁發(fā)送手機驗證碼是一樣的)和消息隊列(消息隊列最為重要的作用就是“削峰”和“上下游節(jié)點解耦合”)來進行優(yōu)化;此外還要采用無狀態(tài)服務(wù)設(shè)計祖凫,這樣才便于進行水平擴展(通過增加設(shè)備來為系統(tǒng)擴容)琼蚯。
- 超賣現(xiàn)象:比如某商品的庫存為1,此時用戶1和用戶2并發(fā)購買該商品惠况,用戶1提交訂單后該商品的庫存被修改為0遭庶,而此時用戶2并不知道的情況下提交訂單,該商品的庫存再次被修改為-1這就是超賣現(xiàn)象稠屠。解決超賣現(xiàn)象有三種常見的思路:
- 悲觀鎖控制:查詢商品數(shù)量的時候就用
select ... for update
對數(shù)據(jù)加鎖峦睡,這樣的話用戶1查詢庫存時,用戶2因無法讀取庫存數(shù)量被阻塞完箩,直到用戶1提交或者回滾了更新庫存的操作后才能繼續(xù)赐俗,從而解決了超賣問題。但是這種做法對并發(fā)訪問量很高的商品來說性能太過糟糕弊知,實際開發(fā)中可以在庫存小于某個值時才考慮加鎖阻逮,但是總的來說這種做法不太可取。 - 樂觀鎖控制:查詢商品數(shù)量不用加鎖秩彤,更新庫存的時候設(shè)定商品數(shù)量必須與之前查詢數(shù)量相同才能更新叔扼,否則說明其他事務(wù)已經(jīng)更新了庫存事哭,必須重新發(fā)出請求。這種做法要求事務(wù)隔離級別為可重復(fù)讀瓜富,否則仍然會產(chǎn)生問題鳍咱。
- 嘗試減庫存:將上面的查詢(
select
)和更新(update
)操作合并為一條SQL操作,更新庫存的時候与柑,在where
篩選條件中加上庫存>=購買數(shù)量
或庫存-購買數(shù)量>=0
的條件谤辜。
- 悲觀鎖控制:查詢商品數(shù)量的時候就用
提示:有興趣的可以自己在知乎上看看關(guān)于這類問題的討論。
靜態(tài)資源管理
靜態(tài)資源的管理可以自己架設(shè)文件服務(wù)器或者分布式文件服務(wù)器(FastDFS)价捧,但是一般的項目中沒有必要這樣做而且效果未必是最好的丑念,我們建議使用云存儲服務(wù)來管理網(wǎng)站的靜態(tài)資源,國內(nèi)外的云服務(wù)提供商如亞馬遜结蟋、阿里云脯倚、騰訊云、七牛嵌屎、LeanCloud推正、Bmob等都提供了非常優(yōu)質(zhì)的云存儲服務(wù),而且價格也是一般公司可以接受的宝惰≈查牛可以參考《在阿里云OSS上托管靜態(tài)網(wǎng)站》一文來完成對網(wǎng)站靜態(tài)資源的管理,代碼相關(guān)的內(nèi)容可以參考阿里云的對象存儲 OSS開發(fā)人員指南掌测。
全文檢索
方案選擇
- 使用數(shù)據(jù)庫的模糊查詢功能 - 效率低内贮,每次需要全表掃描,不支持分詞汞斧。
- 使用數(shù)據(jù)庫的全文檢索功能 - MySQL 5.6以前只適用于MyISAM引擎,檢索操作和其他的DML操作耦合在數(shù)據(jù)庫中什燕,可能導(dǎo)致檢索操作非常緩慢粘勒,數(shù)據(jù)量達到百萬級性能顯著下降,查詢時間很長屎即。
- 使用開源搜索引擎 - 索引數(shù)據(jù)和原始數(shù)據(jù)分離庙睡,可以使用ElasticSearch或Solr來提供外置索引服務(wù),如果不考慮高并發(fā)的全文檢索需求技俐,純Python的Whoosh也可以考慮乘陪。
ElasticSearch
ElasticSearch既是一個分布式文檔數(shù)據(jù)庫又是一個高可擴展的開源全文搜索和分析引擎,它允許存儲雕擂、搜索和分析大量的數(shù)據(jù)啡邑,并且這個過程是近實時的。它通常被用作底層引擎和技術(shù)井赌,為復(fù)雜的搜索功能和要求提供動力谤逼,大家熟知的維基百科贵扰、Stack-Overflow、Github都使用了ElasticSearch流部。
ElasticSearch的底層是開源搜索引擎Lucene戚绕,但是直接用Lucene會非常麻煩,必須自己編寫代碼去調(diào)用它的接口而且只支持Java語言枝冀。ElasticSearch相當于對Lucene進行了一次全面的封裝舞丛,提供了REST風(fēng)格的API接口,通過基于HTTP協(xié)議的訪問方式屏蔽了編程語言的差異果漾。ElasticSearch會為數(shù)據(jù)構(gòu)建倒排索引球切,但是ElasticSearch內(nèi)置的分詞器對中文分詞的支持幾乎為零,因此需要通過安裝elasticsearch-analysis-ik插件來提供中文分詞服務(wù)跨晴。
ElasticSearch的安裝和配置可以參考《ElasticSearch之Docker安裝》欧聘。除了ElasticSearch之外,也可以使用Solr端盆、Whoosh等來提供搜索引擎服務(wù)怀骤,基本上Django項目中可以考慮如下兩套方案:
- haystack(django-haystack / drf-haystack) + whoosh + Jieba
- haystack (django-haystack / drf-haystack)+ elasticsearch
安裝和使用ElasticSearch
-
使用Docker安裝ElasticSearch。
docker pull elasticsearch:6.5.3 docker run -d -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms512m -Xmx512m" --name es elasticsearch:6.5.3
說明:上面創(chuàng)建容器時通過
-e
參數(shù)指定了使用單機模式和Java虛擬機最小最大可用堆空間的大小焕妙,堆空間大小可以根據(jù)服務(wù)器實際能夠提供給ElasticSearch的內(nèi)存大小來決定蒋伦,默認為2G。 -
創(chuàng)建數(shù)據(jù)庫焚鹊。
請求:PUT -
http://1.2.3.4:9200/demo
響應(yīng):
{ "acknowledged": true, "shards_acknowledged": true, "index": "demo" }
-
查看創(chuàng)建的數(shù)據(jù)庫痕届。
請求:GET -
http://1.2.3.4:9200/demo
響應(yīng):
{ "demo": { "aliases": {}, "mappings": {}, "settings": { "index": { "creation_date": "1552213970199", "number_of_shards": "5", "number_of_replicas": "1", "uuid": "ny3rCn10SAmCsqW6xPP1gw", "version": { "created": "6050399" }, "provided_name": "demo" } } } }
-
插入數(shù)據(jù)。
請求:POST -
http://1.2.3.4:9200/demo/goods/1/
請求頭:Content-Type: application/json
參數(shù):
{ "no": "5089253", "title": "Apple iPhone X (A1865) 64GB 深空灰色 移動聯(lián)通電信4G手機", "brand": "Apple", "name": "Apple iPhone X", "product": "中國大陸", "resolution": "2436 x 1125", "intro": "一直以來末患,Apple都心存一個設(shè)想研叫,期待能夠打造出這樣一部iPhone:它有整面屏幕,能讓你在使用時璧针,完全沉浸其中嚷炉,仿佛忘了它的存在。它是如此智能探橱,哪怕輕輕一瞥申屹,都能得到它心有靈犀的回應(yīng)。而這個設(shè)想隧膏,終于隨著iPhone X的到來成為了現(xiàn)實』┘ィ現(xiàn)在,就跟未來見個面吧胞枕。" }
響應(yīng):
{ "_index": "demo", "_type": "goods", "_id": "1", "_version": 4, "result": "created", "_shards": { "total": 2, "successful": 1, "failed": 0 }, "_seq_no": 3, "_primary_term": 1 }
-
刪除數(shù)據(jù)杆煞。
請求:DELETE -
http://1.2.3.4:9200/demo/goods/1/
響應(yīng):
{ "_index": "demo", "_type": "goods", "_id": "1", "_version": 2, "result": "deleted", "_shards": { "total": 2, "successful": 1, "failed": 0 }, "_seq_no": 1, "_primary_term": 1 }
-
更新數(shù)據(jù)。
請求:PUT -
http://1.2.3.4:9200/demo/goods/1/_update
請求頭:Content-Type: application/json
參數(shù):
{ "doc": { "no": "5089253", "title": "Apple iPhone X (A1865) 64GB 深空灰色 移動聯(lián)通電信4G手機", "brand": "Apple(蘋果)", "name": "Apple iPhone X", "product": "美國", "resolution": "2436 x 1125", "intro": "一直以來,Apple都心存一個設(shè)想索绪,期待能夠打造出這樣一部iPhone:它有整面屏幕湖员,能讓你在使用時,完全沉浸其中瑞驱,仿佛忘了它的存在娘摔。它是如此智能,哪怕輕輕一瞥唤反,都能得到它心有靈犀的回應(yīng)凳寺。而這個設(shè)想,終于隨著iPhone X的到來成為了現(xiàn)實⊥蹋現(xiàn)在肠缨,就跟未來見個面吧。" } }
響應(yīng):
{ "_index": "demo", "_type": "goods", "_id": "1", "_version": 10, "result": "updated", "_shards": { "total": 2, "successful": 1, "failed": 0 }, "_seq_no": 9, "_primary_term": 1 }
-
查詢數(shù)據(jù)盏阶。
請求:GET -
http://1.2.3.4:9200/demo/goods/1/
響應(yīng):
{ "_index": "demo", "_type": "goods", "_id": "1", "_version": 10, "found": true, "_source": { "doc": { "no": "5089253", "title": "Apple iPhone X (A1865) 64GB 深空灰色 移動聯(lián)通電信4G手機", "brand": "Apple(蘋果)", "name": "Apple iPhone X", "product": "美國", "resolution": "2436 x 1125", "intro": "一直以來晒奕,Apple都心存一個設(shè)想,期待能夠打造出這樣一部iPhone:它有整面屏幕名斟,能讓你在使用時脑慧,完全沉浸其中,仿佛忘了它的存在砰盐。它是如此智能闷袒,哪怕輕輕一瞥,都能得到它心有靈犀的回應(yīng)岩梳。而這個設(shè)想囊骤,終于隨著iPhone X的到來成為了現(xiàn)實。現(xiàn)在冀值,就跟未來見個面吧也物。" } } }
配置中文分詞和拼音插件
-
進入Docker容器的plugins目錄。
docker exec -it es /bin/bash
-
下載和ElasticSearch版本對應(yīng)的ik和pinyin插件列疗。
cd plugins/ mkdir ik cd ik wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.5.3/elasticsearch-analysis-ik-6.5.3.zip unzip elasticsearch-analysis-ik-6.5.3.zip rm -f elasticsearch-analysis-ik-6.5.3.zip cd .. mkdir pinyin cd pinyin wget https://github.com/medcl/elasticsearch-analysis-pinyin/releases/download/v6.5.3/elasticsearch-analysis-pinyin-6.5.3.zip unzip elasticsearch-analysis-pinyin-6.5.3.zip rm -f elasticsearch-analysis-pinyin-6.5.3.zip
-
退出容器焦除,重啟ElasticSearch。
docker restart es
-
測試中文分詞效果作彤。
請求:POST -
http://1.2.3.4:9200/_analyze
請求頭:Content-Type: application/json
參數(shù):
{ "analyzer": "ik_smart", "text": "中國男足在2022年卡塔爾世界杯預(yù)選賽中勇奪小組最后一名" }
響應(yīng):
{ "tokens": [ { "token": "中國", "start_offset": 0, "end_offset": 2, "type": "CN_WORD", "position": 0 }, { "token": "男足", "start_offset": 2, "end_offset": 4, "type": "CN_WORD", "position": 1 }, { "token": "在", "start_offset": 4, "end_offset": 5, "type": "CN_CHAR", "position": 2 }, { "token": "2022年", "start_offset": 5, "end_offset": 10, "type": "TYPE_CQUAN", "position": 3 }, { "token": "卡塔爾", "start_offset": 10, "end_offset": 13, "type": "CN_WORD", "position": 4 }, { "token": "世界杯", "start_offset": 13, "end_offset": 16, "type": "CN_WORD", "position": 5 }, { "token": "預(yù)選賽", "start_offset": 16, "end_offset": 19, "type": "CN_WORD", "position": 6 }, { "token": "中", "start_offset": 19, "end_offset": 20, "type": "CN_CHAR", "position": 7 }, { "token": "勇奪", "start_offset": 20, "end_offset": 22, "type": "CN_WORD", "position": 8 }, { "token": "小組", "start_offset": 22, "end_offset": 24, "type": "CN_WORD", "position": 9 }, { "token": "最后", "start_offset": 24, "end_offset": 26, "type": "CN_WORD", "position": 10 }, { "token": "一名", "start_offset": 26, "end_offset": 28, "type": "CN_WORD", "position": 11 } ] }
-
測試拼音分詞效果。
請求:POST -
http://1.2.3.4:9200/_analyze
請求頭:Content-Type: application/json
參數(shù):
響應(yīng):
{ "tokens": [ { "token": "zhang", "start_offset": 0, "end_offset": 0, "type": "word", "position": 0 }, { "token": "zxy", "start_offset": 0, "end_offset": 0, "type": "word", "position": 0 }, { "token": "xue", "start_offset": 0, "end_offset": 0, "type": "word", "position": 1 }, { "token": "you", "start_offset": 0, "end_offset": 0, "type": "word", "position": 2 } ] }
全文檢索功能
可以通過GET或者POST請求進行搜索乌逐,下面演示了搜索有“未來”關(guān)鍵詞商品竭讳。
-
GET -
http://120.77.222.217:9200/demo/goods/_search?q=未來
注意:URL中的中文應(yīng)該要處理成百分號編碼。
{ "took": 19, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 2, "max_score": 0.73975396, "hits": [ { "_index": "demo", "_type": "goods", "_id": "1", "_score": 0.73975396, "_source": { "doc": { "no": "5089253", "title": "Apple iPhone X (A1865) 64GB 深空灰色 移動聯(lián)通電信4G手機", "brand": "Apple(蘋果)", "name": "Apple iPhone X", "product": "美國", "resolution": "2436*1125", "intro": "一直以來浙踢,Apple都心存一個設(shè)想绢慢,期待能夠打造出這樣一部iPhone:它有整面屏幕,能讓你在使用時,完全沉浸其中胰舆,仿佛忘了它的存在骚露。它是如此智能,哪怕輕輕一瞥缚窿,都能得到它心有靈犀的回應(yīng)棘幸。而這個設(shè)想,終于隨著iPhone X的到來成為了現(xiàn)實【肓悖現(xiàn)在误续,就跟未來見個面吧。" } } }, { "_index": "demo", "_type": "goods", "_id": "3", "_score": 0.68324494, "_source": { "no": "42417956432", "title": "小米9 透明尊享版 手機 透明尊享 全網(wǎng)通(12GB + 256GB)", "brand": "小米(MI)", "name": "小米(MI)小米9透明", "product": "中國大陸", "resolution": "2340*1080", "intro": "全面透明機身扫茅,獨特科幻機甲風(fēng)蹋嵌,來自未來的設(shè)計。" } } ] } }
URL中可用的搜索參數(shù)如下表所示:
參數(shù) 說明 q 查詢字符串 analyzer 分析查詢字符串使用的分詞器 analyze_wildcard 通配符或者前綴查詢是否被分析葫隙,默認為false default_operator 多個條件之間的關(guān)系栽烂,默認為OR,可以修改為AND explain 在返回的結(jié)果中包含評分機制的解釋 fields 只返回索引中指定的列恋脚,多個列中間用逗號隔開 sort 排序參考的字段腺办,可以用:asc和:desc來指定升序和降序 timeout 超時時間 from 匹配結(jié)果的開始值,默認為0 size 匹配結(jié)果的條數(shù)慧起,默認為10 -
POST -
http://120.77.222.217:9200/demo/goods/_search
請求頭:Content-Type: application/json
參數(shù):
響應(yīng):
Django對接ElasticSearch
Python對接ElasticSearch的第三方庫是HayStack菇晃,在Django項目中可以使用django-haystack,通過HayStack可以在不修改代碼對接多種搜索引擎服務(wù)蚓挤。
pip install django-haystack elasticsearch
配置文件:
INSTALLED_APPS = [
...
'haystack',
...
]
HAYSTACK_CONNECTIONS = {
'default': {
# 引擎配置
'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
# 搜索引擎服務(wù)的URL
'URL': 'http://1.2.3.4:9200',
# 索引庫的名稱
'INDEX_NAME': 'goods',
},
}
# 添加/刪除/更新數(shù)據(jù)時自動生成索引
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
索引類:
from haystack import indexes
class GoodsIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
def get_model(self):
return Goods
def index_queryset(self, using=None):
return self.get_model().objects.all()
編輯text字段的模板(需要放在templates/search/indexes/demo/goods_text.txt):
{{object.title}}
{{object.intro}}
配置URL:
urlpatterns = [
# ...
url('search/', include('haystack.urls')),
]
生成初始索引:
python manage.py rebuild_index
說明:可以參考《Django Haystack 全文檢索與關(guān)鍵詞高亮》一文來更深入的了解基于Haystack的全文檢索操作磺送。