Skip to content

Commit d85884d

Browse files
jonnyeomcolinodell
andauthored
feat(elasticsearch): filtering on nested fields (#5820)
* chore(elasticsearch): define a valid, known type for 'baz' * fix(elasticsearch): fix nested object paths (api-platform/api-platform#1375) * fix(elasticsearch): support additional nesting within collections (#5642) --------- Co-authored-by: Colin O'Dell <[email protected]>
1 parent 32242a2 commit d85884d

File tree

4 files changed

+83
-4
lines changed

4 files changed

+83
-4
lines changed

src/Elasticsearch/Tests/Filter/MatchFilterTest.php

+33-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public function testApply(): void
8787
);
8888
}
8989

90-
public function testApplyWithNestedProperty(): void
90+
public function testApplyWithNestedArrayProperty(): void
9191
{
9292
$fooType = new Type(Type::BUILTIN_TYPE_ARRAY, false, Foo::class, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, Foo::class));
9393
$barType = new Type(Type::BUILTIN_TYPE_STRING);
@@ -119,6 +119,38 @@ public function testApplyWithNestedProperty(): void
119119
);
120120
}
121121

122+
public function testApplyWithNestedObjectProperty(): void
123+
{
124+
$fooType = new Type(Type::BUILTIN_TYPE_OBJECT, false, Foo::class);
125+
$barType = new Type(Type::BUILTIN_TYPE_STRING);
126+
127+
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
128+
$propertyMetadataFactoryProphecy->create(Foo::class, 'foo')->willReturn((new ApiProperty())->withBuiltinTypes([$fooType]))->shouldBeCalled();
129+
$propertyMetadataFactoryProphecy->create(Foo::class, 'bar')->willReturn((new ApiProperty())->withBuiltinTypes([$barType]))->shouldBeCalled();
130+
131+
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
132+
$resourceClassResolverProphecy->isResourceClass(Foo::class)->willReturn(true)->shouldBeCalled();
133+
134+
$nameConverterProphecy = $this->prophesize(NameConverterInterface::class);
135+
$nameConverterProphecy->normalize('foo.bar', Foo::class, null, Argument::type('array'))->willReturn('foo.bar')->shouldBeCalled();
136+
$nameConverterProphecy->normalize('foo', Foo::class, null, Argument::type('array'))->willReturn('foo')->shouldBeCalled();
137+
138+
$matchFilter = new MatchFilter(
139+
$this->prophesize(PropertyNameCollectionFactoryInterface::class)->reveal(),
140+
$propertyMetadataFactoryProphecy->reveal(),
141+
$resourceClassResolverProphecy->reveal(),
142+
$this->prophesize(IriConverterInterface::class)->reveal(),
143+
$this->prophesize(PropertyAccessorInterface::class)->reveal(),
144+
$nameConverterProphecy->reveal(),
145+
['foo.bar' => null]
146+
);
147+
148+
self::assertSame(
149+
['bool' => ['must' => [['nested' => ['path' => 'foo', 'query' => ['match' => ['foo.bar' => 'Krupicka']]]]]]],
150+
$matchFilter->apply([], Foo::class, null, ['filters' => ['foo.bar' => 'Krupicka']])
151+
);
152+
}
153+
122154
public function testApplyWithInvalidFilters(): void
123155
{
124156
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);

src/Elasticsearch/Tests/Filter/TermFilterTest.php

+33-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public function testApply(): void
8787
);
8888
}
8989

90-
public function testApplyWithNestedProperty(): void
90+
public function testApplyWithNestedArrayProperty(): void
9191
{
9292
$fooType = new Type(Type::BUILTIN_TYPE_ARRAY, false, Foo::class, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, Foo::class));
9393
$barType = new Type(Type::BUILTIN_TYPE_STRING);
@@ -119,6 +119,38 @@ public function testApplyWithNestedProperty(): void
119119
);
120120
}
121121

122+
public function testApplyWithNestedObjectProperty(): void
123+
{
124+
$fooType = new Type(Type::BUILTIN_TYPE_OBJECT, false, Foo::class);
125+
$barType = new Type(Type::BUILTIN_TYPE_STRING);
126+
127+
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
128+
$propertyMetadataFactoryProphecy->create(Foo::class, 'foo')->willReturn((new ApiProperty())->withBuiltinTypes([$fooType]))->shouldBeCalled();
129+
$propertyMetadataFactoryProphecy->create(Foo::class, 'bar')->willReturn((new ApiProperty())->withBuiltinTypes([$barType]))->shouldBeCalled();
130+
131+
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
132+
$resourceClassResolverProphecy->isResourceClass(Foo::class)->willReturn(true)->shouldBeCalled();
133+
134+
$nameConverterProphecy = $this->prophesize(NameConverterInterface::class);
135+
$nameConverterProphecy->normalize('foo.bar', Foo::class, null, Argument::type('array'))->willReturn('foo.bar')->shouldBeCalled();
136+
$nameConverterProphecy->normalize('foo', Foo::class, null, Argument::type('array'))->willReturn('foo')->shouldBeCalled();
137+
138+
$termFilter = new TermFilter(
139+
$this->prophesize(PropertyNameCollectionFactoryInterface::class)->reveal(),
140+
$propertyMetadataFactoryProphecy->reveal(),
141+
$resourceClassResolverProphecy->reveal(),
142+
$this->prophesize(IriConverterInterface::class)->reveal(),
143+
$this->prophesize(PropertyAccessorInterface::class)->reveal(),
144+
$nameConverterProphecy->reveal(),
145+
['foo.bar' => null]
146+
);
147+
148+
self::assertSame(
149+
['bool' => ['must' => [['nested' => ['path' => 'foo', 'query' => ['term' => ['foo.bar' => 'Krupicka']]]]]]],
150+
$termFilter->apply([], Foo::class, null, ['filters' => ['foo.bar' => 'Krupicka']])
151+
);
152+
}
153+
122154
public function testApplyWithInvalidFilters(): void
123155
{
124156
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);

src/Elasticsearch/Tests/Util/FieldDatatypeTraitTest.php

+13
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ public function testGetNestedFieldPath(): void
3232
$fieldDatatype = $this->getValidFieldDatatype();
3333

3434
self::assertSame('foo.bar', $fieldDatatype->getNestedFieldPath(Foo::class, 'foo.bar.baz'));
35+
self::assertSame('foo', $fieldDatatype->getNestedFieldPath(Foo::class, 'foo.baz'));
36+
self::assertNull($fieldDatatype->getNestedFieldPath(Foo::class, 'baz'));
37+
}
38+
39+
public function testGetNestedFieldInNestedCollection(): void
40+
{
41+
$fieldDatatype = $this->getValidFieldDatatype();
42+
43+
self::assertSame('bar.foo', $fieldDatatype->getNestedFieldPath(Foo::class, 'bar.foo.baz'));
44+
self::assertSame('bar', $fieldDatatype->getNestedFieldPath(Foo::class, 'bar.foo'));
3545
self::assertNull($fieldDatatype->getNestedFieldPath(Foo::class, 'baz'));
3646
}
3747

@@ -72,17 +82,20 @@ public function testIsNestedField(): void
7282
$fieldDatatype = $this->getValidFieldDatatype();
7383

7484
self::assertTrue($fieldDatatype->isNestedField(Foo::class, 'foo.bar.baz'));
85+
self::assertTrue($fieldDatatype->isNestedField(Foo::class, 'foo.baz'));
7586
self::assertFalse($fieldDatatype->isNestedField(Foo::class, 'baz'));
7687
}
7788

7889
private function getValidFieldDatatype()
7990
{
8091
$fooType = new Type(Type::BUILTIN_TYPE_OBJECT, false, Foo::class);
8192
$barType = new Type(Type::BUILTIN_TYPE_ARRAY, false, Foo::class, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, Foo::class));
93+
$bazType = new Type(Type::BUILTIN_TYPE_STRING, false, Foo::class);
8294

8395
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
8496
$propertyMetadataFactoryProphecy->create(Foo::class, 'foo')->willReturn((new ApiProperty())->withBuiltinTypes([$fooType]))->shouldBeCalled();
8597
$propertyMetadataFactoryProphecy->create(Foo::class, 'bar')->willReturn((new ApiProperty())->withBuiltinTypes([$barType]))->shouldBeCalled();
98+
$propertyMetadataFactoryProphecy->create(Foo::class, 'baz')->willReturn((new ApiProperty())->withBuiltinTypes([$bazType]));
8699

87100
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
88101
$resourceClassResolverProphecy->isResourceClass(Foo::class)->willReturn(true)->shouldBeCalled();

src/Elasticsearch/Util/FieldDatatypeTrait.php

+4-2
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ private function getNestedFieldPath(string $resourceClass, string $property): ?s
6969
) {
7070
$nestedPath = $this->getNestedFieldPath($nextResourceClass, implode('.', $properties));
7171

72-
return null === $nestedPath ? $nestedPath : "$currentProperty.$nestedPath";
72+
return null === $nestedPath ? $currentProperty : "$currentProperty.$nestedPath";
7373
}
7474

7575
if (
@@ -78,7 +78,9 @@ private function getNestedFieldPath(string $resourceClass, string $property): ?s
7878
&& null !== ($className = $type->getClassName())
7979
&& $this->resourceClassResolver->isResourceClass($className)
8080
) {
81-
return $currentProperty;
81+
$nestedPath = $this->getNestedFieldPath($className, implode('.', $properties));
82+
83+
return null === $nestedPath ? $currentProperty : "$currentProperty.$nestedPath";
8284
}
8385
}
8486

0 commit comments

Comments
 (0)