In this tutorial I am going to look at the possibility of using Drupal 7 as a content management system that powers another high performance application. To illustrate the latter, I will use the Silex PHP microframework and Elasticsearch as the data source. The goal is to create a proof of concept, demonstrating using these three technologies together.
在本教程中,我将探讨将Drupal 7用作为另一个高性能应用程序提供动力的内容管理系统的可能性。 为了说明后者,我将使用Silex PHP微框架和Elasticsearch作为数据源。 目标是创建概念证明,以一起使用这三种技术进行演示。
![esdrupalsilex](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2015/03/1425574421esdrupalsilex.png)
The article comes with a git repository that you should check out, which contains more complete code than can be presented in the tutorial itself. Additionally, if you are unfamiliar with either of the three open source projects being used, I recommend following the links above and also checking out the documentation on their respective websites.
本文附带了一个您应该检出的git存储库 ,其中包含的完整代码比本教程本身提供的代码还要完整。 此外,如果您不熟悉所使用的三个开源项目中的任何一个,我建议您遵循上面的链接,并在各自网站上查看文档。
The tutorial will be split into two pieces, because there is quite a lot of ground to cover.
本教程将分为两部分,因为有很多基础。
In this part, we’ll set up Elasticsearch on the server and integrate it with Drupal by creating a small, custom module that will insert, update, and delete Drupal nodes into Elasticsearch.
在这一部分中,我们将在服务器上设置Elasticsearch并将其与Drupal集成,方法是创建一个小的自定义模块,该模块会将Drupal节点插入,更新和删除到Elasticsearch中。
In the second part, we’ll create a small Silex app that fetches and displays the node data directly from Elasticsearch, completely bypassing the Drupal installation.
在第二部分中,我们将创建一个小的Silex应用程序,该应用程序直接从Elasticsearch获取并显示节点数据,完全绕开Drupal安装。
弹性搜索 (Elasticsearch)
The first step is to install Elasticsearch on the server. Assuming you are using Linux, you can follow this guide and set it up to run when the server starts. There are a number of configuration options you can set here.
第一步是在服务器上安装Elasticsearch。 假设您正在使用Linux,则可以遵循本指南并将其设置为在服务器启动时运行。 您可以在此处设置许多配置选项。
A very important thing to remember is that Elasticsearch has no access control so, once it is running on your server, it is publicly accessible through the (default) 9200 port. To avoid having problems, make sure that in the configuration file you uncomment this line:
要记住的一个非常重要的事情是,Elasticsearch没有访问控制,因此,一旦它在您的服务器上运行,就可以通过(默认)9200端口对其进行公开访问。 为避免出现问题,请确保在配置文件中取消注释以下行:
network.bind_host: localhost
And add the following one:
并添加以下内容:
script.disable_dynamic: true
These options make sure that Elasticsearch is not accessible from the outside, nor are dynamic scripts allowed. These are recommended security measures you need to take.
这些选项可确保从外部无法访问Elasticsearch,也不允许动态脚本。 这些是建议您采取的安全措施。
Drupal (Drupal)
The next step is to set up the Drupal site on the same server. Using the Elasticsearch Connector Drupal module, you can get some integration with the Elasticsearch instance: it comes with the PHP SDK for Elasticsearch, some statistics about the Elasticsearch instance and some other helpful submodules. I’ll leave it up to you to explore those at your leisure.
下一步是在同一服务器上设置Drupal站点。 使用Elasticsearch Connector Drupal模块 ,您可以与Elasticsearch实例进行一些集成:它随附于ElasticsearchPHP SDK,一些有关Elasticsearch实例的统计信息以及一些其他有用的子模块。 我将由您自己来闲暇时探索。
Once the connector module is enabled, in your custom module you can retrieve the Elasticsearch client object wrapper to access data:
启用连接器模块后,可以在您的定制模块中检索Elasticsearch客户端对象包装器以访问数据:
$client = elastic_connector_get_client_by_id('my_cluster_id');
Here, my_cluster_id
is the Drupal machine name that you gave to the Elasticsearch cluster (at admin/config/elasticsearch-connector/clusters
). The $client
object will now allow you to perform all sorts of operations, as illustrated in the docs I referenced above.
在这里, my_cluster_id
是您赋予Elasticsearch集群的Drupal机器名称(位于admin/config/elasticsearch-connector/clusters
)。 现在, $client
对象将允许您执行各种操作,如我在上面引用的文档中所示。
插入资料 (Inserting data)
The first thing we need to do is make sure we insert some Drupal data into Elasticsearch. Sticking to nodes for now, we can write a hook_node_insert() implementation that will save every new node to Elasticsearch. Here’s an example, inside a custom module called elastic
:
我们要做的第一件事是确保将一些Drupal数据插入Elasticsearch。 现在停留在节点上,我们可以编写一个hook_node_insert()实现,它将每个新节点保存到Elasticsearch。 这是一个示例,位于一个名为elastic
的自定义模块中:
/**
* Implements hook_node_insert().
*/
function elastic_node_insert($node) {
$client = elasticsearch_connector_get_client_by_id('my_cluster_id');
$params = _elastic_prepare_node($node);
if ( ! $params) {
drupal_set_message(t('There was a problem saving this node to Elasticsearch.'));
return;
}
$result = $client->index($params);
if ($result && $result['created'] === false) {
drupal_set_message(t('There was a problem saving this node to Elasticsearch.'));
return;
}
drupal_set_message(t('The node has been saved to Elasticsearch.'));
}
As you can see, we instantiate a client object that we use to index the data from the node. You may be wondering what _elastic_prepare_node()
is:
如您所见,我们实例化了一个客户端对象,该对象用于索引节点中的数据。 您可能想知道_elastic_prepare_node()
是什么:
/**
* Prepares a node to be added to Elasticsearch
*
* @param $node
* @return array
*/
function _elastic_prepare_node($node) {
if ( ! is_object($node)) {
return;
}
$params = array(
'index' => 'node',
'type' => $node->type,
'body' => array(),
);
// Add the simple properties
$wanted = array('vid', 'uid', 'title', 'log', 'status', 'comment', 'promote', 'sticky', 'nid', 'type', 'language', 'created', 'changed', 'revision_timestamp', 'revision_uid');
$exist = array_filter($wanted, function($property) use($node) {
return property_exists($node, $property);
});
foreach ($exist as $field) {
$params['body'][$field] = $node->{$field};
}
// Add the body field if exists
$body_field = isset($node->body) ? field_get_items('node', $node, 'body') : false;
if ($body_field) {
$params['body']['body'] = $body_field;
}
// Add the image field if exists
$image_field = isset($node->field_image) ? field_get_items('node', $node, 'field_image') : false;
if ($image_field) {
$params['body']['field_image'] = array_map(function($img) {
$img = file_load($img['fid']);
$img->url = file_create_url($img->uri);
return $img;
}, $image_field);
}
return $params;
}
It is just a helper function I wrote, which is responsible for “serializing” the node data and getting it ready for insertion into Elasticsearch. This is just an example and definitely not a complete or fully scalable one. It is also assuming that the respective image field name is field_image
. An important point to note is that we are inserting the nodes into the node
index with a type = $node->type
.
它只是我编写的一个辅助函数,负责“序列化”节点数据并准备好将其插入到Elasticsearch中。 这只是一个例子,绝对不是一个完整或完全可扩展的例子 。 还假设相应的图像字段名称为field_image
。 需要注意的重要一点是,我们正在使用类型= $node->type
将节点插入node
索引。
更新数据 (Updating data)
Inserting is not enough, we need to make sure that node changes get reflected in Elasticsearch as well. We can do this with a hook_node_update() implementation:
插入还不够,我们需要确保节点更改也能反映在Elasticsearch中。 我们可以通过hook_node_update()实现来实现:
/**
* Implements hook_node_update().
*/
function elastic_node_update($node) {
if ($node->is_new !== false) {
return;
}
$client = elasticsearch_connector_get_client_by_id('my_cluster_id');
$params = _elastic_prepare_node($node);
if ( ! $params) {
drupal_set_message(t('There was a problem updating this node in Elasticsearch.'));
return;
}
$result = _elastic_perform_node_search_by_id($client, $node);
if ($result && $result['hits']['total'] !== 1) {
drupal_set_message(t('There was a problem updating this node in Elasticsearch.'));
return;
}
$params['id'] = $result['hits']['hits'][0]['_id'];
$version = $result['hits']['hits'][0]['_version'];
$index = $client->index($params);
if ($index['_version'] !== $version + 1) {
drupal_set_message(t('There was a problem updating this node in Elasticsearch.'));
return;
}
drupal_set_message(t('The node has been updated in Elasticsearch.'));
}
We again use the helper function to prepare our node for insertion, but this time we also search for the node in Elasticsearch to make sure we are updating and not creating a new one. This happens using another helper function I wrote as an example:
我们再次使用helper函数来准备要插入的节点,但是这次我们还在Elasticsearch中搜索该节点,以确保我们正在更新而不创建新节点。 这是通过使用我以示例编写的另一个辅助函数进行的:
/**
* Helper function that returns a node from Elasticsearch by its nid.
*
* @param $client
* @param $node
* @return mixed
*/
function _elastic_perform_node_search_by_id($client, $node) {
$search = array(
'index' => 'node',
'type' => $node->type,
'version' => true,
'body' => array(
'query' => array(
'match' => array(
'nid' => $node->nid,
),
),
),
);
return $client->search($search);
}
You’ll notice that I am asking Elasticsearch to return the document version as well. This is so that I can check if a document has been updated with my request.
您会注意到,我也在要求Elasticsearch返回文档版本。 这样一来,我可以检查文档是否已根据我的请求进行了更新。
删除资料 (Deleting data)
The last (for now) feature we need is the ability to remove the data from Elasticsearch when a node gets deleted. hook_node_delete() can help us with that:
我们需要的最后一个(暂时)功能是当节点被删除时从Elasticsearch中删除数据的功能。 hook_node_delete()可以帮助我们:
/**
* Implements hook_node_delete().
*/
function elastic_node_delete($node) {
$client = elasticsearch_connector_get_client_by_id('my_cluster_id');
// If the node is in Elasticsearch, remove it
$result = _elastic_perform_node_search_by_id($client, $node);
if ($result && $result['hits']['total'] !== 1) {
drupal_set_message(t('There was a problem deleting this node in Elasticsearch.'));
return;
}
$params = array(
'index' => 'node',
'type' => $node->type,
'id' => $result['hits']['hits'][0]['_id'],
);
$result = $client->delete($params);
if ($result && $result['found'] !== true) {
drupal_set_message(t('There was a problem deleting this node in Elasticsearch.'));
return;
}
drupal_set_message(t('The node has been deleted in Elasticsearch.'));
}
Again, we search for the node in Elasticsearch and use the returned ID as a marker to delete the document.
同样,我们在Elasticsearch中搜索该节点,然后使用返回的ID作为标记来删除文档。
Please keep in mind though that using early returns such as illustrated above is not ideal inside Drupal hook implementations unless this is more or less all the functionality that needs to go in them. I recommend splitting the logic into helper functions if you need to perform other unrelated tasks inside these hooks.
请记住,尽管在Drupal钩子实现中使用上述返回值并不理想,除非这或多或少是它们需要的所有功能。 如果您需要在这些挂钩中执行其他不相关的任务,建议将逻辑分为辅助函数。
This is enough to get us started using Elasticsearch as a very simple data source on top of Drupal. With this basic code in place, you can navigate to your Drupal site and start creating some nodes, updating them and deleting them.
这足以使我们开始使用Elasticsearch作为Drupal之上的非常简单的数据源。 使用此基本代码后,您可以导航到Drupal站点并开始创建一些节点,对其进行更新并删除它们。
One way to check if Elasticsearch actually gets populated, is to disable the remote access restriction I mentioned above you need to enable. Make sure you only do this on your local, development, environment. This way, you can perform HTTP requests directly from the browser and get JSON data back from Elasticsearch.
检查Elasticsearch是否实际填充的一种方法是禁用我上面需要启用的远程访问限制。 确保仅在本地,开发环境中执行此操作。 这样,您可以直接从浏览器执行HTTP请求,并从Elasticsearch获得JSON数据。
You can do a quick search for all the nodes in Elasticsearch by navigating to this URL:
您可以通过导航到以下URL来快速搜索Elasticsearch中的所有节点:
http://localhost:9200/node/_search
…where localhost points to your local server and 9200 is the default Elasticsearch port.
…其中localhost指向您的本地服务器,而9200是默认的Elasticsearch端口。
For article nodes only:
仅对于文章节点:
http://localhost:9200/node/article/_search
And for individual articles, by the auto generated Elasticsearch ids:
对于个别文章,通过自动生成的Elasticsearch ID:
http://localhost:9200/node/article/AUnJgdPGGE7A1g9FtqdV
Go ahead and check out the Elasticsearch documentation for all the amazing ways you can interact with it.
继续并查看Elasticsearch文档,了解与之交互的所有奇妙方式。
结论 (Conclusion)
We’ve seen in this article how we can start working to integrate Elasticsearch with Drupal. Obviously, there is far more we can do based on even the small things we’ve accomplished. We can extend the integration to other entities and even Drupal configuration if needed. In any case, we now have some Drupal data in Elasticsearch, ready to be used from an external application.
我们已经在本文中看到了如何开始将Elasticsearch与Drupal集成。 显然,即使是已经完成的小事情,我们还有很多工作要做。 如果需要,我们可以将集成扩展到其他实体,甚至可以扩展到Drupal配置。 无论如何,我们现在在Elasticsearch中有一些Drupal数据,可以从外部应用程序中使用。
That external application will be the task for the second part of this tutorial. We’ll be setting up a small Silex app that, using the Elasticsearch PHP SDK, will read the Drupal data directly from Elasticsearch. As with part 1, above, we won’t be going through a step-by-step tutorial on accomplishing a given task, but instead will explore one of the ways that you can start building this integration. See you there.
该外部应用程序将是本教程第二部分的任务。 我们将建立一个小的Silex应用程序,该应用程序将使用Elasticsearch PHP SDK直接从Elasticsearch读取Drupal数据。 与上面的第1部分一样,我们不会逐步学习如何完成给定任务,而是将探索您可以开始构建此集成的方法之一。 到时候那里见。
翻译自: https://www.sitepoint.com/install-integrate-elasticsearch-drupal/