Skip to content

Instantly share code, notes, and snippets.

@maxiloc
Created Apr 25, 2019
Embed
What would you like to do?
<?php
class DisjunctiveSearcher
{
private $client;
private $indexName;
public function __construct(\Algolia\AlgoliaSearch\SearchClient $client, $indexName)
{
$this->client = $client;
$this->indexName = $indexName;
}
function search($query, $args, $requestHeaders = [])
{
if (!is_array($args['disjunctiveFacets']) || count($args['disjunctiveFacets']) <= 0) {
throw new \InvalidArgumentException('disjunctiveFacets needs to be an non empty array');
}
if (isset($args['filters'])) {
throw new \InvalidArgumentException('You can not use disjunctive faceting and the filters parameter');
}
/**
* Prepare queries
*/
// Get the list of disjunctive queries to do: 1 per disjunctive facet
$disjunctiveQueries = $this->getDisjunctiveQueries($args);
// Format disjunctive queries for multipleQueries call
foreach ($disjunctiveQueries as &$disjunctiveQuery) {
$disjunctiveQuery['indexName'] = $this->indexName;
$disjunctiveQuery['query'] = $query;
unset($disjunctiveQuery['disjunctiveFacets']);
}
// Merge facets and disjunctiveFacets for the hits query
$facets = isset($args['facets']) ? $args['facets'] : array();
$facets = array_merge($facets, $args['disjunctiveFacets']);
unset($args['disjunctiveFacets']);
// format the hits query for multipleQueries call
$args['query'] = $query;
$args['indexName'] = $this->indexName;
$args['facets'] = $facets;
// Put the hit query first
array_unshift($disjunctiveQueries, $args);
/**
* Do all queries in one call
*/
$results = $this->client->multipleQueries(array_values($disjunctiveQueries), $requestHeaders);
$results = $results['results'];
/**
* Merge facets from disjunctive queries with facets from the hits query
*/
// The first query is the hits query that the one we'll return to the user
$queryResults = array_shift($results);
// To be able to add facets from disjunctive query we create 'facets' key in case we only have disjunctive facets
if (false === isset($queryResults['facets'])) {
$queryResults['facets'] = array();
}
foreach ($results as $disjunctiveResults) {
if (isset($disjunctiveResults['facets'])) {
foreach ($disjunctiveResults['facets'] as $facetName => $facetValues) {
$queryResults['facets'][$facetName] = $facetValues;
}
}
}
return $queryResults;
}
private function getDisjunctiveQueries($queryParams)
{
$queriesParams = array();
foreach ($queryParams['disjunctiveFacets'] as $facetName) {
$params = $queryParams;
$params['facets'] = array($facetName);
$facetFilters = isset($params['facetFilters']) ? $params['facetFilters']: array();
$numericFilters = isset($params['numericFilters']) ? $params['numericFilters']: array();
$additionalParams = array(
'hitsPerPage' => 1,
'page' => 0,
'attributesToRetrieve' => array(),
'attributesToHighlight' => array(),
'attributesToSnippet' => array(),
'analytics' => false
);
$additionalParams['facetFilters'] = $this->getAlgoliaFiltersArrayWithoutCurrentRefinement($facetFilters, $facetName . ':');
$additionalParams['numericFilters'] = $this->getAlgoliaFiltersArrayWithoutCurrentRefinement($numericFilters, $facetName);
$queriesParams[$facetName] = array_merge($params, $additionalParams);
}
return $queriesParams;
}
private function getAlgoliaFiltersArrayWithoutCurrentRefinement($filters, $needle)
{
// iterate on each filters which can be string or array and filter out every refinement matching the needle
for ($i = 0; $i < count($filters); $i++) {
if (is_array($filters[$i])) {
foreach ($filters[$i] as $filter) {
if (mb_substr($filter, 0, mb_strlen($needle)) === $needle) {
unset($filters[$i]);
$filters = array_values($filters);
$i--;
break;
}
}
} else {
if (mb_substr($filters[$i], 0, mb_strlen($needle)) === $needle) {
unset($filters[$i]);
$filters = array_values($filters);
$i--;
}
}
}
return $filters;
}
}
$client = Algolia\AlgoliaSearch\SearchClient::create('APP_ID', 'API_KEY');
$indexName = 'movies';
$searcher = new DisjunctiveSearcher($client, $indexName);
$res = $searcher->search('chris evans', ['facetFilters' => ['year:1997'], 'disjunctiveFacets' => ['year', 'genre']]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment