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

2024-01-11

当尝试通过 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 集成无法按预期工作并继续返回“未找到处理程序”错误消息?


我现在通过创建自定义 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 问题,请随时分享。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

api_platform 产生错误“找不到 uri [/index/_doc/_search] 和方法 [POST] 的处理程序” 的相关文章

随机推荐