/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.test.capedwarf.search.test;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import com.google.appengine.api.NamespaceManager;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.search.Document;
import com.google.appengine.api.search.Field;
import com.google.appengine.api.search.GeoPoint;
import com.google.appengine.api.search.Index;
import com.google.appengine.api.search.IndexSpec;
import com.google.appengine.api.search.PutResponse;
import com.google.appengine.api.search.ScoredDocument;
import com.google.appengine.api.search.SearchService;
import com.google.appengine.api.search.SearchServiceConfig;
import com.google.appengine.api.search.SearchServiceFactory;
import org.jboss.test.capedwarf.common.support.All;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import static org.junit.Assert.assertEquals;
/**
* @author <a href="mailto:mluksa@redhat.com">Marko Luksa</a>
*/
@Category(All.class)
public class SearchTest extends SearchTestBase {
@Test
public void testEmptyQueryReturnsAllDocuments() {
Index index = getTestIndex();
index.put(newDocument("fooaaa", newField("foo").setText("aaa")));
index.put(newDocument("foobbb", newField("bar").setText("bbb")));
assertSearchYields(index, "", "fooaaa", "foobbb");
}
@Test
public void testLeadingAndTrailingWhitespaceInQueryIsIgnored() {
Index index = getTestIndex();
index.put(newDocument("fooaaa", newField("foo").setText("aaa")));
index.put(newDocument("foobbb", newField("foo").setText("bbb")));
assertSearchYields(index, " ", "fooaaa", "foobbb");
assertSearchYields(index, "\t\n", "fooaaa", "foobbb");
assertSearchYields(index, " foo:aaa ", "fooaaa");
assertSearchYields(index, "\tfoo:aaa\t", "fooaaa");
assertSearchYields(index, "\nfoo:aaa\n", "fooaaa");
assertSearchYields(index, " \n foo:aaa \n ", "fooaaa");
}
@Test
public void testSearchBySingleField() {
Index index = getTestIndex();
index.put(newDocument("fooaaa", newField("foo").setText("aaa")));
index.put(newDocument("foobbb", newField("foo").setText("bbb")));
assertSearchYields(index, "foo:aaa", "fooaaa");
}
@Test
public void testSearchByTwoFields() {
Index index = getTestIndex();
index.put(newDocument("fooaaa", newField("foo").setText("aaa"), newField("bar").setText("bbb")));
index.put(newDocument("foobbb", newField("foo").setText("aaa"), newField("bar").setText("ccc")));
assertSearchYields(index, "foo:aaa bar:bbb", "fooaaa");
}
@Test
public void testSearchDisjunction() {
Index index = getTestIndex();
index.put(newDocument("fooaaa", newField("foo").setText("aaa"), newField("bar").setText("bbb")));
index.put(newDocument("foobbb", newField("foo").setText("bbb"), newField("bar").setText("ccc")));
index.put(newDocument("fooccc", newField("foo").setText("ccc"), newField("bar").setText("bbb")));
assertSearchYields(index, "foo:aaa OR bar:bbb", "fooaaa", "fooccc");
}
@Test
public void testSearchByTerm() {
Index index = getTestIndex();
index.put(newDocument("fooaaa", newField("foo").setText("bar aaa baz")));
index.put(newDocument("foobbb", newField("foo").setText("bar bbb baz")));
index.put(newDocument("fooaaa2", newField("foo").setText("baraaabaz")));
assertSearchYields(index, "foo:aaa", "fooaaa");
}
@Test
public void testSearchByPhrase() {
Index index = getTestIndex();
index.put(newDocument("fooaaa", newField("foo").setText("aaa bbb ccc ddd")));
index.put(newDocument("foobbb", newField("foo").setText("bbb ccc aaa ddd")));
assertSearchYields(index, "foo:\"aaa bbb ccc\"", "fooaaa");
}
@Test
public void testTextWithCommonWord() {
Index index = getTestIndex();
index.put(newDocument("fooaaa", newField("foo").setText("text with num 0"))); // "with" is a very common word
index.put(newDocument("foobbb", newField("foo").setText("text with num 1")));
assertSearchYields(index, "text with num", "fooaaa", "foobbb");
}
@Test
public void testSearchByStringEquality() {
Index index = getTestIndex();
index.put(newDocument("d", newField("foo").setText("ddd")));
index.put(newDocument("b", newField("foo").setText("bbb")));
index.put(newDocument("c", newField("foo").setText("ccc")));
index.put(newDocument("a", newField("foo").setText("aaa")));
assertSearchYields(index, "foo = bbb", "b");
}
@Test
public void testSearchByDateEqualityAndInequality() {
if (isRunningInsideGaeDevServer()) {
// this test only works on dev server if the _system_ timezone is set to UTC
return;
}
Index index = getTestIndex();
index.put(newDocument("d", newField("date").setDate(createDate(2004, 5, 5))));
index.put(newDocument("b", newField("date").setDate(createDate(2002, 5, 5))));
index.put(newDocument("c", newField("date").setDate(createDate(2003, 5, 5))));
index.put(newDocument("a", newField("date").setDate(createDate(2001, 5, 5))));
assertSearchYields(index, "date > 2002-05-05", "c", "d");
assertSearchYields(index, "date >= 2002-05-05", "b", "c", "d");
assertSearchYields(index, "date < 2002-05-05", "a");
assertSearchYields(index, "date <= 2002-05-05", "a", "b");
assertSearchYields(index, "date = 2002-05-05", "b");
}
@Test
public void testSearchByNumberEqualityAndInequality() {
Index index = getTestIndex();
index.put(newDocument("d", newField("num").setNumber(4.0d)));
index.put(newDocument("b", newField("num").setNumber(2.0d)));
index.put(newDocument("c", newField("num").setNumber(3.0d)));
index.put(newDocument("a", newField("num").setNumber(1.0d)));
assertSearchYields(index, "num > 2", "c", "d");
assertSearchYields(index, "num >= 2", "b", "c", "d");
assertSearchYields(index, "num < 2", "a");
assertSearchYields(index, "num <= 2", "a", "b");
assertSearchYields(index, "num = 2", "b");
assertSearchYields(index, "num:2", "b");
}
@Test
public void testSearchOnHtmlFieldIgnoresTags() {
Index index = getTestIndex();
index.put(newDocument("a", newField("html").setHTML("<html><body>hello</body></html>")));
index.put(newDocument("b", newField("html").setHTML("<html><body>body</body></html>")));
assertSearchYields(index, "html:body", "b");
}
@Test
public void testSearchForNumberInText() {
Index index = getTestIndex();
index.put(newDocument("a", newField("text").setText("Founded in 1993, Red Hat has its corporate headquarters in Raleigh, North Carolina with satellite offices worldwide.")));
assertSearchYields(index, "text:1993", "a");
}
@Test
public void testSearchForLocationWithinSpecifiedDistance() {
if (isRunningInsideGaeDevServer()) {
return; // dev appengine does not support geo points
}
Index index = getTestIndex();
index.put(newDocument("a", newField("location").setGeoPoint(new GeoPoint(45.0, 15.0))));
index.put(newDocument("b", newField("location").setGeoPoint(new GeoPoint(60.0, 40.0))));
assertSearchYields(index, "distance(location, geopoint(45.0, 15.0)) < 1000", "a");
}
@Test
public void testArgumentsOfDistanceFunctionCanBeSwapped() {
if (isRunningInsideGaeDevServer()) {
return; // dev appengine does not support geo points
}
Index index = getTestIndex();
index.put(newDocument("a", newField("location").setGeoPoint(new GeoPoint(45.0, 15.0))));
index.put(newDocument("b", newField("location").setGeoPoint(new GeoPoint(60.0, 40.0))));
assertSearchYields(index, "distance(geopoint(45.0, 15.0), location) < 1000", "a");
}
@Test
public void testSearchForLocationYieldsResultsInsideRadiusButNotInsideSquare() {
if (isRunningInsideGaeDevServer()) {
return; // dev appengine does not support geo points
}
Index index = getTestIndex();
index.put(newDocument("a", newField("location").setGeoPoint(new GeoPoint(46.051464, 14.515833))));
index.put(newDocument("b", newField("location").setGeoPoint(new GeoPoint(46.046111, 14.513889))));
assertSearchYields(index, "distance(location, geopoint(46.051464, 14.506097)) < 800", "a");
}
private Date createDate(int year, int month, int day) {
Calendar cal = Calendar.getInstance();
//noinspection MagicConstant
cal.set(year, month - 1, day, 0, 0, 0);
cal.clear(Calendar.MILLISECOND);
return cal.getTime();
}
@Ignore("No stemming yet")
@Test
public void testSearchByWordStem() {
Index index = getTestIndex();
index.put(newDocument("fooaaa", newField("foo").setText("rolling trolled")));
index.put(newDocument("foobbb", newField("foo").setText("bowling")));
assertSearchYields(index, "foo:roll", "fooaaa");
}
@Test
public void testSearchReturnsDocumentsInCorrectIndex() {
Index fooIndex = getIndex("fooIndex");
fooIndex.put(newDocument("foo", newField("foo").setText("aaa")));
Index barIndex = getIndex("barIndex");
barIndex.put(newDocument("bar", newField("foo").setText("aaa")));
assertSearchYields(fooIndex, "foo:aaa", "foo");
}
@Test
public void testSearchReturnsDocumentsInCorrectNamespace() {
Index fooIndex = getSearchService(FOO_NAMESPACE).getIndex(getIndexSpec("index"));
fooIndex.put(newDocument("foo", newField("foo").setText("aaa")));
Index barIndex = getSearchService(BAR_NAMESPACE).getIndex(getIndexSpec("index"));
barIndex.put(newDocument("bar", newField("foo").setText("aaa")));
assertSearchYields(fooIndex, "foo:aaa", "foo");
}
/**
* see https://issues.jboss.org/browse/CAPEDWARF-232
*/
@Test
public void testCD232Bug() {
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
for (int i = 0; i < 10; i++) {
createDataInNamespace(datastore, "namespace" + i, "index");
}
SearchService search = SearchServiceFactory.getSearchService(SearchServiceConfig.newBuilder().setNamespace("").build());
Index index = search.getIndex(IndexSpec.newBuilder().setName("index"));
assertSearchYields(index, "");
}
private void createDataInNamespace(DatastoreService datastore, String namespace, String indexName) {
NamespaceManager.set(namespace);
Entity entity = new Entity("kind", 1);
entity.setProperty("foo", 1);
datastore.put(entity);
SearchService search = SearchServiceFactory.getSearchService();
Index index = search.getIndex(IndexSpec.newBuilder().setName(indexName));
index.put(Document.newBuilder()
.addField(Field.newBuilder().setName("info").setText("index: " + indexName + " ns: " + namespace))
.build());
}
@Test
public void testSearchOnAllFields() {
Index index = getTestIndex();
index.put(newDocument(newField("foo").setText("aaa"), newField("bar").setText("bbb")));
index.put(newDocument(newField("foo").setText("bbb"), newField("bar").setText("aaa")));
index.put(newDocument(newField("foo").setText("bbb"), newField("bar").setText("bbb")));
assertEquals(2, index.search("aaa").getResults().size());
}
@Test
public void testComplexSearch1() {
Index index = getTestIndex();
index.put(newDocument("bm", newField("author").setText("Bob Marley")));
index.put(newDocument("rj", newField("author").setText("Rose Jones")));
index.put(newDocument("rt", newField("author").setText("Rose Trunk")));
index.put(newDocument("tj", newField("author").setText("Tom Jones")));
assertSearchYields(index, "author:(bob OR ((rose OR tom) AND jones))", "bm", "rj", "tj");
}
@Test
public void testSearchWithNegation() {
Index index = getTestIndex();
index.put(newDocument("with_baz", newField("body").setText("Foo bar baz")));
index.put(newDocument("without_baz", newField("body").setText("Foo bar.")));
assertSearchYields(index, "body:foo AND NOT body:baz", "without_baz");
assertSearchYields(index, "body:foo NOT body:baz", "without_baz");
}
@Test
public void testSearchWithDisjunctionAndNegation() {
Index index = getTestIndex();
index.put(newDocument("foo_with_baz", newField("body").setText("Foo bar baz")));
index.put(newDocument("foo_without_baz", newField("body").setText("Foo bar.")));
index.put(newDocument("without_foo_without_baz", newField("body").setText("bar.")));
index.put(newDocument("without_foo_with_baz", newField("body").setText("bar baz.")));
assertSearchYields(index, "body:foo OR NOT body:baz", "foo_with_baz", "foo_without_baz", "without_foo_without_baz");
}
@Test
public void testSearchWithNegationOnly() {
Index index = getTestIndex();
index.put(newDocument("with_baz", newField("body").setText("Foo bar baz")));
index.put(newDocument("without_baz", newField("body").setText("Foo bar.")));
assertSearchYields(index, "NOT body=baz", "without_baz");
}
@Test
public void testGet() {
Index index = getTestIndex();
PutResponse ar = index.put(newDocument("get_id", newField("acme").setText("bipbip")));
List<String> ids = ar.getIds();
Assert.assertNotNull(ids);
Assert.assertFalse(ids.isEmpty());
Assert.assertNotNull(index.get(ids.get(0)));
}
@Test(expected = IllegalArgumentException.class)
public void testGetOnlyField() {
Index index = getTestIndex();
index.put(newDocument("get_only_field", newField("foo").setText("foo"), newField("foo").setText("bar")));
for (ScoredDocument document : index.search("").getResults()) {
document.getOnlyField("foo");
}
}
}