api_platform 产生错误“没有找到 uri [/index/_doc/_search] 和方法 [POST] 的处理程序”

问题描述 投票:0回答:2

当尝试通过 fos_elastica-bundle (v6.0.0) 将 elasticsearch (v7.9.3) 实现到我的 Symfony (v5.3.10) - App with api_platform (v2.6.6) 中时,我不断收到此错误:

"{"error":"no handler found for uri [//posts/_doc/_search] and method [POST]"}",

我的 api_platform.yaml 内容为:

api_platform:
    [...]
    elasticsearch:
        hosts: [ '%env(ELASTICSEARCH_URL)%' ]
        mapping:
            App\Document\Post:
                index: posts

和我的 fos_elastica.yaml:

fos_elastica:
    clients:
        default: { url: '%env(ELASTICSEARCH_URL)%' }
    indexes:
        posts:
            properties:
                id:
                    "type": "keyword"
                source: ~
                title: ~
                description: ~
                body: ~
                children: ~
                tags: ~
                originalContent: ~
            persistence:
                driver: mongodb
                model: App\Document\Post

通过调试 fos-elastica Bundle,我发现 Elastica-Connector 使用此请求正文正确触发了对“/posts/_doc/_search”的 [POST] 请求:

{"sort":[{"id":{"order":"asc"}}],"query":{"match_all":{}},"size":30,"from":0}

如果我使用 Kibana 开发工具控制台并触发相同的请求

POST /posts/_doc/_search
  {"sort":[{"id":{"order":"asc"}}],"query":{"match_all":{}},"size":30,"from":60}

我确实从 elasticsearch 中得到了预期的结果:

#! Deprecation: [types removal] Specifying types in search requests is deprecated.
    {
      "took" : 12,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 3082,
          "relation" : "eq"
        },
        "max_score" : null,
        "hits" : [
          {
            "_index" : "posts",
            "_type" : "_doc",
[...]

除了弃用通知之外,一切似乎都很好。

有谁知道为什么 fos_elastica-bundle 的 api_platform 集成无法按预期工作并不断返回“未找到处理程序”错误消息?

php symfony elasticsearch api-platform.com
2个回答
0
投票

我现在已经通过创建自定义 ApiResource - 过滤器来帮助自己

#[ApiFilter(FulltextFilter::class, arguments: ['index' => 'post'], properties: ['body','description','tag'])]

我的自定义过滤器实现了

ApiPlatform\Core\Bridge\Doctrine\MongoDbOdm\Filter\FilterInterface
,直接与ElasticSearch服务器通信,发送查询以搜索指定索引(帖子),并向aggregationBuilder添加另一个match()指令,其中包含一组与原始搜索匹配的ID:

<?php

declare(strict_types=1);

namespace App\Filter;

use ApiPlatform\Core\Bridge\Doctrine\MongoDbOdm\Filter\FilterInterface;
use Doctrine\ODM\MongoDB\Aggregation\Builder;
use Elastica\Result;
use Elastica\Client;
use Elastica\Query;
use Symfony\Component\PropertyInfo\Type;

/**
 * Filter the collection by given properties.
 *
 */
final class FulltextFilter implements FilterInterface
{
    protected $index = '';
    protected $properties = [];
    protected $client;
    protected $searchParameterName;
    protected $maxResultsParameterName;
    const DEFAULT_MAX_RESULTS = 200;
    public function __construct(Client $client, string $index = '', string $maxResultsParameterName = 'amount', string $searchParameterName = 'query', array $properties = []) {
        $this->index = $index;
        $this->properties = $properties;
        $this->client = $client;
        $this->searchParameterName = $searchParameterName;
        $this->maxResultsParameterName = $maxResultsParameterName;
    }
    public function getFilteredIds($searchterm, $index = null, $properties = null, $maxResults = null) {
        $matches = [];
        if (is_null($properties)) {
            $properties = array_keys($this->properties);
        }
        foreach ($properties as $propertyName) {
            array_push($matches, ['match'=>[$propertyName => $searchterm]]);
        }
        $queryObject = ['query' => ['bool' => ['should' => $matches]]];
        $queryObject['size'] = (int) $maxResults >0 ? (int) $maxResults : self::DEFAULT_MAX_RESULTS;

        $query = new Query();
        $response = $this->client->getIndex($index ?? $this->index)
            ->search($query->setRawQuery($queryObject))
            ->getResults();
        return array_map(function(Result $result) {return $result->getHit()['_source']['id'];}, $response);

    }

    public function apply(Builder $aggregationBuilder, string $resourceClass, string $operationName = null, array &$context = [])
    {
        $maxResults = $context['filters'][$this->maxResultsParameterName] ?? null;
        $searchterm = $context['filters'][$this->searchParameterName] ?? false;
        if ($searchterm !== false) {
            $aggregationBuilder->match()->field('id')->in($this->getFilteredIds($searchterm, null, null, $maxResults));
        }
    }

    public function getDescription(string $resourceClass): array
    {
        return [];
    }
}

此解决方案可能不如使用 api_platform 原生提供的 ElasticSearch-Connector 那么优雅,但它相当高效且有效。

但是,如果有人提出解决方案来解决 api_platform 所描述的 ES-Connector 问题,请随时分享。


0
投票

问题是,FOS Elastica 需要带有结尾斜杠的 ES URL。但 Api 平台要求 URL 不以斜杠结尾。

我们通常在 .env 文件中定义 URL,然后在配置文件中调用它。 为了解决这个问题,我们可以在 .env 中定义不带斜杠的 URL,并将斜杠添加到 FOS Elastica 配置中。

# .env

###> friendsofsymfony/elastica-bundle ###
ELASTICSEARCH_URL=http://localhost:9200
###< friendsofsymfony/elastica-bundle ###
# config/packages/api_platform.yaml

api_platform:
    elasticsearch:
        enabled: true
        hosts: [ '%env(ELASTICSEARCH_URL)%' ]
# config/packages/fos_elastica.yaml

fos_elastica:
    clients:
        default: { url: '%env(ELASTICSEARCH_URL)%/' }
© www.soinside.com 2019 - 2024. All rights reserved.