1<?php 2 3/** 4 * The tests are relying on FTS index for beer-sample bucket exists with the following mapping for type 'beer': 5 * 6 * name | text | index | store | include in _all field | include term vectors 7 * description | text | index | store | include in _all field | include term vectors 8 * updated | datetime | index | store | include in _all field 9 * abv | number | index | store | include in _all field 10 * style | text | index | store | include in _all field | include term vectors 11 * category | text | index | store | include in _all field | include term vectors 12 */ 13class SearchTest extends PHPUnit_Framework_TestCase { 14 public function __construct() { 15 $this->testDsn = getenv('CB_DSN'); 16 if ($this->testDsn === FALSE) { 17 $this->testDsn = 'couchbase://localhost/'; 18 } 19 $this->authenticator = new \Couchbase\PasswordAuthenticator(); 20 $this->authenticator->username(getenv('CB_USER'))->password(getenv('CB_PASSWORD')); 21 } 22 23 protected function setUp() { 24 $this->cluster = new \Couchbase\Cluster($this->testDsn); 25 $this->cluster->authenticate($this->authenticator); 26 $this->bucket = $this->cluster->openBucket('beer-sample'); 27 } 28 29 function testSearchWithLimit() { 30 $queryPart = \Couchbase\SearchQuery::matchPhrase("hop beer"); 31 $query = new \Couchbase\SearchQuery("beer-search", $queryPart); 32 $query->limit(3); 33 34 $result = $this->bucket->query($query); 35 36 $this->assertNotNull($result); 37 $this->assertNotEmpty($result->hits); 38 $this->assertCount(2, $result->hits); 39 $this->assertEquals(2, $result->metrics['total_hits']); 40 41 foreach ($result->hits as $hit) { 42 $this->assertNotNull($hit->id); 43 $this->assertStringStartsWith("beer-search", $hit->index); 44 $this->assertGreaterThan(0, $hit->score); 45 } 46 } 47 48 function testSearchWithNoHits() { 49 $queryPart = \Couchbase\SearchQuery::matchPhrase("doesnotexistintheindex"); 50 $query = new \Couchbase\SearchQuery("beer-search", $queryPart); 51 $query->limit(3); 52 53 $result = $this->bucket->query($query); 54 55 $this->assertNotNull($result); 56 $this->assertEmpty($result->hits); 57 $this->assertEquals(0, $result->metrics['total_hits']); 58 } 59 60 function testSearchWithConsistency() { 61 $cluster = new \Couchbase\Cluster($this->testDsn . '?fetch_mutation_tokens=true'); 62 $cluster->authenticate($this->authenticator); 63 $bucket = $cluster->openBucket('beer-sample'); 64 65 $id = uniqid('testSearchWithConsistency'); 66 $queryPart = \Couchbase\SearchQuery::matchPhrase($id); 67 $query = new \Couchbase\SearchQuery("beer-search", $queryPart); 68 $query->limit(3); 69 70 $result = $bucket->query($query); 71 72 $this->assertNotNull($result); 73 $this->assertEmpty($result->hits); 74 $this->assertEquals(0, $result->metrics['total_hits']); 75 76 $result = $bucket->upsert($id, ["type" => "beer", "name" => $id]); 77 $mutationState = \Couchbase\MutationState::from($result); 78 79 $query->consistentWith($mutationState); 80 $result = $bucket->query($query); 81 82 $this->assertNotNull($result); 83 $this->assertNotEmpty($result->hits); 84 $this->assertEquals(1, $result->metrics['total_hits']); 85 $this->assertEquals($id, $result->hits[0]->id); 86 } 87 88 function testSearchWithFields() { 89 $queryPart = \Couchbase\SearchQuery::matchPhrase("hop beer"); 90 $query = new \Couchbase\SearchQuery("beer-search", $queryPart); 91 $query->limit(3)->fields("name"); 92 93 $result = $this->bucket->query($query); 94 95 $this->assertNotNull($result); 96 $this->assertNotEmpty($result->hits); 97 $this->assertCount(2, $result->hits); 98 $this->assertEquals(2, $result->metrics['total_hits']); 99 100 foreach ($result->hits as $hit) { 101 $this->assertNotNull($hit->id); 102 $this->assertStringStartsWith("beer-search", $hit->index); 103 $this->assertGreaterThan(0, $hit->score); 104 $this->assertObjectHasAttribute('fields', $hit); 105 $this->assertObjectHasAttribute('name', $hit->fields); 106 $this->assertNotNull($hit->fields->name); 107 } 108 } 109 110 function testSearchWithRanges() { 111 $queryPart = \Couchbase\SearchQuery::numericRange()->field("abv")->min(2.0)->max(3.2); 112 $query = new \Couchbase\SearchQuery("beer-search", $queryPart); 113 $query->fields("abv"); 114 $result = $this->bucket->query($query); 115 116 $this->assertNotNull($result); 117 $this->assertNotEmpty($result->hits); 118 $this->assertEquals(count($result->hits), $result->metrics['total_hits']); 119 120 foreach ($result->hits as $hit) { 121 $this->assertNotNull($hit->id); 122 $this->assertStringStartsWith("beer-search", $hit->index); 123 $this->assertGreaterThan(0, $hit->score); 124 $this->assertObjectHasAttribute('fields', $hit); 125 $this->assertObjectHasAttribute('abv', $hit->fields); 126 $this->assertGreaterThanOrEqual(2.0, $hit->fields->abv); 127 $this->assertLessThan(3.2, $hit->fields->abv); 128 } 129 130 $startDate = new DateTime('2010-11-01 10:00:00'); 131 $startStr = $startDate->format(DATE_RFC3339); 132 $endInt = mktime(20, 0, 0, 12, 1, 2010); 133 $endDate = DateTime::createFromFormat("U", $endInt); 134 $queryPart = \Couchbase\SearchQuery::conjuncts( 135 \Couchbase\SearchQuery::term("beer")->field("type"), 136 \Couchbase\SearchQuery::dateRange()->field("updated")->start($startStr)->end($endInt) 137 ); 138 $query = new \Couchbase\SearchQuery("beer-search", $queryPart); 139 $query->fields("updated", "type"); 140 $result = $this->bucket->query($query); 141 142 $this->assertNotNull($result); 143 $this->assertNotEmpty($result->hits); 144 $this->assertEquals(count($result->hits), $result->metrics['total_hits']); 145 146 foreach ($result->hits as $hit) { 147 $this->assertNotNull($hit->id); 148 $this->assertStringStartsWith("beer-search", $hit->index); 149 $this->assertGreaterThan(0, $hit->score); 150 $this->assertObjectHasAttribute('fields', $hit); 151 $this->assertObjectHasAttribute('updated', $hit->fields); 152 $hitDate = new DateTime($hit->fields->updated); 153 $diff = $startDate->diff($hitDate); 154 $this->assertEquals(0, $diff->invert, 155 "The hit->update date ({$hitDate->format(DATE_RFC3339)}) should go after start date ({$startDate->format(DATE_RFC3339)})"); 156 $diff = $endDate->diff($hitDate); 157 $this->assertEquals(1, $diff->invert, 158 "The hit->update date ({$hitDate->format(DATE_RFC3339)}) should go before or equals to end date ({$startDate->format(DATE_RFC3339)})"); 159 } 160 } 161 162 function testCompoundSearchQueries() { 163 $nameQuery = \Couchbase\SearchQuery::match("green")->field("name")->boost(3.4); 164 $descriptionQuery = \Couchbase\SearchQuery::match("hop")->field("description")->fuzziness(1); 165 166 $disjunctionQuery = \Couchbase\SearchQuery::disjuncts($nameQuery, $descriptionQuery); 167 $query = new \Couchbase\SearchQuery("beer-search", $disjunctionQuery); 168 $query->fields("type", "name", "description"); 169 $result = $this->bucket->query($query); 170 $this->assertGreaterThan(1000, $result->metrics['total_hits']); 171 $this->assertNotEmpty($result->hits); 172 $this->assertRegexp('/green/i', $result->hits[0]->fields->name); 173 $this->assertNotRegexp('/hop/i', $result->hits[0]->fields->name); 174 $this->assertRegexp('/hop/i', $result->hits[0]->fields->description); 175 $this->assertNotRegexp('/green/i', $result->hits[0]->fields->description); 176 177 $disjunctionQuery->min(2); 178 $query = new \Couchbase\SearchQuery("beer-search", $disjunctionQuery); 179 $query->fields("type", "name", "description"); 180 $result = $this->bucket->query($query); 181 $this->assertNotEmpty($result->hits); 182 $this->assertLessThan(10, $result->metrics['total_hits']); 183 $disjunction2Result = $result; 184 185 $conjunctionQuery = \Couchbase\SearchQuery::conjuncts($nameQuery, $descriptionQuery); 186 $query = new \Couchbase\SearchQuery("beer-search", $conjunctionQuery); 187 $query->fields("type", "name", "description"); 188 $result = $this->bucket->query($query); 189 $this->assertNotEmpty($result->hits); 190 $this->assertEquals(count($disjunction2Result->hits), count($result->hits)); 191 $this->assertEquals($disjunction2Result->hits[0]->fields->name, 192 $result->hits[0]->fields->name); 193 $this->assertEquals($disjunction2Result->hits[0]->fields->description, 194 $result->hits[0]->fields->description); 195 } 196 197 function testSearchWithFragments() { 198 $queryPart = \Couchbase\SearchQuery::match("hop beer"); 199 $query = new \Couchbase\SearchQuery("beer-search", $queryPart); 200 $query->limit(3)->highlight(\Couchbase\SearchQuery::HIGHLIGHT_HTML, "name"); 201 202 $result = $this->bucket->query($query); 203 $this->assertNotEmpty($result->hits); 204 205 foreach ($result->hits as $hit) { 206 $this->assertNotNull($hit->id); 207 $this->assertObjectHasAttribute('fragments', $hit); 208 $this->assertObjectHasAttribute('name', $hit->fragments); 209 foreach ($hit->fragments->name as $fragment) { 210 $this->assertRegexp('/<mark>/', $fragment); 211 } 212 } 213 } 214 215 function testSearchWithFacets() { 216 $queryPart = \Couchbase\SearchQuery::term("beer")->field("type"); 217 $query = new \Couchbase\SearchQuery("beer-search", $queryPart); 218 $query 219 ->addFacet("foo", \Couchbase\SearchQuery::termFacet("name", 3)) 220 ->addFacet("bar", \Couchbase\SearchQuery::dateRangeFacet("updated", 1) 221 //->addRange("old", NULL, mktime(0, 0, 0, 1, 1, 2014))) 222 ->addRange("old", NULL, "2014-01-01T00:00:00")) 223 ->addFacet("baz", \Couchbase\SearchQuery::numericRangeFacet("abv", 2) 224 ->addRange("strong", 4.9, NULL) 225 ->addRange("light", NULL, 4.89)); 226 227 $result = $this->bucket->query($query); 228 $this->assertNotEmpty($result->hits); 229 $this->assertObjectHasAttribute('facets', $result); 230 $this->assertNotEmpty($result->facets); 231 232 $this->assertNotNull($result->facets['foo']); 233 $this->assertEquals('name', $result->facets['foo']['field']); 234 $this->assertEquals('ale', $result->facets['foo']['terms'][0]['term']); 235 $this->assertGreaterThan(1000, $result->facets['foo']['terms'][0]['count']); 236 237 $this->assertNotNull($result->facets['bar']); 238 $this->assertEquals('updated', $result->facets['bar']['field']); 239 $this->assertEquals('old', $result->facets['bar']['date_ranges'][0]['name']); 240 $this->assertGreaterThan(5000, $result->facets['bar']['date_ranges'][0]['count']); 241 242 $this->assertNotNull($result->facets['baz']); 243 $this->assertEquals('abv', $result->facets['baz']['field']); 244 $this->assertEquals('light', $result->facets['baz']['numeric_ranges'][0]['name']); 245 $this->assertGreaterThan(100, $result->facets['baz']['numeric_ranges'][0]['count']); 246 } 247} 248