Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.97% covered (success)
96.97%
32 / 33
80.00% covered (warning)
80.00%
4 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
SemanticSearchResultBuilder
96.97% covered (success)
96.97%
32 / 33
80.00% covered (warning)
80.00%
4 / 5
12
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 newBuilder
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 build
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
4
 getTitleHelper
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 doInnerHits
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
1<?php
2
3namespace CirrusSearch\Search;
4
5use CirrusSearch\Search\Fetch\HighlightingTrait;
6use MediaWiki\Title\Title;
7use MediaWiki\Utils\MWTimestamp;
8
9class SemanticSearchResultBuilder {
10    use HighlightingTrait;
11
12    /** @var CirrusSearchResultBuilder|null */
13    private $builder;
14
15    /** @var TitleHelper */
16    private $titleHelper;
17
18    private string $innerHitsField;
19    private string $snippetField;
20    private string $sectionField;
21
22    /** @var string[] */
23    private $extraFields;
24
25    /**
26     * @param TitleHelper $titleHelper
27     * @param string $innerHitsField The nested field that contains vectors
28     * @param string $snippetField The source field within the nested field to use as a snippet
29     * @param string $sectionField The source field within the nested field to use as section link anchor
30     * @param string[] $extraFields list of extra fields to extract from the source doc
31     */
32    public function __construct(
33        TitleHelper $titleHelper,
34        string $innerHitsField,
35        string $snippetField,
36        string $sectionField,
37        array $extraFields = []
38    ) {
39        $this->titleHelper = $titleHelper;
40        $this->innerHitsField = $innerHitsField;
41        $this->snippetField = $snippetField;
42        $this->sectionField = $sectionField;
43        $this->extraFields = $extraFields;
44    }
45
46    /**
47     * @param Title $title
48     * @param string $docId
49     * @return CirrusSearchResultBuilder
50     */
51    private function newBuilder( Title $title, $docId ): CirrusSearchResultBuilder {
52        if ( $this->builder === null ) {
53            $this->builder = new CirrusSearchResultBuilder( $title, $docId );
54        } else {
55            $this->builder->reset( $title, $docId );
56        }
57        return $this->builder;
58    }
59
60    public function build( \Elastica\Result $result ): CirrusSearchResult {
61        $title = $this->getTitleHelper()->makeTitle( $result );
62        $fields = $result->getFields();
63        $builder = $this->newBuilder( $title, $result->getId() )
64            ->wordCount( $fields['text.word_count'][0] ?? 0 )
65            ->byteSize( $result->text_bytes ?? 0 )
66            ->timestamp( new MWTimestamp( $result->timestamp ) )
67            ->score( $result->getScore() )
68            ->explanation( $result->getExplanation() );
69
70        if ( isset( $result->namespace_text ) ) {
71            $builder->interwikiNamespaceText( $result->namespace_text );
72        }
73
74        $this->doInnerHits( $title, $result->getParam( 'inner_hits' ) );
75
76        $source = $result->getData();
77        foreach ( $this->extraFields as $field ) {
78            if ( isset( $source[$field] ) ) {
79                $builder->addExtraField( $field, $source[$field] );
80            }
81        }
82
83        return $builder->build();
84    }
85
86    protected function getTitleHelper(): TitleHelper {
87        return $this->titleHelper;
88    }
89
90    private function doInnerHits( Title $title, array $innerHits ): void {
91        foreach ( $innerHits[$this->innerHitsField]['hits']['hits'] as $hit ) {
92            if ( isset( $hit['_source'][$this->snippetField] ) ) {
93                $this->builder->textSnippet( $hit['_source'][$this->snippetField] );
94            }
95            if ( isset( $hit['_source'][$this->sectionField] ) ) {
96                $this->builder->sectionTitle( $title->createFragmentTarget( $this->titleHelper->sanitizeSectionFragment(
97                    $hit['_source'][$this->sectionField] ) ) );
98            }
99            return;
100        }
101    }
102
103}