1. B2B - 商家对商家,交易双方都是企业(商家),最典型的案例就是阿里巴巴。
    2. C2C - 个人对个人,例如:淘宝、人人车。
    3. B2C - 商家对个人,例如:唯品会,聚美优品。
    4. C2B - 个人对商家,先有消费者提出需求,后有商家按需求组织生产,例如: 尚品宅配。
    5. O2O - 线上到线下,将线下的商务机会与互联网结合,让互联网成为线下交易的平台,例如:美团外卖、饿了么。
    6. B2B2C - 商家对商家对个人,例如:天猫、京东。

    需求要点

      • 首页(商品分类、广告轮播、滚动快讯、瀑布加载、推荐、折扣、热销、……)

      • 用户(登录(第三方登录)、注册、注销、自服务(个人信息、浏览历史、收货地址、……))

      • 商品(分类、列表、详情、搜索、热门搜索、搜索历史、添加到购物车、收藏、关注、评论、……)

      • 购物车(查看、编辑(修改数量、删除商品、清空))
      • 订单(提交订单(支付)、历史订单、订单详情、订单评价、……)
    1. 管理端
      • 核心业务实体的CRUD
      • 定时任务(周期性和非周期性,如处理未支付订单、采集数据对异常事件报警、……)
      • 报表功能(导入导出Excel、PDF等以及前端ECharts统计图表展示)
      • 权限控制(RBAC、白名单、黑名单、……)
      • 业务流转(如发起退款流程,常用流程引擎有:Activity、Airflow、Spiff等)
      • 三方服务(接入地图、短信、物流、支付、实名认证、天气、监控、云存储、……)

    物理模型设计

    首先要搞清楚两个概念:SPU(Standard Product Unit)和SKU(Stock Keeping Unit)。

    • SPU:iPhone 6s
    • SKU:iPhone 6s 64G 土豪金

    第三方登录是指利用第三方网站(通常是知名社交网站)的账号进行登录验证(主要是通过知名第三方网站获取到用户相关信息),比如国内的 QQ、微博,国外的Google、Facebook等。第三方登录大部分都是使用协议,它是一个关于授权的开放网络标准(数据的所有者告诉系统,同意授权第三方应用进入系统,获取这些数据。系统从而产生一个短期的进入令牌,用来代替密码,供第三方应用使用),得到了广泛的应用,目前通常使用的是2.0版本。关于OAuth的基础知识,可以阅读阮一峰老师的《理解OAuth 2.0》。关于令牌密码的区别,我们可以简单总结出以下三点差异:

    1. 令牌是短期的,到期会自动失效,用户自己无法修改。密码一般长期有效,用户不修改,就不会发生变化。
    2. 令牌可以被数据所有者撤销,会立即失效。以上例而言,屋主可以随时取消快递员的令牌。密码一般不允许被他人撤销。
    3. 令牌有权限范围(scope),比如只能进小区的二号门。对于网络服务来说,只读令牌就比读写令牌更安全。密码一般是完整权限。

    所以,通过令牌既可以让第三方应用获得权限,同时又随时可控,不会危及系统安全。这就是OAuth协议的优势。

    OAuth 2.0授权流程

    1. 用户打开客户端以后,客户端要求用户(资源所有者)给予授权。
    2. 用户(资源所有者)同意给予客户端授权。
    3. 客户端使用上一步获得的授权,向认证服务器申请访问令牌。
    4. 认证服务器对客户端进行认证以后,发放访问令牌。
    5. 客户端使用访问令牌向资源服务器申请获取资源。
    6. 资源服务器确认访问令牌无误,同意向客户端开放资源。

    第97天 - 电商网站技术要点剖析 - 图1

    如果使用微博登录进行接入,其具体步骤可以参考微博开放平台上的文档。使用QQ登录进行接入,需要首先注册成为QQ互联开发者并通过审核,具体的步骤可以参考QQ互联上的“接入指南”,具体的步骤可以参考。

    通常电商网站在使用第三方登录时,会要求与网站账号进行绑定或者根据获取到的第三方账号信息(如:手机号)自动完成账号绑定。

    缓存预热和查询缓存

    缓存预热

    所谓缓存预热,是指在启动服务器时将数据提前加载到缓存中,为此可以在Django应用的模块中编写AppConfig的子类并重写ready()方法,代码如下所示。

    接下来,还需要在应用的__init__.py中编写下面的代码。

    1. default_app_config = 'common.apps.CommonConfig'

    或者在项目的settings.py文件中注册应用。

    1. INSTALLED_APPS = [
    2. ...
    3. 'common.apps.CommonConfig',
    4. ...
    5. ]

    查询缓存

    自定义装饰器实现查询结果的缓存。

    1. from pickle import dumps, loads
    2. from django.core.cache import caches
    3. MODEL_CACHE_KEY = 'project:modelcache:%s'
    4. def my_model_cache(key, section='default', timeout=None):
    5. """实现模型缓存的装饰器"""
    6. def wrapper1(func):
    7. def wrapper2(*args, **kwargs):
    8. real_key = '%s:%s' % (MODEL_CACHE_KEY % key, ':'.join(map(str, args)))
    9. serialized_data = caches[section].get(real_key)
    10. if serialized_data:
    11. data = loads(serialized_data)
    12. else:
    13. data = func(*args, **kwargs)
    14. cache.set(real_key, dumps(data), timeout=timeout)
    15. return data
    16. return wrapper2
    17. return wrapper1
    1. @my_model_cache(key='provinces')
    2. def get_all_provinces():
    3. return list(Province.objects.all())

    购物车实现

    问题一:已登录用户的购物车放在哪里?未登录用户的购物车放在哪里?

    1. class CartItem(object):
    2. """购物车中的商品项"""
    3. def __init__(self, sku, amount=1, selected=False):
    4. self.sku = sku
    5. self.amount = amount
    6. self.selected = selected
    7. @property
    8. def total(self):
    9. return self.sku.price * self.amount
    10. class ShoppingCart(object):
    11. """购物车"""
    12. def __init__(self):
    13. self.items = {}
    14. self.index = 0
    15. def add_item(self, item):
    16. if item.sku.id in self.items:
    17. self.items[item.sku.id].amount += item.amount
    18. else:
    19. self.items[item.sku.id] = item
    20. def remove_item(self, sku_id):
    21. if sku_id in self.items:
    22. self.items.remove(sku_id)
    23. def clear_all_items(self):
    24. self.items.clear()
    25. @property
    26. def cart_items(self):
    27. return self.items.values()
    28. @property
    29. def cart_total(self):
    30. total = 0
    31. for item in self.items.values():
    32. total += item.total
    33. return total

    已登录用户的购物车可以放在数据库中(可以先在Redis中缓存);未登录用户的购物车可以保存在Cookie、localStorage或sessionStorage中(减少服务器端内存开销)。

    1. {
    2. '1001': {sku: {...}, 'amount': 1, 'selected': True},
    3. '1002': {sku: {...}, 'amount': 2, 'selected': False},
    4. '1003': {sku: {...}, 'amount': 3, 'selected': True},
    5. }
    1. request.get_signed_cookie('cart')
    2. cart_base64 = base64.base64encode(pickle.dumps(cart))
    3. response.set_signed_cookie('cart', cart_base64)

    问题二:用户登录之后,如何合并购物车?(目前电商应用的购物车几乎都做了持久化处理,主要是方便在多个终端之间共享数据)

    问题一:支付信息如何持久化?(必须保证每笔交易都有记录)

    问题二:如何接入支付宝?(接入其他平台基本类似)

    1. 蚂蚁金服开放平台
    2. 开发者中心
    3. SDK集成 - 。
    4. API列表

    配置文件:

    1. ALIPAY_APPID = '......'
    2. ALIPAY_URL = 'https://openapi.alipaydev.com/gateway.do'
    3. ALIPAY_DEBUG = False

    获得支付链接(发起支付):

    1. # 创建调用支付宝的对象
    2. alipay = AliPay(
    3. # 在线创建应用时分配的ID
    4. appid=settings.ALIPAY_APPID,
    5. app_notify_url=None,
    6. # 自己应用的私钥
    7. app_private_key_path=os.path.join(
    8. os.path.dirname(os.path.abspath(__file__)),
    9. 'keys/app_private_key.pem'),
    10. # 支付宝的公钥
    11. alipay_public_key_path=os.path.join(
    12. os.path.dirname(os.path.abspath(__file__)),
    13. 'keys/alipay_public_key.pem'),
    14. sign_type='RSA2',
    15. debug=settings.ALIPAY_DEBUG
    16. )
    17. # 调用获取支付页面操作
    18. order_info = alipay.api_alipay_trade_page_pay(
    19. out_trade_no='...',
    20. total_amount='...',
    21. subject='...',
    22. return_url='http://...'
    23. )
    24. # 生成完整的支付页面URL
    25. alipay_url = settings.ALIPAY_URL + '?' + order_info
    26. return JsonResponse({'alipay_url': alipay_url})

    通过上面返回的链接可以进入支付页面,支付完成后会自动跳转回上面代码中设定好的项目页面,在该页面中可以获得订单号(out_trade_no)、支付流水号(trade_no)、交易金额(total_amount)和对应的签名(sign)并请求后端验证和保存交易结果,代码如下所示:

    1. # 创建调用支付宝的对象
    2. alipay = AliPay(
    3. # 在线创建应用时分配的ID
    4. appid=settings.ALIPAY_APPID,
    5. app_notify_url=None,
    6. # 自己应用的私钥
    7. app_private_key_path=os.path.join(
    8. os.path.dirname(os.path.abspath(__file__)),
    9. 'keys/app_private_key.pem'),
    10. # 支付宝的公钥
    11. alipay_public_key_path=os.path.join(
    12. os.path.dirname(os.path.abspath(__file__)),
    13. 'keys/alipay_public_key.pem'),
    14. debug=settings.ALIPAY_DEBUG
    15. )
    16. # 请求参数(假设是POST请求)中包括订单号、支付流水号、交易金额和签名
    17. params = request.POST.dict()
    18. # 调用验证操作
    19. if alipay.verify(params, params.pop('sign')):
    20. # 对交易进行持久化操作

    支付宝的支付API还提供了交易查询、交易结算、退款、退款查询等一系列的接口,可以根据业务需要进行调用,此处不再进行赘述。

    秒杀和超卖

    1. 秒杀:秒杀是通常意味着要在很短的时间处理极高的并发,系统在短时间需要承受平时百倍以上的流量,因此秒杀架构是一个比较复杂的问题,其核心思路是流量控制和性能优化,需要从前端(通过JavaScript实现倒计时、避免重复提交和限制频繁刷新)到后台各个环节的配合。流量控制主要是限制只有少部分流量进入服务后端(毕竟最终只有少部分用户能够秒杀成功),同时在物理架构上使用缓存(一方面是因为读操作多写操作少;另外可以将库存放在Redis中,利用DECR原语实现减库存;同时也可以利用Redis来进行限流,道理跟限制频繁发送手机验证码是一样的)和消息队列(消息队列最为重要的作用就是“削峰”和“上下游节点解耦合”)来进行优化;此外还要采用无状态服务设计,这样才便于进行水平扩展(通过增加设备来为系统扩容)。
    2. 超卖现象:比如某商品的库存为1,此时用户1和用户2并发购买该商品,用户1提交订单后该商品的库存被修改为0,而此时用户2并不知道的情况下提交订单,该商品的库存再次被修改为-1这就是超卖现象。解决超卖现象有三种常见的思路:
      • 悲观锁控制:查询商品数量的时候就用select ... for update对数据加锁,这样的话用户1查询库存时,用户2因无法读取库存数量被阻塞,直到用户1提交或者回滚了更新库存的操作后才能继续,从而解决了超卖问题。但是这种做法对并发访问量很高的商品来说性能太过糟糕,实际开发中可以在库存小于某个值时才考虑加锁,但是总的来说这种做法不太可取。
      • 乐观锁控制:查询商品数量不用加锁,更新库存的时候设定商品数量必须与之前查询数量相同才能更新,否则说明其他事务已经更新了库存,必须重新发出请求。
      • 尝试减库存:将上面的查询(select)和更新(update)操作合并为一条SQL操作,更新库存的时候,在where筛选条件中加上库存>=购买数量库存-购买数量>=0的条件,这种做法要求事务隔离级别为读提交(read committed)。

    静态资源管理

    静态资源的管理可以自己架设文件服务器或者分布式文件服务器(FastDFS),但是一般的项目中没有必要这样做而且效果未必是最好的,我们建议使用云存储服务来管理网站的静态资源,国内外的云服务提供商如、阿里云、、LeanCloud、等都提供了非常优质的云存储服务,而且价格也是一般公司可以接受的,具体的操作可以参考官方文档,例如:阿里云的对象存储 OSS开发人员指南

    方案选择

    1. 使用数据库的模糊查询功能 - 效率低,每次需要全表扫描,不支持分词。
    2. 使用数据库的全文检索功能 - MySQL 5.6以前只适用于MyISAM引擎,检索操作和其他的DML操作耦合在数据库中,可能导致检索操作非常缓慢,数据量达到百万级性能显著下降,查询时间很长。
    3. 使用开源搜索引擎 - 索引数据和原始数据分离,可以使用ElasticSearch或Solr来提供外置索引服务,如果不考虑高并发的全文检索需求,纯Python的Whoosh也可以考虑。

    ElasticSearch

    ElasticSearch的底层是开源搜索引擎,但是直接用Lucene会非常麻烦,必须自己编写代码去调用它的接口而且只支持Java语言。ElasticSearch相当于对Lucene进行了一次全面的封装,提供了REST风格的API接口,通过基于HTTP协议的访问方式屏蔽了编程语言的差异。ElasticSearch会为数据构建倒排索引,但是ElasticSearch内置的分词器对中文分词的支持几乎为零,因此需要通过安装elasticsearch-analysis-ik插件来提供中文分词服务。

    ElasticSearch的安装和配置可以参考。除了ElasticSearch之外,也可以使用Solr、Whoosh等来提供搜索引擎服务,基本上Django项目中可以考虑如下几种方案:

    • haystack(django-haystack / drf-haystack) + whoosh + Jieba
    • haystack (django-haystack / drf-haystack)+ elasticsearch
    • requests + elasticsearch
    • django-elasticsearch-dsl

    安装和使用ElasticSearch

    1. 使用Docker安装ElasticSearch。

    2. 创建数据库。

      请求:PUT - http://1.2.3.4:9200/demo/

      响应:

      1. {
      2. "acknowledged": true,
      3. "shards_acknowledged": true,
      4. "index": "demo"
      5. }
    3. 查看创建的数据库。

      请求:GET - http://1.2.3.4:9200/demo/

      响应:

      1. {
      2. "demo": {
      3. "aliases": {},
      4. "mappings": {},
      5. "settings": {
      6. "index": {
      7. "creation_date": "1552213970199",
      8. "number_of_shards": "5",
      9. "number_of_replicas": "1",
      10. "version": {
      11. "created": "6050399"
      12. },
      13. "provided_name": "demo"
      14. }
      15. }
      16. }
      17. }
    4. 插入数据。

      请求:POST - http://1.2.3.4:9200/demo/goods/1/

      请求头:Content-Type: application/json

      参数:

      1. {
      2. "no": "5089253",
      3. "title": "Apple iPhone X (A1865) 64GB 深空灰色 移动联通电信4G手机",
      4. "brand": "Apple",
      5. "name": "Apple iPhone X",
      6. "product": "中国大陆",
      7. "resolution": "2436 x 1125",
      8. "intro": "一直以来,Apple都心存一个设想,期待能够打造出这样一部iPhone:它有整面屏幕,能让你在使用时,完全沉浸其中,仿佛忘了它的存在。它是如此智能,哪怕轻轻一瞥,都能得到它心有灵犀的回应。而这个设想,终于随着iPhone X的到来成为了现实。现在,就跟未来见个面吧。"
      9. }

      响应:

      1. {
      2. "_index": "demo",
      3. "_type": "goods",
      4. "_id": "1",
      5. "_version": 4,
      6. "result": "created",
      7. "_shards": {
      8. "total": 2,
      9. "successful": 1,
      10. "failed": 0
      11. },
      12. "_seq_no": 3,
      13. "_primary_term": 1
      14. }
    5. 删除数据。

      请求:DELETE - http://1.2.3.4:9200/demo/goods/1/

      响应:

      1. {
      2. "_index": "demo",
      3. "_type": "goods",
      4. "_id": "1",
      5. "_version": 2,
      6. "result": "deleted",
      7. "_shards": {
      8. "total": 2,
      9. "successful": 1,
      10. "failed": 0
      11. },
      12. "_seq_no": 1,
      13. "_primary_term": 1
      14. }
    6. 更新数据。

      请求:PUT - http://1.2.3.4:9200/demo/goods/1/_update

      请求头:Content-Type: application/json

      参数:

      1. {
      2. "doc": {
      3. "no": "5089253",
      4. "title": "Apple iPhone X (A1865) 64GB 深空灰色 移动联通电信4G手机",
      5. "brand": "Apple(苹果)",
      6. "name": "Apple iPhone X",
      7. "product": "美国",
      8. "resolution": "2436 x 1125",
      9. "intro": "一直以来,Apple都心存一个设想,期待能够打造出这样一部iPhone:它有整面屏幕,能让你在使用时,完全沉浸其中,仿佛忘了它的存在。它是如此智能,哪怕轻轻一瞥,都能得到它心有灵犀的回应。而这个设想,终于随着iPhone X的到来成为了现实。现在,就跟未来见个面吧。"
      10. }
      11. }

      响应:

      1. {
      2. "_index": "demo",
      3. "_type": "goods",
      4. "_id": "1",
      5. "_version": 10,
      6. "result": "updated",
      7. "_shards": {
      8. "total": 2,
      9. "successful": 1,
      10. "failed": 0
      11. },
      12. "_seq_no": 9,
      13. "_primary_term": 1
      14. }
    7. 查询数据。

      请求:GET - http://1.2.3.4:9200/demo/goods/1/

      响应:

      1. {
      2. "_index": "demo",
      3. "_type": "goods",
      4. "_id": "1",
      5. "_version": 10,
      6. "found": true,
      7. "_source": {
      8. "doc": {
      9. "no": "5089253",
      10. "title": "Apple iPhone X (A1865) 64GB 深空灰色 移动联通电信4G手机",
      11. "brand": "Apple(苹果)",
      12. "name": "Apple iPhone X",
      13. "product": "美国",
      14. "resolution": "2436 x 1125",
      15. "intro": "一直以来,Apple都心存一个设想,期待能够打造出这样一部iPhone:它有整面屏幕,能让你在使用时,完全沉浸其中,仿佛忘了它的存在。它是如此智能,哪怕轻轻一瞥,都能得到它心有灵犀的回应。而这个设想,终于随着iPhone X的到来成为了现实。现在,就跟未来见个面吧。"
      16. }
      17. }
      18. }

    配置中文分词和拼音插件

    1. 进入Docker容器的plugins目录。

      1. docker exec -it es /bin/bash
      1. yum install -y wget
      2. cd plugins/
      3. mkdir ik
      4. cd ik
      5. wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.6.0/elasticsearch-analysis-ik-7.6.0.zip
      6. unzip elasticsearch-analysis-ik-7.6.0.zip
      7. rm -f elasticsearch-analysis-ik-7.6.0.zip
      8. cd ..
      9. mkdir pinyin
      10. cd pinyin
      11. wget https://github.com/medcl/elasticsearch-analysis-pinyin/releases/download/v7.6.0/elasticsearch-analysis-pinyin-7.6.0.zip
      12. unzip elasticsearch-analysis-pinyin-7.6.0.zip
      13. rm -f elasticsearch-analysis-pinyin-7.6.0.zip
    2. 退出容器,重启ElasticSearch。

    3. 测试中文分词效果。

      请求:POST - http://1.2.3.4:9200/_analyze

      请求头:Content-Type: application/json

      参数:

      1. {
      2. "analyzer": "ik_smart",
      3. "text": "中国男足在2022年卡塔尔世界杯预选赛中勇夺小组最后一名"
      4. }

      响应:

      1. {
      2. "tokens": [
      3. {
      4. "token": "中国",
      5. "start_offset": 0,
      6. "end_offset": 2,
      7. "type": "CN_WORD",
      8. "position": 0
      9. },
      10. {
      11. "token": "男足",
      12. "start_offset": 2,
      13. "end_offset": 4,
      14. "type": "CN_WORD",
      15. "position": 1
      16. },
      17. {
      18. "token": "在",
      19. "start_offset": 4,
      20. "end_offset": 5,
      21. "type": "CN_CHAR",
      22. "position": 2
      23. },
      24. {
      25. "token": "2022年",
      26. "start_offset": 5,
      27. "end_offset": 10,
      28. "type": "TYPE_CQUAN",
      29. "position": 3
      30. },
      31. {
      32. "token": "卡塔尔",
      33. "end_offset": 13,
      34. "type": "CN_WORD",
      35. "position": 4
      36. },
      37. {
      38. "token": "世界杯",
      39. "start_offset": 13,
      40. "end_offset": 16,
      41. "type": "CN_WORD",
      42. "position": 5
      43. },
      44. {
      45. "token": "预选赛",
      46. "start_offset": 16,
      47. "end_offset": 19,
      48. "type": "CN_WORD",
      49. "position": 6
      50. },
      51. {
      52. "start_offset": 19,
      53. "end_offset": 20,
      54. "type": "CN_CHAR",
      55. "position": 7
      56. },
      57. {
      58. "token": "勇夺",
      59. "start_offset": 20,
      60. "end_offset": 22,
      61. "type": "CN_WORD",
      62. "position": 8
      63. },
      64. {
      65. "token": "小组",
      66. "start_offset": 22,
      67. "end_offset": 24,
      68. "type": "CN_WORD",
      69. "position": 9
      70. },
      71. {
      72. "token": "最后",
      73. "start_offset": 24,
      74. "end_offset": 26,
      75. "type": "CN_WORD",
      76. "position": 10
      77. },
      78. {
      79. "token": "一名",
      80. "start_offset": 26,
      81. "end_offset": 28,
      82. "type": "CN_WORD",
      83. "position": 11
      84. }
      85. ]
      86. }
    4. 测试拼音分词效果。

      请求:POST - http://1.2.3.4:9200/_analyze

      请求头:Content-Type: application/json

      参数:

      1. {
      2. "analyzer": "pinyin",
      3. "text": "张学友"
      4. }

      响应:

      1. {
      2. "tokens": [
      3. {
      4. "token": "zhang",
      5. "start_offset": 0,
      6. "end_offset": 0,
      7. "type": "word",
      8. "position": 0
      9. },
      10. {
      11. "token": "zxy",
      12. "start_offset": 0,
      13. "end_offset": 0,
      14. "type": "word",
      15. "position": 0
      16. },
      17. {
      18. "token": "xue",
      19. "start_offset": 0,
      20. "end_offset": 0,
      21. "type": "word",
      22. "position": 1
      23. },
      24. {
      25. "token": "you",
      26. "start_offset": 0,
      27. "end_offset": 0,
      28. "type": "word",
      29. "position": 2
      30. }
      31. ]
      32. }

    全文检索功能

    可以通过GET或者POST请求进行搜索,下面演示了搜索有“未来”关键词商品。

    1. GET - http://120.77.222.217:9200/demo/goods/_search?q=未来

      1. {
      2. "took": 19,
      3. "timed_out": false,
      4. "_shards": {
      5. "total": 5,
      6. "successful": 5,
      7. "skipped": 0,
      8. "failed": 0
      9. },
      10. "hits": {
      11. "total": 2,
      12. "max_score": 0.73975396,
      13. "hits": [
      14. {
      15. "_index": "demo",
      16. "_type": "goods",
      17. "_id": "1",
      18. "_score": 0.73975396,
      19. "_source": {
      20. "doc": {
      21. "no": "5089253",
      22. "title": "Apple iPhone X (A1865) 64GB 深空灰色 移动联通电信4G手机",
      23. "brand": "Apple(苹果)",
      24. "name": "Apple iPhone X",
      25. "product": "美国",
      26. "resolution": "2436*1125",
      27. "intro": "一直以来,Apple都心存一个设想,期待能够打造出这样一部iPhone:它有整面屏幕,能让你在使用时,完全沉浸其中,仿佛忘了它的存在。它是如此智能,哪怕轻轻一瞥,都能得到它心有灵犀的回应。而这个设想,终于随着iPhone X的到来成为了现实。现在,就跟未来见个面吧。"
      28. }
      29. }
      30. },
      31. {
      32. "_index": "demo",
      33. "_type": "goods",
      34. "_id": "3",
      35. "_score": 0.68324494,
      36. "_source": {
      37. "no": "42417956432",
      38. "title": "小米9 透明尊享版 手机 透明尊享 全网通(12GB + 256GB)",
      39. "brand": "小米(MI)",
      40. "name": "小米(MI)小米9透明",
      41. "product": "中国大陆",
      42. "resolution": "2340*1080",
      43. "intro": "全面透明机身,独特科幻机甲风,来自未来的设计。"
      44. }
      45. }
      46. ]
      47. }
      48. }

      URL中可用的搜索参数如下表所示:

      | 参数 | 说明 | | ———————— | ————————————————————————- | | q | 查询字符串 | | analyzer | 分析查询字符串使用的分词器 | | analyze_wildcard | 通配符或者前缀查询是否被分析,默认为false | | default_operator | 多个条件之间的关系,默认为OR,可以修改为AND | | explain | 在返回的结果中包含评分机制的解释 | | fields | 只返回索引中指定的列,多个列中间用逗号隔开 | | sort | 排序参考的字段,可以用:asc和:desc来指定升序和降序 | | timeout | 超时时间 | | from | 匹配结果的开始值,默认为0 | | size | 匹配结果的条数,默认为10 |

    2. POST - http://120.77.222.217:9200/demo/goods/_search

      请求头:Content-Type: application/json

      参数:

      1. {
      2. "query": {
      3. "term": {
      4. "type": ""
      5. }
      6. }
      7. }

      POST搜索是基于DSL的。

    Django对接ElasticSearch

    Python对接ElasticSearch的第三方库是HayStack,在Django项目中可以使用django-haystack,通过HayStack可以在不修改代码对接多种搜索引擎服务。

    1. pip install django-haystack elasticsearch

    配置文件:

    1. INSTALLED_APPS = [
    2. ...
    3. 'haystack',
    4. ...
    5. ]
    6. HAYSTACK_CONNECTIONS = {
    7. 'default': {
    8. # 引擎配置
    9. 'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
    10. # 搜索引擎服务的URL
    11. 'URL': 'http://1.2.3.4:9200',
    12. # 索引库的名称
    13. 'INDEX_NAME': 'goods',
    14. },
    15. }
    16. # 添加/删除/更新数据时自动生成索引
    17. HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'

    索引类:

    1. from haystack import indexes
    2. class GoodsIndex(indexes.SearchIndex, indexes.Indexable):
    3. text = indexes.CharField(document=True, use_template=True)
    4. def get_model(self):
    5. return Goods
    6. def index_queryset(self, using=None):
    7. return self.get_model().objects.all()

    编辑text字段的模板(需要放在templates/search/indexes/demo/goods_text.txt):

    1. {{object.title}}
    2. {{object.intro}}

    配置URL:

    生成初始索引:

    1. python manage.py rebuild_index