Skip to content

Instantly share code, notes, and snippets.

@niisan-tokyo
Created October 21, 2023 06:45
Show Gist options
  • Save niisan-tokyo/613a12edb2e71fe3f755ad06dd475033 to your computer and use it in GitHub Desktop.
Save niisan-tokyo/613a12edb2e71fe3f755ad06dd475033 to your computer and use it in GitHub Desktop.
ElasticSearchのクエリビルダとそのテスト。テスト駆動開発の例として使う。
<?php
namespace Niisan\PhpElasticsearchQuerybuilder;
class QueryBuilder
{
private array $query = [];
private string $mode = 'must';
public function match(string $field, string $content): self
{
$this->query[] = [
'match' => [
$field => [
'query' => $content
]
]
];
return $this;
}
public function term(string $field, $value): self
{
$this->query[] = [
'term' => [
$field => [
'value' => $value
]
]
];
return $this;
}
public function terms(string $field, array $value): self
{
$this->query[] = [
'terms' => [
$field => [
'value' => $value
]
]
];
return $this;
}
public function range(string $field, $min = null, $max = null): self
{
$this->query[] = [
'range' => [
$field => [
'gte' => $min,
'lte' => $max
]
]
];
return $this;
}
public function or(callable $func = null): self
{
if ($func === null) {
$this->mode = 'should';
return $this;
}
$builder = new self;
$func($builder->or());
$this->query[] = $builder->getQuery();
return $this;
}
public function not(): self
{
$this->mode = 'must_not';
return $this;
}
public function getQuery()
{
return ($this->mode !== 'must_not' and count($this->query) === 1)
? $this->query[0]
: [
'bool' => [
$this->mode => $this->query
]
];
}
}
<?php
namespace Tests;
use Niisan\PhpElasticsearchQuerybuilder\QueryBuilder;
use PHPUnit\Framework\TestCase;
class QueryBuilderTest extends TestCase
{
private QueryBuilder $builder;
public function setUp(): void
{
$this->builder = new QueryBuilder;
}
/**
* @test
*/
public function ワード検索ができる()
{
$this->builder->match('content', '地方球場 真夏');
$this->assertEquals([
'match' => [
'content' => [
'query' => '地方球場 真夏'
]
]
], $this->builder->getQuery());
}
/**
* @test
*/
public function IDなどで検索ができる()
{
$this->builder->term('id', 1000);
$this->assertEquals([
'term' => [
'id' => [
'value' => 1000
]
]
], $this->builder->getQuery());
}
/**
* @test
*/
public function 複数のIDのどれかに一致する検索ができる()
{
$this->builder->terms('id', [1000, 1001]);
$this->assertEquals([
'terms' => [
'id' => [
'value' => [1000, 1001]
]
]
], $this->builder->getQuery());
}
/**
* @test
*/
public function AND検索ができる()
{
$this->builder->match('content', '今日の朝ごはん')->terms('id', [1000, 1001, 1002]);
$this->assertEquals([
'bool' => [
'must' => [
[
'match' => [
'content' => [
'query' => '今日の朝ごはん'
]
]
],
[
'terms' => [
'id' => [
'value' => [1000, 1001, 1002]
]
]
]
]
]
], $this->builder->getQuery());
}
/**
* @test
*/
public function OR検索ができる()
{
$this->builder
->or()
->match('content', '今日の朝ごはん')
->terms('id', [1000, 1001, 1002]);
$this->assertEquals([
'bool' => [
'should' => [
[
'match' => [
'content' => [
'query' => '今日の朝ごはん'
]
]
],
[
'terms' => [
'id' => [
'value' => [1000, 1001, 1002]
]
]
]
]
]
], $this->builder->getQuery());
}
/**
* @test
*/
public function 特定のIDを除外する検索ができる()
{
$this->builder->not()->terms('id', [1000, 1001, 1002]);
$this->assertEquals([
'bool' => [
'must_not' => [
[
'terms' => [
'id' => [
'value' => [1000, 1001, 1002]
]
]
]
]
]
], $this->builder->getQuery());
}
/**
* @test
*/
public function 値の範囲検索ができる()
{
$this->builder->range('age', 20, 30);
$this->assertEquals([
'range' => [
'age' => [
'gte' => 20,
'lte' => 30
]
]
], $this->builder->getQuery());
}
/**
* @test
*/
public function 部分的にOR検索ができる()
{
$this->builder
->match('content', '夏の思い出')
->or(function ($builder) {
$builder->term('id', 1000)
->range('age', 20, 30);
});
$this->assertEquals([
'bool' => [
'must' => [
[
'match' => [
'content' => [
'query' => '夏の思い出'
]
]
],
[
'bool' => [
'should' => [
[
'term' => [
'id' => [
'value' => 1000
]
]
],
[
'range' => [
'age' => [
'gte' => 20,
'lte' => 30
]
]
]
]
]
]
]
]
], $this->builder->getQuery());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment