Skip to content

Commit e69a1c5

Browse files
author
yangxg
committed
Step28: full text search using django-haystack and elasticsearch
1 parent 83064c1 commit e69a1c5

20 files changed

+231
-30
lines changed

Diff for: Pipfile

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
[[source]]
22
name = "pypi"
3-
url = "https://pypi.douban.com/simple"
3+
url = "https://pypi.org/simple"
44
verify_ssl = true
55

66
[dev-packages]
77
fabric = "*"
88

99
[packages]
10-
django = "*"
10+
django = "~=2.2"
1111
markdown = "*"
1212
gunicorn = "*"
1313
faker = "*"
1414
django-pure-pagination = "*"
15+
elasticsearch = ">=2,<3"
16+
django-haystack = "*"
1517

1618
[requires]
1719
python_version = "3.6"

Diff for: Pipfile.lock

+38-23
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: README.md

+1
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ tutorial 分支为项目的主分支,每一篇教程的代码都和历史提
148148
25. [统计各个分类和标签下的文章数](https://www.zmrenwu.com/courses/hellodjango-blog-tutorial/materials/83/)
149149
26. [开启 Django 博客的 RSS 功能](https://www.zmrenwu.com/courses/hellodjango-blog-tutorial/materials/84/)
150150
27. [Django 博客实现简单的全文搜索](https://www.zmrenwu.com/courses/hellodjango-blog-tutorial/materials/85/)
151+
28. [Django Haystack 全文检索与关键词高亮](https://www.zmrenwu.com/courses/hellodjango-blog-tutorial/materials/86/)
151152

152153
## 公众号
153154
<p align="center">

Diff for: blog/elasticsearch2_ik_backend.py

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from haystack.backends.elasticsearch2_backend import Elasticsearch2SearchBackend, Elasticsearch2SearchEngine
2+
3+
DEFAULT_FIELD_MAPPING = {'type': 'string', "analyzer": "ik_max_word", "search_analyzer": "ik_smart"}
4+
5+
6+
class Elasticsearch2IkSearchBackend(Elasticsearch2SearchBackend):
7+
8+
def __init__(self, *args, **kwargs):
9+
self.DEFAULT_SETTINGS['settings']['analysis']['analyzer']['ik_analyzer'] = {
10+
"type": "custom",
11+
"tokenizer": "ik_max_word",
12+
}
13+
super(Elasticsearch2IkSearchBackend, self).__init__(*args, **kwargs)
14+
15+
16+
class Elasticsearch2IkSearchEngine(Elasticsearch2SearchEngine):
17+
backend = Elasticsearch2IkSearchBackend

Diff for: blog/search_indexes.py

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from haystack import indexes
2+
from .models import Post
3+
4+
5+
class PostIndex(indexes.SearchIndex, indexes.Indexable):
6+
text = indexes.CharField(document=True, use_template=True)
7+
8+
def get_model(self):
9+
return Post
10+
11+
def index_queryset(self, using=None):
12+
return self.get_model().objects.all()

Diff for: blog/urls.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99
path('archives/<int:year>/<int:month>/', views.ArchiveView.as_view(), name='archive'),
1010
path('categories/<int:pk>/', views.CategoryView.as_view(), name='category'),
1111
path('tags/<int:pk>/', views.TagView.as_view(), name='tag'),
12-
path('search/', views.search, name='search'),
12+
# path('search/', views.search, name='search'),
1313
]

Diff for: blog/utils.py

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from django.utils.html import strip_tags
2+
from haystack.utils import Highlighter as HaystackHighlighter
3+
4+
5+
class Highlighter(HaystackHighlighter):
6+
"""
7+
自定义关键词高亮器,不截断过短的文本(例如文章标题)
8+
"""
9+
10+
def highlight(self, text_block):
11+
self.text_block = strip_tags(text_block)
12+
highlight_locations = self.find_highlightable_words()
13+
start_offset, end_offset = self.find_window(highlight_locations)
14+
if len(text_block) < self.max_length:
15+
start_offset = 0
16+
return self.render_html(highlight_locations, start_offset, end_offset)

Diff for: blogproject/settings/common.py

+15-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
'django.contrib.staticfiles',
3232

3333
'pure_pagination', # 分页
34-
34+
'haystack', # 搜索
3535
'blog.apps.BlogConfig', # 注册 blog 应用
3636
'comments.apps.CommentsConfig', # 注册 comments 应用
3737
]
@@ -119,3 +119,17 @@
119119
'MARGIN_PAGES_DISPLAYED': 2,
120120
'SHOW_FIRST_PAGE_WHEN_INVALID': True,
121121
}
122+
123+
# 搜索设置
124+
HAYSTACK_CONNECTIONS = {
125+
'default': {
126+
'ENGINE': 'blog.elasticsearch2_ik_backend.Elasticsearch2IkSearchEngine',
127+
'URL': '',
128+
'INDEX_NAME': 'hellodjango_blog_tutorial',
129+
},
130+
}
131+
HAYSTACK_SEARCH_RESULTS_PER_PAGE = 10
132+
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
133+
HAYSTACK_CUSTOM_HIGHLIGHTER = 'blog.utils.Highlighter'
134+
# HAYSTACK_DEFAULT_OPERATOR = 'AND'
135+
# HAYSTACK_FUZZY_MIN_SIM = 0.1

Diff for: blogproject/settings/local.py

+3
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@
55
DEBUG = True
66

77
ALLOWED_HOSTS = ['*']
8+
9+
# 搜索设置
10+
HAYSTACK_CONNECTIONS['default']['URL'] = 'http://elasticsearch_local:9200/'

Diff for: blogproject/settings/production.py

+1
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55
DEBUG = False
66

77
ALLOWED_HOSTS = ['hellodjango-blog-tutorial-demo.zmrenwu.com']
8+
HAYSTACK_CONNECTIONS['default']['URL'] = 'http://hellodjango_blog_tutorial_elasticsearch:9200/'

Diff for: blogproject/urls.py

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
urlpatterns = [
2222
path('admin/', admin.site.urls),
23+
path('search/', include('haystack.urls')),
2324
path('', include('blog.urls')),
2425
path('', include('comments.urls')),
2526

Diff for: compose/production/elasticsearch/Dockerfile

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FROM elasticsearch:2.4.6-alpine
2+
3+
COPY ./compose/production/elasticsearch/elasticsearch-analysis-ik-1.10.6.zip /usr/share/elasticsearch/plugins/
4+
RUN cd /usr/share/elasticsearch/plugins/ && mkdir ik && unzip elasticsearch-analysis-ik-1.10.6.zip -d ik/
5+
RUN rm /usr/share/elasticsearch/plugins/elasticsearch-analysis-ik-1.10.6.zip
6+
7+
USER root
8+
COPY ./compose/production/elasticsearch/elasticsearch.yml /usr/share/elasticsearch/config/
9+
RUN chown elasticsearch:elasticsearch /usr/share/elasticsearch/config/elasticsearch.yml
10+
11+
USER elasticsearch
12+
Binary file not shown.
Binary file not shown.

Diff for: compose/production/elasticsearch/elasticsearch.yml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
bootstrap.memory_lock: true
2+
network.host: 0.0.0.0

Diff for: local.yml

+25-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ version: '3'
22

33
volumes:
44
database_local:
5+
esdata_local:
56

67
services:
78
hellodjango_blog_tutorial_local:
@@ -16,4 +17,27 @@ services:
1617
- .:/app
1718
ports:
1819
- "8000:8000"
19-
command: /start.sh
20+
command: /start.sh
21+
depends_on:
22+
- elasticsearch_local
23+
24+
elasticsearch_local:
25+
build:
26+
context: .
27+
dockerfile: ./compose/production/elasticsearch/Dockerfile
28+
image: hellodjango_blog_tutorial_elasticsearch_local
29+
container_name: hellodjango_blog_tutorial_elasticsearch_local
30+
volumes:
31+
- esdata_local:/usr/share/elasticsearch/data
32+
ports:
33+
- "9200:9200"
34+
environment:
35+
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
36+
ulimits:
37+
memlock:
38+
soft: -1
39+
hard: -1
40+
nproc: 65536
41+
nofile:
42+
soft: 65536
43+
hard: 65536

Diff for: production.yml

+23-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ version: '3'
33
volumes:
44
static:
55
database:
6+
esdata:
67

78
services:
89
hellodjango_blog_tutorial:
@@ -31,4 +32,25 @@ services:
3132
- static:/apps/hellodjango_blog_tutorial/static
3233
ports:
3334
- "80:80"
34-
- "443:443"
35+
- "443:443"
36+
37+
elasticsearch:
38+
build:
39+
context: .
40+
dockerfile: ./compose/production/elasticsearch/Dockerfile
41+
image: hellodjango_blog_tutorial_elasticsearch
42+
container_name: hellodjango_blog_tutorial_elasticsearch
43+
volumes:
44+
- esdata:/usr/share/elasticsearch/data
45+
ports:
46+
- "9200:9200"
47+
environment:
48+
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
49+
ulimits:
50+
memlock:
51+
soft: -1
52+
hard: -1
53+
nproc: 65536
54+
nofile:
55+
soft: 65536
56+
hard: 65536

Diff for: templates/base.html

+6-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@
4646
padding-left: 10px;
4747
white-space: pre;
4848
}
49+
50+
/* 搜索关键词高亮 */
51+
span.highlighted {
52+
color: red;
53+
}
4954
</style>
5055

5156
<!-- js -->
@@ -84,7 +89,7 @@ <h1><a href="{% url 'blog:index' %}"><b>Black</b> &amp; White</a></h1>
8489
<div id="header-search-box">
8590
<a id="search-menu" href="#"><span id="search-icon" class="ion-ios-search-strong"></span></a>
8691
<div id="search-form" class="search-form">
87-
<form role="search" method="get" id="searchform" action="{% url 'blog:search' %}">
92+
<form role="search" method="get" id="searchform" action="{% url 'haystack_search' %}">
8893
<input type="search" name="q" placeholder="搜索" required>
8994
<button type="submit"><span class="ion-ios-search-strong"></span></button>
9095
</form>

Diff for: templates/search/indexes/blog/post_text.txt

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
{{ object.title }}
2+
{{ object.body }}

0 commit comments

Comments
 (0)