搜索是您网站中最重要的部分,快速搜索更为重要。虽然用户无法手动找到东西或用完时间,但他使用搜索并进行缓慢的搜索会使您的用户更加沮丧
在此博客中,我们将使用
进行REST API在这里,我们正在为电影应用程序构建API,例如Netflix或任何其他流媒体网站。
如果您直接想检查代码then jump here.
到目前为止,Elasticsearch是搜索文本的最佳搜索引擎,并且非常快。与Django集成也很容易。在搜索应用程序时,您还可以按年度,流派和评级过滤结果。如果您使用
进行相同的功能
默认的Django搜索如使用icontains
或contains
,然后在我们击中数据库并击中数据库时,将花费大量时间搜索和过滤结果。但是,使用Elasticsearch,它非常快,因为我们没有击中数据库,而是命中弹性搜索数据,这是一个索引数据,因此比常规DB更快地查询。
我不会深入了解,因为可以为本教程中使用的每种技术写几个博客。
在继续前进之前,我认为您有一些基本知识
- django li>
- Serializers
- REST Framework
- ubuntu-非常基础知识
- 几分钟的空闲时间°
如果您符合上述要求,那么您就可以了,如果不这样做,让我们继续前进,您将了解在制作这种功能时如何导航。ð
所以让我们开始 -
为了节省时间,我已经用完整的说明制作了一个git repo。我将通过足够的解释来做一些事情。
1.克隆回购
git clone https://github.com/selftaughtdev-me/movie-search-api.git
2.安装所需的软件包
Mac用户。您需要自己找到此软件包的安装方法。不用担心,因为它们很容易安装。对于Windows,您可以安装WSL2或在Windows本身上安装这些软件包。但是我建议您迟早使用WSL2,如果您正在与Django一起工作,您将需要此功能
sudo apt update -y
# install postgresql as sqlite is not efficient enough to handle millions of records
sudo apt install libpq-dev postgresql postgresql-contrib -y
sudo service postgresql start
# install python3 & build-essential
sudo add-apt-repository ppa:deadsnakes/ppa # for all python versions
sudo apt update -y
sudo apt-get install apt-transport-https
sudo apt install python3.8 python3.8-dev python3.8-venv build-essential -y
# install java as it is required for elasticsearch
sudo apt install openjdk-11-jdk openjdk-11-jre -y
# install ElasticSearch
curl -fsSL https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-7.x.list
sudo apt update
sudo apt install elasticsearch -y
sudo service elasticsearch start
sudo service elasticsearch status
上面,我们将弹性搜索与Java一起安装,这是运行此操作所需的。虽然你们可能主要使用Postgres或MySQL等关系数据库,该数据库将数据存储在表单中。弹性搜索是一个开源NOSQL数据库,可将数据存储在JSON格式中。此外,如果您将搜索时间与其他数据库进行了比较,则数据索引非常快。
。弹性搜索是开源的,但是有一些服务
您可能需要像您在使用付费服务时为Mongodb付款一样付款。现在。我们正在使用Postgres存储电影数据和弹性搜索以存储电影数据的副本,但是这些数据将如上所述。
3.创建一个数据库ðIS
sudo -u postgres psql
CREATE DATABASE django_flix;
CREATE USER django_flix_user WITH PASSWORD 'html_programmer';
ALTER ROLE django_flix_user SET client_encoding TO 'utf8';
ALTER ROLE django_flix_user SET default_transaction_isolation TO 'read committed';
ALTER ROLE django_flix_user SET timezone TO 'UTC';
GRANT ALL PRIVILEGES ON DATABASE django_flix TO django_flix_user;
\q
3.安装要求和迁移
# inside project root directory
python3.8 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install wheel
pip install -r requirements.txt
# migrate
./manage.py migrate
./manage.py createsuperuser
等等..您是否将目录更改为Project文件夹。如果没有,请做。
现在,我们需要生成大量数据来测试我们的API。在您的终端中,键入
./manage.py generate_test_data 1000000
ð!此命令将花费一段时间并用1M随机电影填充数据库
- ðâ¡为了节省时间,我只尝试了600k记录的API,这花费了很多时间。因此,我建议只尝试5000记录并在不同的终端窗口中运行此命令,以并行生成数据。
5.生成数据后,向下端到端点以下
./manage.py runserver
http://localhost:8000/api/?q=t
http://localhost:8000/api/search/?q=t&facets=year:1983
http://localhost:8000/api/search/?q=t&facets=year:1983,genre:rise
ð当您在生产中不使用它时,您可以忽略此Elasticsearch警告
ð生成的数据是不现实的。它仅出于演示目的。但是,正如您在右侧的调试面板中看到的那样,SQL计数为0,这意味着它没有击中数据库。它直接击中Elasticsearch
现在,在您测试了API之后,让我们看看它的工作原理。打开settings.py
Django Haystack是一个DJANGO软件包,提供了SearchQuerySet
和许多其他API,您可以使用它们有效地与Elasticsearch进行通信并搜索您的数据。
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"django_extensions",
"rest_framework",
"haystack", # haystack app needs to be put above all app
"apps.core",
"debug_toolbar",
]
# Haystack settings
HAYSTACK_CONNECTIONS = {
"default": {
"ENGINE": "haystack.backends.elasticsearch7_backend.Elasticsearch7SearchEngine",
"URL": "http://127.0.0.1:9200/",
"INDEX_NAME": "django_flix",
},
}
# auto index to elastic search when new data is created or when data is saved in the database
HAYSTACK_SIGNAL_PROCESSOR = "haystack.signals.RealtimeSignalProcessor"
之后,前往core/search_indexes.py
。该文件包含像映射一样。就像您想如何将SQL DB的数据索引到Elasticsearch。
这是结构化的方式 -
from haystack import indexes
from .models import Movie
class MovieIndex(indexes.SearchIndex, indexes.Indexable):
id = indexes.IntegerField(model_attr="id")
text = indexes.CharField(document=True, use_template=True)
title = indexes.CharField(model_attr="title")
description = indexes.CharField(model_attr="description")
year = indexes.IntegerField(model_attr="year")
rating = indexes.FloatField(model_attr="rating")
global_ranking = indexes.IntegerField(model_attr="global_ranking")
length = indexes.CharField(model_attr="length", faceted=True)
revenue = indexes.FloatField(model_attr="revenue", faceted=True)
genre = indexes.CharField(model_attr="genre", faceted=True)
country = indexes.CharField(model_attr="country", faceted=True)
director = indexes.CharField(model_attr="director", faceted=True)
def get_model(self):
return Movie
def prepare_director(self, obj):
return obj.director.name
def prepare_genre(self, obj):
return obj.genre.name
def prepare_country(self, obj):
return obj.country.name
def index_queryset(self, using=None):
return self.get_model().objects.all()
这里要注意的一些事情 -
Facet
->转到亚马逊,当您搜索任何产品时,您会在左侧看到一些过滤器。这些过滤器实际上是方面字段。
faceted=True
->无论您在此Kwarg中输入哪个字段,该字段都可以用于过滤目的。因此,在搜索时,您可以这样搜索ð
model_attr
->模型字段,您指的是地图。
base_api_url.com/?q=some_query&facets=year:1983,genre:rise
现在转到core/views.py
和您看到的第一个视图,负责渲染此数据。
class SearchViewElk(APIView, LimitOffsetPagination):
default_limit = 10
serializer_class = MovieHayStackSerializer
def get(self, request):
# get the query params
query = request.GET.get("q", None)
highlight = request.GET.get("highlight", None)
facets = request.GET.get("facets", None)
# prepare a initial elk SearchQuerySet from Movie Model
sqs = SearchQuerySet().models(Movie)
if query:
query_list = query.split(" ") # split the query string
qs_item = reduce(
operator.and_, (Q(text__contains=item) for item in query_list)
) # filter by every item in query_list - ( using OR filter)
sqs = sqs.filter(qs_item)
if highlight:
# if any value is passed to highlight then highlight the query
sqs = sqs.highlight()
if facets:
sqs = self.filter_sqs_by_facets(sqs, facets)
page = self.paginate_queryset(sqs, request, view=self)
movie_serializer = self.serializer_class(page, many=True)
facets = self.get_facet_fields(sqs)
summary = self.prepare_summary(sqs)
data = {"movies": movie_serializer.data, "facets": facets, "summary": summary}
return Response(data, status=HTTP_200_OK)
def filter_sqs_by_facets(self, sqs, facets):
facet_list = facets.split(",")
for facet in facet_list:
facet_key, facet_value = facet.split(":")
# narrow down the results by facet
sqs = sqs.narrow(f"{facet_key}:{facet_value}")
return sqs
def get_facet_fields(self, sqs):
# return all the possible facet fields from given SQS
facet_fields = (
sqs.facet("year")
.facet("rating")
.facet("global_ranking")
.facet("length")
.facet("revenue")
.facet("country")
.facet("genre")
)
return facet_fields.facet_counts()
def prepare_summary(self, sqs):
# return the summary of the search results
summary = {
"total": sqs.count(),
"next_page": self.get_next_link(),
"previous_page": self.get_previous_link(),
}
return summary
即使我提到了适当的评论,但是您有一些新的术语。
SearchQuerySet()
->搜索类别是SearchQuerySet的抽象和SearchBackend的实际搜索之间的中介。鉴于SearchQuerySet提供的元数据,SearchQuery构建了实际查询,并代表SearchQuerySet上的搜索背景进行交互。任何SearchQuerySet
obj都与django Queryset
对象大致相同。因此,这意味着您可以使用过滤器和其他操作,例如使用Django Model QuerySet。
sqs.highlight()
->这将使结果包含JSON响应中的highlighted
键,并且突出显示的文本将包含在<em>
标签中。您还可以将其自定义为自定义类,然后在给定类的前面进行样式。
sqs.facet('some_facetable_field')
->获取所有可能的方面,例如如果您在year
字段上也有faceted=true
,则在search_indexes.py
中也有faceted=true
。您将获得一个包含信息的对象,例如每年创作了多少电影。
.facet_counts()
->由于sqs.facet
返回一个对象,我们需要从此对象中获取值,并且该属性完成了该作业。
还有一个序列化器将Haystack的SearchQuerySet转换为Json。
# 3rd party imports
from drf_haystack.serializers import HaystackSerializer
# local imports
from .search_indexes import MovieIndex
class MovieHayStackSerializer(HaystackSerializer):
class Meta:
# The `index_classes` attribute is a list of which search indexes
# we want to include in the search.
index_classes = [MovieIndex]
# The `fields` contains all the fields we want to include.
# NOTE: Make sure you don't confuse these with model attributes. These
# fields belong to the search index!
fields = [
"title",
"description",
"year",
"rating",
"global_ranking",
"length",
"revenue",
"genre",
"country",
"director",
]
现在您已经准备好了API。您可以执行其他自定义,例如AutoQuery
,它允许像Google一样搜索。这意味着,如果您在任何查询的前面添加-
,则将排除匹配查询的结果。而且还有其他太多的自定义选项可以在documentation上进行检查。
这是我的第一个博客,我知道这不是可以理解的,但是我会尝试改进。 ð
有用的链接
免责声明 - 我不是专家,仍在学习。因此,可能有一些我可能会错过或可能被错误解释的事情。当我收到有关错误或在某个地方发现的任何评论时,我将在博客中对其进行更正。