0byt3m1n1
Path:
/
data
/
applications
/
aps
/
tikiwiki
/
14.1-0
/
standard
/
htdocs
/
lib
/
core
/
Search
/
Elastic
/
[
Home
]
File: QueryBuilder.php
<?php // (c) Copyright 2002-2015 by authors of the Tiki Wiki CMS Groupware Project // // All Rights Reserved. See copyright.txt for details and a complete list of authors. // Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. // $Id: QueryBuilder.php 53803 2015-02-06 00:42:50Z jyhem $ use Search_Expr_Token as Token; use Search_Expr_And as AndX; use Search_Expr_Or as OrX; use Search_Expr_Not as NotX; use Search_Expr_Range as Range; use Search_Expr_Initial as Initial; use Search_Expr_MoreLikeThis as MoreLikeThis; use Search_Expr_ImplicitPhrase as ImplicitPhrase; class Search_Elastic_QueryBuilder { private $factory; private $documentReader; function __construct() { $this->factory = new Search_Elastic_TypeFactory; $this->documentReader = function ($type, $object) { return null; }; } function build(Search_Expr_Interface $expr) { $query = $expr->traverse($this); if (count($query) && isset($query['bool']) && empty($query['bool'])) { return []; } $query = array("query" => $query); return $query; } function setDocumentReader($callback) { $this->documentReader = $callback; } function __invoke($callback, $node, $childNodes) { if ($node instanceof ImplicitPhrase) { $node = $node->getBasicOperator(); } if ($node instanceof Token) { return $this->handleToken($node); } elseif (count($childNodes) === 1 && ($node instanceof AndX || $node instanceof OrX)) { return reset($childNodes)->traverse($callback); } elseif ($node instanceof OrX) { $inner = array_map( function ($expr) use ($callback) { return $expr->traverse($callback); }, $childNodes ); return array( 'bool' => array( 'should' => $this->flatten($inner, 'should'), "minimum_number_should_match" => 1, ), ); } elseif ($node instanceof AndX) { $not = array(); $inner = array_map( function ($expr) use ($callback) { return $expr->traverse($callback); }, $childNodes ); $inner = array_filter( $inner, function ($part) use (& $not) { // Only merge in the single-part NOT if (isset($part['bool']['must_not']) && count($part['bool']) == 1) { $not = array_merge($not, $part['bool']['must_not']); return false; } else { return true; } } ); $inner = $this->flatten($inner, 'must'); if (count($inner) == 1 && isset($inner[0]['bool'])) { $base = $inner[0]['bool']; if (! isset($base['must_not'])) { $base['must_not'] = array(); } $base['must_not'] = array_merge($base['must_not'], $not); return array( 'bool' => array_filter($base), ); } else { return array( 'bool' => array_filter( array( 'must' => $inner, 'must_not' => $not, ) ), ); } } elseif ($node instanceof NotX) { $inner = array_map( function ($expr) use ($callback) { return $expr->traverse($callback); }, $childNodes ); return array( 'bool' => array( 'must_not' => $inner, ), ); } elseif ($node instanceof Initial) { return array( 'match_phrase_prefix' => array( $node->getField() . '.sort' => array( "query" => $this->getTerm($node), "boost" => $node->getWeight(), ), ), ); } elseif ($node instanceof Range) { return array( 'range' => array( $node->getField() => array( "from" => $this->getTerm($node->getToken('from')), "to" => $this->getTerm($node->getToken('to')), "boost" => $node->getWeight(), "include_upper" => false, ), ), ); } elseif ($node instanceof MoreLikeThis) { $type = $node->getObjectType(); $object = $node->getObjectId(); $content = $node->getContent() ?: $this->getDocumentContent($type, $object); return array( 'more_like_this' => array( 'fields' => array($node->getField() ?: 'contents'), 'like_text' => $content, 'boost' => $node->getWeight(), ), ); } else { throw new Exception(tr('Feature not supported.')); } } private function flatten($list, $type) { // Only merge when alone, should queries contain the 'minimum_number_should_match' attribute $limit = ($type == 'should') ? 2 : 1; $out = array(); foreach ($list as $entry) { if (isset($entry['bool'][$type]) && count($entry['bool']) === $limit) { $out = array_merge($out, $entry['bool'][$type]); } else { $out[] = $entry; } } return $out; } private function getTerm($node) { $value = $node->getValue($this->factory); return strtolower($value->getValue()); } private function handleToken($node) { if ($node->getType() == 'identifier') { $value = $node->getValue($this->factory)->getValue(); return array("match" => array( $node->getField() => array("query" => $value), )); } elseif ($node->getType() == 'multivalue') { $value = $node->getValue($this->factory)->getValue(); return array("match" => array( $node->getField() => array("query" => reset($value)), )); } else { return array("match" => array( $node->getField() => array("query" => $this->getTerm($node), "boost" => $node->getWeight()), )); } } private function getDocumentContent($type, $object) { $cb = $this->documentReader; $document = $cb($type, $object); if (isset($document['contents'])) { return $document['contents']; } return ''; } }