From: <lph...@us...> - 2013-06-10 19:26:37
|
Revision: 46273 http://sourceforge.net/p/tikiwiki/code/46273 Author: lphuberdeau Date: 2013-06-10 19:26:34 +0000 (Mon, 10 Jun 2013) Log Message: ----------- [NEW]?\194?\160Adding basic structure to support facet search in elasticsearch Modified Paths: -------------- trunk/lib/core/Search/Elastic/Index.php trunk/lib/core/Search/Index/Interface.php trunk/lib/core/Search/Index/Lucene.php trunk/lib/core/Search/Index/Memory.php trunk/lib/core/Search/Query.php trunk/lib/core/Search/ResultSet.php Added Paths: ----------- trunk/lib/core/Search/Elastic/FacetBuilder.php trunk/lib/core/Search/Elastic/FacetReader.php trunk/lib/core/Search/Query/Facet/ trunk/lib/core/Search/Query/Facet/Interface.php trunk/lib/core/Search/Query/Facet/Term.php trunk/lib/core/Search/Query/Interface.php trunk/lib/core/Search/ResultSet/FacetFilter.php trunk/lib/test/core/Search/Elastic/FacetBuilderTest.php trunk/lib/test/core/Search/Elastic/FacetReaderTest.php trunk/lib/test/core/Search/Elastic/FacetTest.php Added: trunk/lib/core/Search/Elastic/FacetBuilder.php =================================================================== --- trunk/lib/core/Search/Elastic/FacetBuilder.php (rev 0) +++ trunk/lib/core/Search/Elastic/FacetBuilder.php 2013-06-10 19:26:34 UTC (rev 46273) @@ -0,0 +1,33 @@ +<?php +// (c) Copyright 2002-2013 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$ + +class Search_Elastic_FacetBuilder +{ + function build(array $facets) + { + if (empty($facets)) { + return array(); + } + + $out = array(); + foreach ($facets as $facet) { + $out[$facet->getName()] = $this->buildFacet($facet); + } + + return array( + 'facets' => $out, + ); + } + + private function buildFacet(Search_Query_Facet_Interface $facet) + { + return array('terms' => array( + 'field' => $facet->getField(), + )); + } +} + Property changes on: trunk/lib/core/Search/Elastic/FacetBuilder.php ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +Id \ No newline at end of property Added: trunk/lib/core/Search/Elastic/FacetReader.php =================================================================== --- trunk/lib/core/Search/Elastic/FacetReader.php (rev 0) +++ trunk/lib/core/Search/Elastic/FacetReader.php 2013-06-10 19:26:34 UTC (rev 46273) @@ -0,0 +1,41 @@ +<?php +// (c) Copyright 2002-2013 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$ + +class Search_Elastic_FacetReader +{ + private $data; + + function __construct(stdClass $data) + { + $this->data = $data; + } + + function getFacetFilter(Search_Query_Facet_Interface $facet) + { + $facetName = $facet->getName(); + + if (! isset($this->data->facets->$facetName)) { + return null; + } + + $entry = $this->data->facets->$facetName; + + return new Search_ResultSet_FacetFilter($facet, $this->getFromTerms($entry)); + } + + private function getFromTerms($entry) + { + $out = array(); + + foreach ($entry->terms as $term) { + $out[] = array('value' => $term->term, 'count' => $term->count); + } + + return $out; + } +} + Property changes on: trunk/lib/core/Search/Elastic/FacetReader.php ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +Id \ No newline at end of property Modified: trunk/lib/core/Search/Elastic/Index.php =================================================================== --- trunk/lib/core/Search/Elastic/Index.php 2013-06-10 19:20:01 UTC (rev 46272) +++ trunk/lib/core/Search/Elastic/Index.php 2013-06-10 19:26:34 UTC (rev 46273) @@ -96,17 +96,21 @@ } } - function find(Search_Expr_Interface $expr, Search_Query_Order $sortOrder, $resultStart, $resultCount) + function find(Search_Query_Interface $query, $resultStart, $resultCount) { $builder = new Search_Elastic_QueryBuilder; - $query = $builder->build($expr); + $queryPart = $builder->build($query->getExpr()); $builder = new Search_Elastic_OrderBuilder; - $order = $builder->build($sortOrder); + $orderPart = $builder->build($query->getSortOrder()); - $query = array_merge( - $query, - $order, + $builder = new Search_Elastic_FacetBuilder; + $facetPart = $builder->build($query->getFacets()); + + $fullQuery = array_merge( + $queryPart, + $orderPart, + $facetPart, array( "from" => $resultStart, "size" => $resultCount, @@ -120,7 +124,7 @@ ) ); - $result = $this->connection->search($this->index, $query, $resultStart, $resultCount); + $result = $this->connection->search($this->index, $fullQuery, $resultStart, $resultCount); $hits = $result->hits; $entries = array_map( @@ -139,6 +143,13 @@ $resultSet = new Search_Elastic_ResultSet($entries, $hits->total, $resultStart, $resultCount); + $reader = new Search_Elastic_FacetReader($result); + foreach ($query->getFacets() as $facet) { + if ($filter = $reader->getFacetFilter($facet)) { + $resultSet->addFacetFilter($filter); + } + } + return $resultSet; } Modified: trunk/lib/core/Search/Index/Interface.php =================================================================== --- trunk/lib/core/Search/Index/Interface.php 2013-06-10 19:20:01 UTC (rev 46272) +++ trunk/lib/core/Search/Index/Interface.php 2013-06-10 19:26:34 UTC (rev 46273) @@ -13,7 +13,7 @@ function endUpdate(); - function find(Search_Expr_Interface $query, Search_Query_Order $sortOrder, $resultStart, $resultCount); + function find(Search_Query_Interface $query, $resultStart, $resultCount); function getTypeFactory(); Modified: trunk/lib/core/Search/Index/Lucene.php =================================================================== --- trunk/lib/core/Search/Index/Lucene.php 2013-06-10 19:20:01 UTC (rev 46272) +++ trunk/lib/core/Search/Index/Lucene.php 2013-06-10 19:26:34 UTC (rev 46273) @@ -96,9 +96,10 @@ return $query->getExpr(); } - function find(Search_Expr_Interface $query, Search_Query_Order $sortOrder, $resultStart, $resultCount) + function find(Search_Query_Interface $query, $resultStart, $resultCount) { - $data = $this->internalFind($query, $sortOrder); + $expr = $query->getExpr(); + $data = $this->internalFind($expr, $query->getSortOrder()); $result = array_slice($data['result'], $resultStart, $resultCount); @@ -106,7 +107,7 @@ $resultSet->setEstimate($data['count']); if ($this->highlight) { - $resultSet->setHighlightHelper(new Search_Index_Lucene_HighlightHelper($query)); + $resultSet->setHighlightHelper(new Search_Index_Lucene_HighlightHelper($expr)); } else { $resultSet->setHighlightHelper(new Search_ResultSet_SnippetHelper); } Modified: trunk/lib/core/Search/Index/Memory.php =================================================================== --- trunk/lib/core/Search/Index/Memory.php 2013-06-10 19:20:01 UTC (rev 46272) +++ trunk/lib/core/Search/Index/Memory.php 2013-06-10 19:26:34 UTC (rev 46273) @@ -26,10 +26,10 @@ { } - function find(Search_Expr_Interface $query, Search_Query_Order $sortOrder, $resultStart, $resultCount) + function find(Search_Query_Interface $query, $resultStart, $resultCount) { - $this->lastQuery = $query; - $this->lastOrder = $sortOrder; + $this->lastQuery = $query->getExpr(); + $this->lastOrder = $query->getSortOrder(); $this->lastStart = $resultStart; $this->lastCount = $resultCount; return array(); Added: trunk/lib/core/Search/Query/Facet/Interface.php =================================================================== --- trunk/lib/core/Search/Query/Facet/Interface.php (rev 0) +++ trunk/lib/core/Search/Query/Facet/Interface.php 2013-06-10 19:26:34 UTC (rev 46273) @@ -0,0 +1,13 @@ +<?php +// (c) Copyright 2002-2013 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$ + +interface Search_Query_Facet_Interface +{ + function getName(); + function getField(); +} + Property changes on: trunk/lib/core/Search/Query/Facet/Interface.php ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +Id \ No newline at end of property Added: trunk/lib/core/Search/Query/Facet/Term.php =================================================================== --- trunk/lib/core/Search/Query/Facet/Term.php (rev 0) +++ trunk/lib/core/Search/Query/Facet/Term.php 2013-06-10 19:26:34 UTC (rev 46273) @@ -0,0 +1,27 @@ +<?php +// (c) Copyright 2002-2013 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$ + +class Search_Query_Facet_Term implements Search_Query_Facet_Interface +{ + private $field; + + function __construct($field) + { + $this->field = $field; + } + + function getName() + { + return $this->field; + } + + function getField() + { + return $this->field; + } +} + Property changes on: trunk/lib/core/Search/Query/Facet/Term.php ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +Id \ No newline at end of property Added: trunk/lib/core/Search/Query/Interface.php =================================================================== --- trunk/lib/core/Search/Query/Interface.php (rev 0) +++ trunk/lib/core/Search/Query/Interface.php 2013-06-10 19:26:34 UTC (rev 46273) @@ -0,0 +1,13 @@ +<?php +// (c) Copyright 2002-2013 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$ + +interface Search_Query_Interface +{ + function getExpr(); + function getSortOrder(); + function getFacets(); +} Property changes on: trunk/lib/core/Search/Query/Interface.php ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +Id \ No newline at end of property Modified: trunk/lib/core/Search/Query.php =================================================================== --- trunk/lib/core/Search/Query.php 2013-06-10 19:20:01 UTC (rev 46272) +++ trunk/lib/core/Search/Query.php 2013-06-10 19:26:34 UTC (rev 46273) @@ -5,7 +5,7 @@ // Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. // $Id$ -class Search_Query +class Search_Query implements Search_Query_Interface { private $objectList; private $expr; @@ -16,6 +16,7 @@ private $identifierFields = null; private $subQueries = array(); + private $facets = array(); function __construct($query = null) { @@ -185,14 +186,17 @@ $this->weightCalculator = $calculator; } - function search(Search_Index_Interface $index) + function getSortOrder() { if ($this->sortOrder) { - $sortOrder = $this->sortOrder; + return $this->sortOrder; } else { - $sortOrder = Search_Query_Order::getDefault(); + return Search_Query_Order::getDefault(); } + } + function search(Search_Index_Interface $index) + { if ($this->weightCalculator) { $this->expr->walk(array($this->weightCalculator, 'calculate')); } @@ -206,7 +210,7 @@ }); } - return $index->find($this->expr, $sortOrder, $this->start, $this->count); + return $index->find($this, $this->start, $this->count); } function getExpr() @@ -259,4 +263,14 @@ return $this->subQueries[$name]; } + + function requestFacet(Search_Query_Facet_Interface $facet) + { + $this->facets[] = $facet; + } + + function getFacets() + { + return $this->facets; + } } Added: trunk/lib/core/Search/ResultSet/FacetFilter.php =================================================================== --- trunk/lib/core/Search/ResultSet/FacetFilter.php (rev 0) +++ trunk/lib/core/Search/ResultSet/FacetFilter.php 2013-06-10 19:26:34 UTC (rev 46273) @@ -0,0 +1,24 @@ +<?php +// (c) Copyright 2002-2013 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$ + +class Search_ResultSet_FacetFilter +{ + private $facet; + private $data; + + function __construct(Search_Query_Facet_Interface $facet, array $data) + { + $this->facet = $facet; + $this->data = $data; + } + + function isFacet(Search_Query_Facet_Interface $facet) + { + return $this->facet->getName() === $facet->getName(); + } +} + Property changes on: trunk/lib/core/Search/ResultSet/FacetFilter.php ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +Id \ No newline at end of property Modified: trunk/lib/core/Search/ResultSet.php =================================================================== --- trunk/lib/core/Search/ResultSet.php 2013-06-10 19:20:01 UTC (rev 46272) +++ trunk/lib/core/Search/ResultSet.php 2013-06-10 19:26:34 UTC (rev 46273) @@ -13,6 +13,7 @@ private $maxRecords; private $highlightHelper; + private $filters = array(); public static function create($list) { @@ -102,5 +103,19 @@ { return $this->count > $this->offset + $this->maxRecords; } + + function getFacet(Search_Query_Facet_Interface $facet) + { + foreach ($this->filters as $filter) { + if ($filter->isFacet($facet)) { + return $filter; + } + } + } + + function addFacetFilter(Search_ResultSet_FacetFilter $facet) + { + $this->filters[] = $facet; + } } Added: trunk/lib/test/core/Search/Elastic/FacetBuilderTest.php =================================================================== --- trunk/lib/test/core/Search/Elastic/FacetBuilderTest.php (rev 0) +++ trunk/lib/test/core/Search/Elastic/FacetBuilderTest.php 2013-06-10 19:26:34 UTC (rev 46273) @@ -0,0 +1,52 @@ +<?php +// (c) Copyright 2002-2013 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$ + +class Search_Elastic_FacetBuilderTest extends PHPUnit_Framework_TestCase +{ + private $builder; + + function setUp() + { + $this->builder = new Search_Elastic_FacetBuilder; + } + + function testBuildNoFacet() + { + $this->assertEquals(array(), $this->builder->build(array())); + } + + function testBuildSingleFacet() + { + $this->assertEquals(array( + 'facets' => array( + 'categories' => array( + 'terms' => array('field' => 'categories'), + ), + ), + ), $this->builder->build(array( + new Search_Query_Facet_Term('categories'), + ))); + } + + function testBuildMultipleFacets() + { + $this->assertEquals(array( + 'facets' => array( + 'categories' => array( + 'terms' => array('field' => 'categories'), + ), + 'deep_categories' => array( + 'terms' => array('field' => 'deep_categories'), + ), + ), + ), $this->builder->build(array( + new Search_Query_Facet_Term('categories'), + new Search_Query_Facet_Term('deep_categories'), + ))); + } +} + Property changes on: trunk/lib/test/core/Search/Elastic/FacetBuilderTest.php ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +Id \ No newline at end of property Added: trunk/lib/test/core/Search/Elastic/FacetReaderTest.php =================================================================== --- trunk/lib/test/core/Search/Elastic/FacetReaderTest.php (rev 0) +++ trunk/lib/test/core/Search/Elastic/FacetReaderTest.php 2013-06-10 19:26:34 UTC (rev 46273) @@ -0,0 +1,57 @@ +<?php +// (c) Copyright 2002-2013 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$ + +class Search_Elastic_FacetReaderTest extends PHPUnit_Framework_TestCase +{ + private $reader; + + function setUp() + { + $this->reader = new Search_Elastic_FacetReader((object) array( + 'facets' => (object) array( + 'categories' => (object) array( + '_type' => "terms", + 'missing' => 0, + 'total' => 7, + 'other' => 0, + 'terms' => array( + (object) array( + 'term' => "1", + 'count' => 3, + ), + (object) array( + 'term' => "2", + 'count' => 2, + ), + (object) array( + 'term' => "3", + 'count' => 1, + ), + ), + ), + ), + )); + } + + function testReadUnavailable() + { + $this->assertNull($this->reader->getFacetFilter(new Search_Query_Facet_Term('foobar'))); + } + + function testReadAvailable() + { + $facet = new Search_Query_Facet_Term('categories'); + $expect = new Search_ResultSet_FacetFilter($facet, array( + array('value' => "1", 'count' => 3), + array('value' => "2", 'count' => 2), + array('value' => "3", 'count' => 1), + )); + + $this->assertEquals($expect, $this->reader->getFacetFilter($facet)); + } +} + Property changes on: trunk/lib/test/core/Search/Elastic/FacetReaderTest.php ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +Id \ No newline at end of property Added: trunk/lib/test/core/Search/Elastic/FacetTest.php =================================================================== --- trunk/lib/test/core/Search/Elastic/FacetTest.php (rev 0) +++ trunk/lib/test/core/Search/Elastic/FacetTest.php 2013-06-10 19:26:34 UTC (rev 46273) @@ -0,0 +1,72 @@ +<?php +// (c) Copyright 2002-2013 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$ + +class Search_Elastic_FacetTest extends PHPUnit_Framework_TestCase +{ + function setUp() + { + $connection = new Search_Elastic_Connection('http://localhost:9200'); + + $status = $connection->getStatus(); + if (! $status->ok) { + $this->markTestSkipped('ElasticSearch needs to be available on localhost:9200 for the test to run.'); + } + + $this->index = new Search_Elastic_Index($connection, 'test_index'); + $this->index->destroy(); + + $this->populate($this->index); + } + + function tearDown() + { + if ($this->index) { + $this->index->destroy(); + } + } + + function testRequireFacet() + { + $facet = new Search_Query_Facet_Term('categories'); + + $query = new Search_Query; + $query->filterType('wiki page'); + $query->requestFacet($facet); + + $result = $query->search($this->index); + $values = $result->getFacet($facet); + + $this->assertEquals(new Search_ResultSet_FacetFilter($facet, array( + array('value' => 1, 'count' => 3), + array('value' => 2, 'count' => 2), + array('value' => 'orphan', 'count' => 1), + array('value' => 3, 'count' => 1), + )), $values); + } + + protected function populate($index) + { + $this->add($index, 'ABC', array(1, 2, 3)); + $this->add($index, 'AB', array(1, 2)); + $this->add($index, 'A', array(1)); + $this->add($index, 'empty', array('orphan')); + } + + private function add($index, $page, array $categories) + { + $typeFactory = $index->getTypeFactory(); + + $index->addDocument( + array( + 'object_type' => $typeFactory->identifier('wiki page'), + 'object_id' => $typeFactory->identifier($page), + 'categories' => $typeFactory->multivalue($categories), + ) + ); + } +} + Property changes on: trunk/lib/test/core/Search/Elastic/FacetTest.php ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +Id \ No newline at end of property This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |