// Copyright 2017 JanusGraph Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.janusgraph.diskstorage.indexing;
import com.google.common.base.Preconditions;
import com.google.common.collect.*;
import org.janusgraph.core.Cardinality;
import org.janusgraph.core.schema.Mapping;
import org.janusgraph.graphdb.internal.Order;
import org.janusgraph.core.schema.Parameter;
import org.janusgraph.core.attribute.*;
import org.janusgraph.diskstorage.BackendException;
import org.janusgraph.diskstorage.BaseTransactionConfig;
import org.janusgraph.diskstorage.EntryMetaData;
import org.janusgraph.diskstorage.util.StandardBaseTransactionConfig;
import org.janusgraph.diskstorage.util.time.TimestampProviders;
import org.janusgraph.graphdb.query.JanusGraphPredicate;
import org.janusgraph.graphdb.query.condition.*;
import org.janusgraph.testutil.RandomGenerator;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.time.Duration;
import java.time.Instant;
import java.util.*;
import static org.junit.Assert.*;
/**
* @author Matthias Broecheler (me@matthiasb.com)
*/
public abstract class IndexProviderTest {
private static final Random random = new Random();
private static final Parameter[] NO_PARAS = new Parameter[0];
protected IndexProvider index;
protected IndexFeatures indexFeatures;
protected IndexTransaction tx;
protected Map<String,KeyInformation> allKeys;
protected KeyInformation.IndexRetriever indexRetriever;
public static final String TEXT = "text", TIME = "time", WEIGHT = "weight", LOCATION = "location", BOUNDARY = "boundary", NAME = "name", PHONE_LIST = "phone_list", PHONE_SET = "phone_set", DATE = "date";
public static StandardKeyInformation of(Class<?> clazz, Cardinality cardinality, Parameter... paras) {
return new StandardKeyInformation(clazz, cardinality, paras);
}
public static final KeyInformation.IndexRetriever getIndexRetriever(final Map<String,KeyInformation> mappings) {
return new KeyInformation.IndexRetriever() {
@Override
public KeyInformation get(String store, String key) {
//Same for all stores
return mappings.get(key);
}
@Override
public KeyInformation.StoreRetriever get(String store) {
return new KeyInformation.StoreRetriever() {
@Override
public KeyInformation get(String key) {
return mappings.get(key);
}
};
}
};
}
public static final Map<String,KeyInformation> getMapping(final IndexFeatures indexFeatures) {
Preconditions.checkArgument(indexFeatures.supportsStringMapping(Mapping.TEXTSTRING) ||
(indexFeatures.supportsStringMapping(Mapping.TEXT) && indexFeatures.supportsStringMapping(Mapping.STRING)),
"Index must support string and text mapping");
return new HashMap<String,KeyInformation>() {{
put(TEXT,new StandardKeyInformation(String.class, Cardinality.SINGLE, new Parameter("mapping",
indexFeatures.supportsStringMapping(Mapping.TEXT)?Mapping.TEXT:Mapping.TEXTSTRING)));
put(TIME,new StandardKeyInformation(Long.class, Cardinality.SINGLE));
put(WEIGHT,new StandardKeyInformation(Double.class, Cardinality.SINGLE, new Parameter("mapping",Mapping.DEFAULT)));
put(LOCATION,new StandardKeyInformation(Geoshape.class, Cardinality.SINGLE));
put(BOUNDARY,new StandardKeyInformation(Geoshape.class, Cardinality.SINGLE, new Parameter("mapping",Mapping.PREFIX_TREE)));
put(NAME,new StandardKeyInformation(String.class, Cardinality.SINGLE, new Parameter("mapping",
indexFeatures.supportsStringMapping(Mapping.STRING)?Mapping.STRING:Mapping.TEXTSTRING)));
if(indexFeatures.supportsCardinality(Cardinality.LIST)) {
put(PHONE_LIST, new StandardKeyInformation(String.class, Cardinality.LIST, new Parameter("mapping",
indexFeatures.supportsStringMapping(Mapping.STRING) ? Mapping.STRING : Mapping.TEXTSTRING)));
}
if(indexFeatures.supportsCardinality(Cardinality.SET)) {
put(PHONE_SET, new StandardKeyInformation(String.class, Cardinality.SET, new Parameter("mapping",
indexFeatures.supportsStringMapping(Mapping.STRING) ? Mapping.STRING : Mapping.TEXTSTRING)));
}
put(DATE,new StandardKeyInformation(Instant.class, Cardinality.SINGLE));
}};
}
public abstract IndexProvider openIndex() throws BackendException;
public abstract boolean supportsLuceneStyleQueries();
@Before
public void setUp() throws Exception {
index = openIndex();
index.clearStorage();
index.close();
open();
}
public void open() throws BackendException {
index = openIndex();
indexFeatures = index.getFeatures();
allKeys = getMapping(indexFeatures);
indexRetriever = getIndexRetriever(allKeys);
newTx();
}
public void newTx() throws BackendException {
if (tx != null) tx.commit();
tx = openTx();
}
public IndexTransaction openTx() throws BackendException {
BaseTransactionConfig config = StandardBaseTransactionConfig.of(TimestampProviders.MILLI);
return new IndexTransaction(index, indexRetriever, config, Duration.ofMillis(2000L));
}
@After
public void tearDown() throws Exception {
close();
}
public void close() throws BackendException {
if (tx != null) tx.commit();
index.close();
}
public void clopen() throws BackendException {
close();
open();
}
@Test
public void openClose() {
}
@Test
public void singleStore() throws Exception {
storeTest("vertex");
}
@Test
public void multipleStores() throws Exception {
storeTest("vertex", "edge");
}
private void storeTest(String... stores) throws Exception {
Multimap<String, Object> doc1 = getDocument("Hello world", 1001, 5.2, Geoshape.point(48.0, 0.0), Geoshape.polygon(Arrays.asList(new double[][] {{-0.1,47.9},{0.1,47.9},{0.1,48.1},{-0.1,48.1},{-0.1,47.9}})),Arrays.asList("1", "2", "3"), Sets.newHashSet("1", "2"), Instant.ofEpochSecond(1));
Multimap<String, Object> doc2 = getDocument("Tomorrow is the world", 1010, 8.5, Geoshape.point(49.0, 1.0), Geoshape.line(Arrays.asList(new double[][] {{0.9,48.9},{0.9,49.1},{1.1,49.1},{1.1,48.9}})), Arrays.asList("4", "5", "6"), Sets.newHashSet("4", "5"), Instant.ofEpochSecond(2));
Multimap<String, Object> doc3 = getDocument("Hello Bob, are you there?", -500, 10.1, Geoshape.point(47.0, 10.0), Geoshape.polygon(Arrays.asList(new double[][] {{9.9,46.9},{10.1,46.9},{10.1,47.1},{9.9,47.1},{9.9,46.9}})), Arrays.asList("7", "8", "9"), Sets.newHashSet("7", "8"), Instant.ofEpochSecond(3));
for (String store : stores) {
initialize(store);
add(store, "doc1", doc1, true);
add(store, "doc2", doc2, true);
add(store, "doc3", doc3, false);
}
ImmutableList<IndexQuery.OrderEntry> orderTimeAsc = ImmutableList.of(new IndexQuery.OrderEntry(TIME, Order.ASC, Integer.class));
ImmutableList<IndexQuery.OrderEntry> orderWeightAsc = ImmutableList.of(new IndexQuery.OrderEntry(WEIGHT, Order.ASC, Double.class));
ImmutableList<IndexQuery.OrderEntry> orderTimeDesc = ImmutableList.of(new IndexQuery.OrderEntry(TIME, Order.DESC, Integer.class));
ImmutableList<IndexQuery.OrderEntry> orderWeightDesc = ImmutableList.of(new IndexQuery.OrderEntry(WEIGHT, Order.DESC, Double.class));
ImmutableList<IndexQuery.OrderEntry> jointOrder = ImmutableList.of(new IndexQuery.OrderEntry(WEIGHT, Order.DESC, Double.class), new IndexQuery.OrderEntry(TIME, Order.DESC, Integer.class));
clopen();
for (String store : stores) {
//Token
List<String> result = tx.query(new IndexQuery(store, PredicateCondition.of(TEXT, Text.CONTAINS, "world")));
assertEquals(ImmutableSet.of("doc1", "doc2"), ImmutableSet.copyOf(result));
assertEquals(ImmutableSet.copyOf(result), ImmutableSet.copyOf(tx.query(new IndexQuery(store, PredicateCondition.of(TEXT, Text.CONTAINS, "wOrLD")))));
assertEquals(1, tx.query(new IndexQuery(store, PredicateCondition.of(TEXT, Text.CONTAINS, "bob"))).size());
assertEquals(0, tx.query(new IndexQuery(store, PredicateCondition.of(TEXT, Text.CONTAINS, "worl"))).size());
assertEquals(1, tx.query(new IndexQuery(store, PredicateCondition.of(TEXT, Text.CONTAINS, "Tomorrow world"))).size());
assertEquals(1, tx.query(new IndexQuery(store, PredicateCondition.of(TEXT, Text.CONTAINS, "WorLD HELLO"))).size());
assertEquals(1, tx.query(new IndexQuery(store, PredicateCondition.of(TEXT, Text.CONTAINS_FUZZY, "boby"))).size());
//Ordering
result = tx.query(new IndexQuery(store, PredicateCondition.of(TEXT, Text.CONTAINS, "world"), orderTimeDesc));
assertEquals(ImmutableList.of("doc2", "doc1"), result);
result = tx.query(new IndexQuery(store, PredicateCondition.of(TEXT, Text.CONTAINS, "world"), orderWeightDesc));
assertEquals(ImmutableList.of("doc2", "doc1"), result);
result = tx.query(new IndexQuery(store, PredicateCondition.of(TEXT, Text.CONTAINS, "world"), orderTimeAsc));
assertEquals(ImmutableList.of("doc1", "doc2"), result);
result = tx.query(new IndexQuery(store, PredicateCondition.of(TEXT, Text.CONTAINS, "world"), orderWeightAsc));
assertEquals(ImmutableList.of("doc1", "doc2"), result);
result = tx.query(new IndexQuery(store, PredicateCondition.of(TEXT, Text.CONTAINS, "world"), jointOrder));
assertEquals(ImmutableList.of("doc2", "doc1"), result);
result = tx.query(new IndexQuery(store, PredicateCondition.of(TEXT, Text.CONTAINS_PREFIX, "w")));
assertEquals(ImmutableSet.of("doc1", "doc2"), ImmutableSet.copyOf(result));
result = tx.query(new IndexQuery(store, PredicateCondition.of(TEXT, Text.CONTAINS_PREFIX, "wOr")));
assertEquals(ImmutableSet.of("doc1", "doc2"), ImmutableSet.copyOf(result));
assertEquals(0,tx.query(new IndexQuery(store, PredicateCondition.of(TEXT, Text.CONTAINS_PREFIX, "bobi"))).size());
if (index.supports(new StandardKeyInformation(String.class, Cardinality.SINGLE), Text.CONTAINS_REGEX)) {
result = tx.query(new IndexQuery(store, PredicateCondition.of(TEXT, Text.CONTAINS_REGEX, "he[l]+(.*)")));
assertEquals(ImmutableSet.of("doc1", "doc3"), ImmutableSet.copyOf(result));
result = tx.query(new IndexQuery(store, PredicateCondition.of(TEXT, Text.CONTAINS_REGEX, "[h]+e[l]+(.*)")));
assertEquals(ImmutableSet.of("doc1", "doc3"), ImmutableSet.copyOf(result));
result = tx.query(new IndexQuery(store, PredicateCondition.of(TEXT, Text.CONTAINS_REGEX, "he[l]+")));
assertTrue(result.isEmpty());
result = tx.query(new IndexQuery(store, PredicateCondition.of(TEXT, Text.CONTAINS_REGEX, "e[l]+(.*)")));
assertTrue(result.isEmpty());
}
for (JanusGraphPredicate tp : new Text[]{Text.PREFIX, Text.REGEX}) {
try {
assertEquals(0, tx.query(new IndexQuery(store, PredicateCondition.of(TEXT, tp, "tzubull"))).size());
if (indexFeatures.supportsStringMapping(Mapping.TEXT)) fail();
} catch (IllegalArgumentException e) {}
}
//String
assertEquals(1, tx.query(new IndexQuery(store, PredicateCondition.of(NAME, Cmp.EQUAL, "Tomorrow is the world"))).size());
assertEquals(0, tx.query(new IndexQuery(store, PredicateCondition.of(NAME, Cmp.EQUAL, "world"))).size());
assertEquals(3, tx.query(new IndexQuery(store, PredicateCondition.of(NAME, Cmp.NOT_EQUAL, "bob"))).size());
assertEquals(1, tx.query(new IndexQuery(store, PredicateCondition.of(NAME, Text.PREFIX, "Tomorrow"))).size());
assertEquals(0, tx.query(new IndexQuery(store, PredicateCondition.of(NAME, Text.PREFIX, "wor"))).size());
assertEquals(1, tx.query(new IndexQuery(store, PredicateCondition.of(NAME, Text.FUZZY, "Tomorow is the world"))).size());
for (JanusGraphPredicate tp : new Text[]{Text.CONTAINS,Text.CONTAINS_PREFIX, Text.CONTAINS_REGEX}) {
try {
assertEquals(0, tx.query(new IndexQuery(store, PredicateCondition.of(NAME, tp, "tzubull"))).size());
if (indexFeatures.supportsStringMapping(Mapping.STRING)) fail();
} catch (IllegalArgumentException e) {}
}
if (index.supports(new StandardKeyInformation(String.class, Cardinality.SINGLE), Text.REGEX)) {
assertEquals(1, tx.query(new IndexQuery(store, PredicateCondition.of(NAME, Text.REGEX, "Tomo[r]+ow is.*world"))).size());
assertEquals(0, tx.query(new IndexQuery(store, PredicateCondition.of(NAME, Text.REGEX, "Tomorrow"))).size());
}
if (index.supports(new StandardKeyInformation(String.class, Cardinality.SINGLE, new Parameter("mapping", Mapping.STRING)), Text.REGEX)) {
assertEquals(1, tx.query(new IndexQuery(store, PredicateCondition.of(NAME, Text.REGEX, "Tomo[r]+ow is.*world"))).size());
assertEquals(0, tx.query(new IndexQuery(store, PredicateCondition.of(NAME, Text.REGEX, "Tomorrow"))).size());
}
result = tx.query(new IndexQuery(store, And.of(PredicateCondition.of(TEXT, Text.CONTAINS, "world"), PredicateCondition.of(TEXT, Text.CONTAINS, "hello"))));
assertEquals(1, result.size());
assertEquals("doc1", result.get(0));
result = tx.query(new IndexQuery(store, PredicateCondition.of(TIME, Cmp.EQUAL, -500)));
assertEquals(1, result.size());
assertEquals("doc3", result.get(0));
result = tx.query(new IndexQuery(store, And.of(Or.of(PredicateCondition.of(TIME, Cmp.EQUAL, 1001),PredicateCondition.of(TIME, Cmp.EQUAL, -500)))));
assertEquals(2, result.size());
result = tx.query(new IndexQuery(store, Not.of(PredicateCondition.of(TEXT, Text.CONTAINS, "world"))));
assertEquals(1, result.size());
assertEquals("doc3", result.get(0));
result = tx.query(new IndexQuery(store, And.of(PredicateCondition.of(TIME, Cmp.EQUAL, -500), Not.of(PredicateCondition.of(TEXT, Text.CONTAINS, "world")))));
assertEquals(1, result.size());
assertEquals("doc3", result.get(0));
result = tx.query(new IndexQuery(store, And.of(Or.of(PredicateCondition.of(TIME, Cmp.EQUAL, 1001),PredicateCondition.of(TIME, Cmp.EQUAL, -500)), PredicateCondition.of(TEXT, Text.CONTAINS, "world"))));
assertEquals(1, result.size());
assertEquals("doc1", result.get(0));
result = tx.query(new IndexQuery(store, PredicateCondition.of(TEXT, Text.CONTAINS, "Bob")));
assertEquals(1, result.size());
assertEquals("doc3", result.get(0));
result = tx.query(new IndexQuery(store, And.of(PredicateCondition.of(TEXT, Text.CONTAINS, "Bob"))));
assertEquals(1, result.size());
assertEquals("doc3", result.get(0));
result = tx.query(new IndexQuery(store, PredicateCondition.of(TEXT, Text.CONTAINS, "bob")));
assertEquals(1, result.size());
assertEquals("doc3", result.get(0));
result = tx.query(new IndexQuery(store, And.of(PredicateCondition.of(TEXT, Text.CONTAINS, "world"), PredicateCondition.of(WEIGHT, Cmp.GREATER_THAN, 6.0))));
assertEquals(1, result.size());
assertEquals("doc2", result.get(0));
result = tx.query(new IndexQuery(store, PredicateCondition.of(LOCATION, Geo.WITHIN, Geoshape.box(46.5, -0.5, 50.5, 10.5))));
assertEquals(3,result.size());
assertEquals(ImmutableSet.of("doc1", "doc2", "doc3"), ImmutableSet.copyOf(result));
result = tx.query(new IndexQuery(store, PredicateCondition.of(LOCATION, Geo.WITHIN, Geoshape.circle(48.5, 0.5, 200.00))));
assertEquals(2, result.size());
assertEquals(ImmutableSet.of("doc1", "doc2"), ImmutableSet.copyOf(result));
result = tx.query(new IndexQuery(store, PredicateCondition.of(BOUNDARY, Geo.WITHIN, Geoshape.box(46.5, -0.5, 50.5, 10.5))));
assertEquals(3,result.size());
assertEquals(ImmutableSet.of("doc1", "doc2", "doc3"), ImmutableSet.copyOf(result));
result = tx.query(new IndexQuery(store, PredicateCondition.of(BOUNDARY, Geo.WITHIN, Geoshape.circle(48.5, 0.5, 200.00))));
assertEquals(2, result.size());
assertEquals(ImmutableSet.of("doc1", "doc2"), ImmutableSet.copyOf(result));
result = tx.query(new IndexQuery(store, PredicateCondition.of(BOUNDARY, Geo.WITHIN, Geoshape.polygon(Arrays.asList(new double[][]
{{-5.0,47.0},{5.0,47.0},{5.0,50.0},{-5.0,50.0},{-5.0,47.0}})))));
assertEquals(2, result.size());
assertEquals(ImmutableSet.of("doc1","doc2"), ImmutableSet.copyOf(result));
if (index.supports(new StandardKeyInformation(Geoshape.class, Cardinality.SINGLE, new Parameter("mapping", Mapping.PREFIX_TREE)), Geo.DISJOINT)) {
result = tx.query(new IndexQuery(store, PredicateCondition.of(BOUNDARY, Geo.DISJOINT, Geoshape.box(46.5, -0.5, 50.5, 10.5))));
assertEquals(0,result.size());
result = tx.query(new IndexQuery(store, PredicateCondition.of(BOUNDARY, Geo.DISJOINT, Geoshape.circle(48.5, 0.5, 200.00))));
assertEquals(1, result.size());
assertEquals(ImmutableSet.of("doc3"), ImmutableSet.copyOf(result));
result = tx.query(new IndexQuery(store, PredicateCondition.of(BOUNDARY, Geo.DISJOINT, Geoshape.polygon(Arrays.asList(new double[][]
{{-5.0,47.0},{5.0,47.0},{5.0,50.0},{-5.0,50.0},{-5.0,47.0}})))));
assertEquals(1, result.size());
assertEquals(ImmutableSet.of("doc3"), ImmutableSet.copyOf(result));
}
result = tx.query(new IndexQuery(store, PredicateCondition.of(BOUNDARY, Geo.CONTAINS, Geoshape.point(47,10))));
assertEquals(1, result.size());
assertEquals(ImmutableSet.of("doc3"), ImmutableSet.copyOf(result));
result = tx.query(new IndexQuery(store, PredicateCondition.of(BOUNDARY, Geo.INTERSECT, Geoshape.box(48,-1,49,2))));
assertEquals(2,result.size());
assertEquals(ImmutableSet.of("doc1","doc2"), ImmutableSet.copyOf(result));
result = tx.query(new IndexQuery(store, PredicateCondition.of(BOUNDARY, Geo.INTERSECT, Geoshape.circle(48.5, 0.5, 200.00))));
assertEquals(2, result.size());
assertEquals(ImmutableSet.of("doc1", "doc2"), ImmutableSet.copyOf(result));
result = tx.query(new IndexQuery(store, PredicateCondition.of(BOUNDARY, Geo.INTERSECT, Geoshape.polygon(Arrays.asList(new double[][] {{-1.0,48.0},{2.0,48.0},{2.0,49.0},{-1.0,49.0},{-1.0,48.0}})))));
assertEquals(2, result.size());
assertEquals(ImmutableSet.of("doc1","doc2"), ImmutableSet.copyOf(result));
result = tx.query(new IndexQuery(store, And.of(PredicateCondition.of("text", Text.CONTAINS, "tomorrow"), PredicateCondition.of(LOCATION, Geo.WITHIN, Geoshape.circle(48.5, 0.5, 200.00)), PredicateCondition.of(BOUNDARY, Geo.WITHIN, Geoshape.circle(48.5, 0.5, 200.00)))));
assertEquals(ImmutableSet.of("doc2"), ImmutableSet.copyOf(result));
result = tx.query(new IndexQuery(store, And.of(PredicateCondition.of(TIME, Cmp.GREATER_THAN_EQUAL, -1000), PredicateCondition.of(TIME, Cmp.LESS_THAN, 1010), PredicateCondition.of(LOCATION, Geo.WITHIN, Geoshape.circle(48.5, 0.5, 1000.00)), PredicateCondition.of(BOUNDARY, Geo.WITHIN, Geoshape.circle(48.5, 0.5, 1000.00)))));
assertEquals(ImmutableSet.of("doc1", "doc3"), ImmutableSet.copyOf(result));
result = tx.query(new IndexQuery(store, And.of(PredicateCondition.of(WEIGHT, Cmp.GREATER_THAN, 10.0))));
assertEquals(ImmutableSet.of("doc3"), ImmutableSet.copyOf(result));
result = tx.query(new IndexQuery(store, And.of(PredicateCondition.of("blah", Cmp.GREATER_THAN, 10.0))));
assertEquals(0, result.size());
if (supportsLuceneStyleQueries()) {
assertEquals(1, Iterables.size(tx.query(new RawQuery(store,"text:\"Hello Bob\"",NO_PARAS))));
assertEquals(0, Iterables.size(tx.query(new RawQuery(store,"text:\"Hello Bob\"",NO_PARAS).setOffset(1))));
assertEquals(1, Iterables.size(tx.query(new RawQuery(store,"text:(world AND tomorrow)",NO_PARAS))));
// printResult(tx.query(new RawQuery(store,"text:(you there Hello Bob)",NO_PARAS)));
assertEquals(2, Iterables.size(tx.query(new RawQuery(store,"text:(you there Hello Bob)",NO_PARAS))));
assertEquals(1, Iterables.size(tx.query(new RawQuery(store,"text:(you there Hello Bob)",NO_PARAS).setLimit(1))));
assertEquals(1, Iterables.size(tx.query(new RawQuery(store,"text:(you there Hello Bob)",NO_PARAS).setLimit(1).setOffset(1))));
assertEquals(0, Iterables.size(tx.query(new RawQuery(store,"text:(you there Hello Bob)",NO_PARAS).setLimit(1).setOffset(2))));
assertEquals(2, Iterables.size(tx.query(new RawQuery(store,"text:\"world\"",NO_PARAS))));
assertEquals(2, Iterables.size(tx.query(new RawQuery(store,"time:[1000 TO 1020]",NO_PARAS))));
assertEquals(1, Iterables.size(tx.query(new RawQuery(store,"text:world AND time:1001",NO_PARAS))));
assertEquals(1, Iterables.size(tx.query(new RawQuery(store,"name:\"Hello world\"",NO_PARAS))));
}
if (index.supports(new StandardKeyInformation(String.class, Cardinality.LIST, new Parameter("mapping", Mapping.STRING)), Cmp.EQUAL)) {
assertEquals("doc1", tx.query(new IndexQuery(store, PredicateCondition.of(PHONE_LIST, Cmp.EQUAL, "1"))).get(0));
assertEquals("doc1", tx.query(new IndexQuery(store, PredicateCondition.of(PHONE_LIST, Cmp.EQUAL, "2"))).get(0));
assertEquals("doc2", tx.query(new IndexQuery(store, PredicateCondition.of(PHONE_LIST, Cmp.EQUAL, "4"))).get(0));
assertEquals("doc2", tx.query(new IndexQuery(store, PredicateCondition.of(PHONE_LIST, Cmp.EQUAL, "5"))).get(0));
assertEquals("doc3", tx.query(new IndexQuery(store, PredicateCondition.of(PHONE_LIST, Cmp.EQUAL, "7"))).get(0));
assertEquals("doc3", tx.query(new IndexQuery(store, PredicateCondition.of(PHONE_LIST, Cmp.EQUAL, "8"))).get(0));
assertEquals("doc1", tx.query(new IndexQuery(store, PredicateCondition.of(PHONE_SET, Cmp.EQUAL, "1"))).get(0));
assertEquals("doc1", tx.query(new IndexQuery(store, PredicateCondition.of(PHONE_SET, Cmp.EQUAL, "2"))).get(0));
assertEquals("doc2", tx.query(new IndexQuery(store, PredicateCondition.of(PHONE_SET, Cmp.EQUAL, "4"))).get(0));
assertEquals("doc2", tx.query(new IndexQuery(store, PredicateCondition.of(PHONE_SET, Cmp.EQUAL, "5"))).get(0));
assertEquals("doc3", tx.query(new IndexQuery(store, PredicateCondition.of(PHONE_SET, Cmp.EQUAL, "7"))).get(0));
assertEquals("doc3", tx.query(new IndexQuery(store, PredicateCondition.of(PHONE_SET, Cmp.EQUAL, "8"))).get(0));
}
assertEquals("doc1", tx.query(new IndexQuery(store, PredicateCondition.of(DATE, Cmp.EQUAL, Instant.ofEpochSecond(1)))).get(0));
assertEquals("doc2", tx.query(new IndexQuery(store, PredicateCondition.of(DATE, Cmp.EQUAL, Instant.ofEpochSecond(2)))).get(0));
assertEquals("doc3", tx.query(new IndexQuery(store, PredicateCondition.of(DATE, Cmp.EQUAL, Instant.ofEpochSecond(3)))).get(0));
assertEquals("doc3", tx.query(new IndexQuery(store, PredicateCondition.of(DATE, Cmp.GREATER_THAN, Instant.ofEpochSecond(2)))).get(0));
assertEquals(ImmutableSet.of("doc2", "doc3"), ImmutableSet.copyOf(tx.query(new IndexQuery(store, PredicateCondition.of(DATE, Cmp.GREATER_THAN_EQUAL, Instant.ofEpochSecond(2))))));
assertEquals(ImmutableSet.of("doc1"), ImmutableSet.copyOf(tx.query(new IndexQuery(store, PredicateCondition.of(DATE, Cmp.LESS_THAN, Instant.ofEpochSecond(2))))));
assertEquals(ImmutableSet.of("doc1", "doc2"), ImmutableSet.copyOf(tx.query(new IndexQuery(store, PredicateCondition.of(DATE, Cmp.LESS_THAN_EQUAL, Instant.ofEpochSecond(2))))));
assertEquals(ImmutableSet.of("doc1", "doc3"), ImmutableSet.copyOf(tx.query(new IndexQuery(store, PredicateCondition.of(DATE, Cmp.NOT_EQUAL, Instant.ofEpochSecond(2))))));
//Update some data
add(store, "doc4", getDocument("I'ts all a big Bob", -100, 11.2, Geoshape.point(48.0, 8.0), Geoshape.line(Arrays.asList(new double[][] {{7.5, 47.5}, {8.5, 48.5}})), Arrays.asList("10", "11", "12"), Sets.newHashSet("10", "11"), Instant.ofEpochSecond(4)), true);
remove(store, "doc2", doc2, true);
remove(store, "doc3", ImmutableMultimap.of(WEIGHT, (Object) 10.1), false);
add(store, "doc3", ImmutableMultimap.of(TIME, (Object) 2000, TEXT, "Bob owns the world"), false);
remove(store, "doc1", ImmutableMultimap.of(TIME, (Object) 1001), false);
add(store, "doc1", ImmutableMultimap.of(TIME, (Object) 1005, WEIGHT, 11.1), false);
}
clopen();
for (String store : stores) {
List<String> result = tx.query(new IndexQuery(store, PredicateCondition.of(TEXT, Text.CONTAINS, "world")));
assertEquals(ImmutableSet.of("doc1", "doc3"), ImmutableSet.copyOf(result));
result = tx.query(new IndexQuery(store, And.of(PredicateCondition.of(TEXT, Text.CONTAINS, "world"), PredicateCondition.of(WEIGHT, Cmp.GREATER_THAN, 6.0))));
assertEquals(1, result.size());
assertEquals("doc1", result.get(0));
result = tx.query(new IndexQuery(store, PredicateCondition.of(LOCATION, Geo.WITHIN, Geoshape.circle(48.5, 0.5, 200.00))));
assertEquals(ImmutableSet.of("doc1"), ImmutableSet.copyOf(result));
result = tx.query(new IndexQuery(store, PredicateCondition.of(BOUNDARY, Geo.WITHIN, Geoshape.circle(48.5, 0.5, 200.00))));
assertEquals(ImmutableSet.of("doc1"), ImmutableSet.copyOf(result));
result = tx.query(new IndexQuery(store, And.of(PredicateCondition.of(TEXT, Text.CONTAINS, "tomorrow"), PredicateCondition.of(LOCATION, Geo.WITHIN, Geoshape.circle(48.5, 0.5, 200.00)))));
assertEquals(ImmutableSet.of(), ImmutableSet.copyOf(result));
result = tx.query(new IndexQuery(store, And.of(PredicateCondition.of(TEXT, Text.CONTAINS, "tomorrow"), PredicateCondition.of(BOUNDARY, Geo.WITHIN, Geoshape.circle(48.5, 0.5, 200.00)))));
assertEquals(ImmutableSet.of(), ImmutableSet.copyOf(result));
result = tx.query(new IndexQuery(store, And.of(PredicateCondition.of(TIME, Cmp.GREATER_THAN_EQUAL, -1000), PredicateCondition.of(TIME, Cmp.LESS_THAN, 1010), PredicateCondition.of(LOCATION, Geo.WITHIN, Geoshape.circle(48.5, 0.5, 1000.00)))));
assertEquals(ImmutableSet.of("doc1", "doc4"), ImmutableSet.copyOf(result));
result = tx.query(new IndexQuery(store, And.of(PredicateCondition.of(TIME, Cmp.GREATER_THAN_EQUAL, -1000), PredicateCondition.of(TIME, Cmp.LESS_THAN, 1010), PredicateCondition.of(BOUNDARY, Geo.WITHIN, Geoshape.circle(48.5, 0.5, 1000.00)))));
assertEquals(ImmutableSet.of("doc1", "doc4"), ImmutableSet.copyOf(result));
result = tx.query(new IndexQuery(store, And.of(PredicateCondition.of(WEIGHT, Cmp.GREATER_THAN, 10.0))));
assertEquals(ImmutableSet.of("doc1", "doc4"), ImmutableSet.copyOf(result));
result = tx.query(new IndexQuery(store, And.of(PredicateCondition.of("blah", Cmp.GREATER_THAN, 10.0))));
assertEquals(0, result.size());
if (index.supports(new StandardKeyInformation(String.class, Cardinality.LIST, new Parameter("mapping", Mapping.STRING)), Cmp.EQUAL)) {
assertEquals("doc4", tx.query(new IndexQuery(store, PredicateCondition.of(PHONE_LIST, Cmp.EQUAL, "10"))).get(0));
assertEquals("doc4", tx.query(new IndexQuery(store, PredicateCondition.of(PHONE_LIST, Cmp.EQUAL, "11"))).get(0));
assertEquals("doc4", tx.query(new IndexQuery(store, PredicateCondition.of(PHONE_SET, Cmp.EQUAL, "10"))).get(0));
assertEquals("doc4", tx.query(new IndexQuery(store, PredicateCondition.of(PHONE_SET, Cmp.EQUAL, "11"))).get(0));
assertEquals(0, tx.query(new IndexQuery(store, PredicateCondition.of(PHONE_LIST, Cmp.EQUAL, "4"))).size());
assertEquals(0, tx.query(new IndexQuery(store, PredicateCondition.of(PHONE_LIST, Cmp.EQUAL, "5"))).size());
}
assertTrue(tx.query(new IndexQuery(store, PredicateCondition.of(DATE, Cmp.EQUAL, Instant.ofEpochSecond(2)))).isEmpty());
assertEquals("doc4", tx.query(new IndexQuery(store, PredicateCondition.of(DATE, Cmp.EQUAL, Instant.ofEpochSecond(4)))).get(0));
}
}
@Test
public void testCommonSupport() {
assertTrue(index.supports(of(String.class, Cardinality.SINGLE)));
assertTrue(index.supports(of(String.class, Cardinality.SINGLE, new Parameter("mapping", Mapping.TEXT))));
assertTrue(index.supports(of(String.class, Cardinality.SINGLE, new Parameter("mapping",Mapping.STRING))));
assertTrue(index.supports(of(Double.class, Cardinality.SINGLE)));
assertFalse(index.supports(of(Double.class, Cardinality.SINGLE, new Parameter("mapping",Mapping.TEXT))));
assertTrue(index.supports(of(Long.class, Cardinality.SINGLE)));
assertTrue(index.supports(of(Long.class, Cardinality.SINGLE, new Parameter("mapping",Mapping.DEFAULT))));
assertTrue(index.supports(of(Integer.class, Cardinality.SINGLE)));
assertTrue(index.supports(of(Short.class, Cardinality.SINGLE)));
assertTrue(index.supports(of(Byte.class, Cardinality.SINGLE)));
assertTrue(index.supports(of(Float.class, Cardinality.SINGLE)));
assertFalse(index.supports(of(Object.class, Cardinality.SINGLE)));
assertFalse(index.supports(of(Exception.class, Cardinality.SINGLE)));
assertTrue(index.supports(of(Double.class, Cardinality.SINGLE), Cmp.EQUAL));
assertTrue(index.supports(of(Double.class, Cardinality.SINGLE), Cmp.GREATER_THAN_EQUAL));
assertTrue(index.supports(of(Double.class, Cardinality.SINGLE), Cmp.LESS_THAN));
assertTrue(index.supports(of(Double.class, Cardinality.SINGLE, new Parameter("mapping",Mapping.DEFAULT)), Cmp.LESS_THAN));
assertFalse(index.supports(of(Double.class, Cardinality.SINGLE, new Parameter("mapping",Mapping.TEXT)), Cmp.LESS_THAN));
assertFalse(index.supports(of(Double.class, Cardinality.SINGLE), Geo.INTERSECT));
assertFalse(index.supports(of(Long.class, Cardinality.SINGLE), Text.CONTAINS));
assertTrue(index.supports(of(Geoshape.class, Cardinality.SINGLE)));
assertTrue(index.supports(of(Geoshape.class, Cardinality.SINGLE), Geo.WITHIN));
assertTrue(index.supports(of(Geoshape.class, Cardinality.SINGLE), Geo.INTERSECT));
assertTrue(index.supports(of(Geoshape.class, Cardinality.SINGLE, new Parameter("mapping",Mapping.PREFIX_TREE)), Geo.WITHIN));
assertTrue(index.supports(of(Geoshape.class, Cardinality.SINGLE, new Parameter("mapping",Mapping.PREFIX_TREE)), Geo.CONTAINS));
assertTrue(index.supports(of(Geoshape.class, Cardinality.SINGLE, new Parameter("mapping",Mapping.PREFIX_TREE)), Geo.INTERSECT));
}
@Test
public void largeTest() throws Exception {
int numDoc = 30000;
String store = "vertex";
initialize(store);
for (int i = 1; i <= numDoc; i++) {
add(store, "doc" + i, getRandomDocument(), true);
}
clopen();
// List<String> result = tx.query(new IndexQuery(store, And.of(PredicateCondition.of(WEIGHT, Cmp.INTERVAL, Interval.of(0.2,0.3)))));
// List<String> result = tx.query(new IndexQuery(store, And.of(PredicateCondition.of(LOCATION, Geo.WITHIN,Geoshape.circle(48.5,0.5,1000.00)))));
long time = System.currentTimeMillis();
List<String> result = tx.query(new IndexQuery(store, And.of(PredicateCondition.of(WEIGHT, Cmp.GREATER_THAN_EQUAL, 0.2), PredicateCondition.of(WEIGHT, Cmp.LESS_THAN, 0.6), PredicateCondition.of(LOCATION, Geo.WITHIN, Geoshape.circle(48.5, 0.5, 1000.00)))));
int oldresultSize = result.size();
System.out.println(result.size() + " vs " + (numDoc / 1000 * 2.4622623015));
System.out.println("Query time on " + numDoc + " docs (ms): " + (System.currentTimeMillis() - time));
result = tx.query(new IndexQuery(store, And.of(PredicateCondition.of(WEIGHT, Cmp.GREATER_THAN_EQUAL, 0.2), PredicateCondition.of(WEIGHT, Cmp.LESS_THAN, 0.6), PredicateCondition.of(LOCATION, Geo.WITHIN, Geoshape.circle(48.5, 0.5, 1000.00))), numDoc / 1000));
assertEquals(numDoc / 1000, result.size());
result = tx.query(new IndexQuery(store, And.of(PredicateCondition.of(WEIGHT, Cmp.GREATER_THAN_EQUAL, 0.2), PredicateCondition.of(WEIGHT, Cmp.LESS_THAN, 0.6), PredicateCondition.of(LOCATION, Geo.WITHIN, Geoshape.circle(48.5, 0.5, 1000.00))), numDoc / 1000 * 100));
assertEquals(oldresultSize, result.size());
}
@Test
public void testRestore() throws Exception {
final String store1 = "store1";
final String store2 = "store2";
initialize(store1);
initialize(store2);
// add couple of documents with weight > 4.0d
add(store1, "restore-doc1", ImmutableMultimap.of(NAME, "first", TIME, 1L, WEIGHT, 10.2d), true);
add(store1, "restore-doc2", ImmutableMultimap.of(NAME, "second", TIME, 2L, WEIGHT, 4.7d), true);
clopen();
// initial query
Set<String> results = Sets.newHashSet(tx.query(new IndexQuery(store1, And.of(PredicateCondition.of(WEIGHT, Cmp.GREATER_THAN_EQUAL, 4.0)))));
assertEquals(2, results.size());
// now let's try to restore (change values on the existing doc2, delete doc1, and add a new doc)
index.restore(new HashMap<String, Map<String, List<IndexEntry>>>() {{
put(store1, new HashMap<String, List<IndexEntry>>() {{
put("restore-doc1", Collections.<IndexEntry>emptyList());
put("restore-doc2", new ArrayList<IndexEntry>() {{
add(new IndexEntry(NAME, "not-second"));
add(new IndexEntry(WEIGHT, 2.1d));
add(new IndexEntry(TIME, 0L));
}});
put("restore-doc3", new ArrayList<IndexEntry>() {{
add(new IndexEntry(NAME, "third"));
add(new IndexEntry(WEIGHT, 11.5d));
add(new IndexEntry(TIME, 3L));
}});
}});
}}, indexRetriever, tx);
clopen();
// this should return only doc3 (let's make results a set so it filters out duplicates but still has a size)
results = Sets.newHashSet(tx.query(new IndexQuery(store1, And.of(PredicateCondition.of(WEIGHT, Cmp.GREATER_THAN_EQUAL, 4.0)))));
assertEquals(1, results.size());
assertTrue(results.contains("restore-doc3"));
// check if the name and time was set correctly for doc3
results = Sets.newHashSet(tx.query(new IndexQuery(store1, And.of(PredicateCondition.of(NAME, Cmp.EQUAL, "third"), PredicateCondition.of(TIME, Cmp.EQUAL, 3L)))));
assertEquals(1, results.size());
assertTrue(results.contains("restore-doc3"));
// let's check if all of the new properties where set correctly from doc2
results = Sets.newHashSet(tx.query(new IndexQuery(store1, And.of(PredicateCondition.of(NAME, Cmp.EQUAL, "not-second"), PredicateCondition.of(TIME, Cmp.EQUAL, 0L)))));
assertEquals(1, results.size());
assertTrue(results.contains("restore-doc2"));
// now let's throw one more store in the mix (resurrect doc1 in store1 and add it to the store2)
index.restore(new HashMap<String, Map<String, List<IndexEntry>>>() {{
put(store1, new HashMap<String, List<IndexEntry>>() {{
put("restore-doc1", new ArrayList<IndexEntry>() {{
add(new IndexEntry(NAME, "first-restored"));
add(new IndexEntry(WEIGHT, 7.0d));
add(new IndexEntry(TIME, 4L));
}});
}});
put(store2, new HashMap<String, List<IndexEntry>>() {{
put("restore-doc1", new ArrayList<IndexEntry>() {{
add(new IndexEntry(NAME, "first-in-second-store"));
add(new IndexEntry(WEIGHT, 4.0d));
add(new IndexEntry(TIME, 5L));
}});
}});
}}, indexRetriever, tx);
clopen();
// let's query store1 to see if we got doc1 back
results = Sets.newHashSet(tx.query(new IndexQuery(store1, And.of(PredicateCondition.of(WEIGHT, Cmp.GREATER_THAN_EQUAL, 4.0)))));
assertEquals(2, results.size());
assertTrue(results.contains("restore-doc1"));
assertTrue(results.contains("restore-doc3"));
// check if the name and time was set correctly for doc1
results = Sets.newHashSet(tx.query(new IndexQuery(store1, And.of(PredicateCondition.of(NAME, Cmp.EQUAL, "first-restored"), PredicateCondition.of(TIME, Cmp.EQUAL, 4L)))));
assertEquals(1, results.size());
assertTrue(results.contains("restore-doc1"));
// now let's check second store and see if we got doc1 added there too
results = Sets.newHashSet(tx.query(new IndexQuery(store2, And.of(PredicateCondition.of(WEIGHT, Cmp.GREATER_THAN_EQUAL, 4.0)))));
assertEquals(1, results.size());
assertTrue(results.contains("restore-doc1"));
// check if the name and time was set correctly for doc1 (in second store)
results = Sets.newHashSet(tx.query(new IndexQuery(store2, And.of(PredicateCondition.of(NAME, Cmp.EQUAL, "first-in-second-store"), PredicateCondition.of(TIME, Cmp.EQUAL, 5L)))));
assertEquals(1, results.size());
assertTrue(results.contains("restore-doc1"));
}
@Test
public void testTTL() throws Exception {
if (!index.getFeatures().supportsDocumentTTL())
return;
final String store = "store1";
initialize(store);
// add couple of documents with weight > 4.0d
add(store, "expiring-doc1", ImmutableMultimap.of(NAME, "first", TIME, 1L, WEIGHT, 10.2d), true, 2);
add(store, "expiring-doc2", ImmutableMultimap.of(NAME, "second", TIME, 2L, WEIGHT, 4.7d), true);
add(store, "expiring-doc3", ImmutableMultimap.of(NAME, "third", TIME, 3L, WEIGHT, 5.2d), true, 2);
add(store, "expiring-doc4", ImmutableMultimap.of(NAME, "fourth", TIME, 3L, WEIGHT, 7.7d), true, 7);// bigger ttl then one recycle interval, should still show up in the results
clopen();
// initial query
Set<String> results = Sets.newHashSet(tx.query(new IndexQuery(store, And.of(PredicateCondition.of(WEIGHT, Cmp.GREATER_THAN_EQUAL, 4.0)))));
assertEquals(4, results.size());
Thread.sleep(6000); // sleep for elastic search ttl recycle
results = Sets.newHashSet(tx.query(new IndexQuery(store, And.of(PredicateCondition.of(WEIGHT, Cmp.GREATER_THAN_EQUAL, 4.0)))));
assertEquals(2, results.size());
assertTrue(results.contains("expiring-doc2"));
assertTrue(results.contains("expiring-doc4"));
Thread.sleep(5000); // sleep for elastic search ttl recycle
results = Sets.newHashSet(tx.query(new IndexQuery(store, And.of(PredicateCondition.of(WEIGHT, Cmp.GREATER_THAN_EQUAL, 4.0)))));
assertEquals(1, results.size());
assertTrue(results.contains("expiring-doc2"));
}
/* ==================================================================================
CONCURRENT UPDATE CASES
==================================================================================*/
private final String defStore = "store1";
private final String defDoc = "docx1";
private final String defTextValue = "the quick brown fox jumps over the lazy dog";
private interface TxJob {
void run(IndexTransaction tx);
}
private void runConflictingTx(TxJob job1, TxJob job2) throws Exception {
initialize(defStore);
Multimap<String, Object> initialProps = ImmutableMultimap.of(TEXT, defTextValue);
add(defStore, defDoc, initialProps, true);
clopen();
// Sanity check
checkResult(new IndexQuery(defStore, PredicateCondition.of(TEXT, Text.CONTAINS, "brown")),defDoc);
checkResult(new IndexQuery(defStore, PredicateCondition.of(TEXT, Text.CONTAINS, "periwinkle")),null);
IndexTransaction tx1 = openTx(), tx2 = openTx();
job1.run(tx1);
tx1.commit();
job2.run(tx2);
tx2.commit();
clopen();
}
private void checkResult(IndexQuery query, String containedDoc) throws Exception {
List<String> result = tx.query(query);
if (containedDoc!=null) {
assertEquals(1, result.size());
assertEquals(containedDoc, result.get(0));
} else {
assertEquals(0, result.size());
}
}
@Test
public void testDeleteDocumentThenDeleteField() throws Exception {
runConflictingTx(new TxJob() {
@Override
public void run(IndexTransaction tx) {
tx.delete(defStore, defDoc, TEXT, ImmutableMap.of(), true);
}
}, new TxJob() {
@Override
public void run(IndexTransaction tx) {
tx.delete(defStore, defDoc, TEXT, defTextValue, false);
}
});
// Document must not exist
checkResult(new IndexQuery(defStore, PredicateCondition.of(TEXT, Text.CONTAINS, "brown")),null);
}
@Test
public void testDeleteDocumentThenModifyField() throws Exception {
runConflictingTx(new TxJob() {
@Override
public void run(IndexTransaction tx) {
tx.delete(defStore, defDoc, TEXT, ImmutableMap.of(), true);
}
}, new TxJob() {
@Override
public void run(IndexTransaction tx) {
tx.add(defStore, defDoc, TEXT, "the slow brown fox jumps over the lazy dog", false);
}
});
//2nd tx should put document back into existence
checkResult(new IndexQuery(defStore, PredicateCondition.of(TEXT, Text.CONTAINS, "brown")),defDoc);
}
@Test
public void testDeleteDocumentThenAddField() throws Exception {
final String nameValue = "jm keynes";
runConflictingTx(new TxJob() {
@Override
public void run(IndexTransaction tx) {
tx.delete(defStore, defDoc, TEXT, ImmutableMap.of(), true);
}
}, new TxJob() {
@Override
public void run(IndexTransaction tx) {
tx.add(defStore, defDoc, NAME, nameValue, false);
}
});
// TEXT field should have been deleted when document was
checkResult(new IndexQuery(defStore, PredicateCondition.of(TEXT, Text.CONTAINS, "brown")),null);
// but name field should be visible
checkResult(new IndexQuery(defStore, PredicateCondition.of(NAME, Cmp.EQUAL, nameValue)),defDoc);
}
@Test
public void testAddFieldThenDeleteDoc() throws Exception {
final String nameValue = "jm keynes";
runConflictingTx(new TxJob() {
@Override
public void run(IndexTransaction tx) {
tx.add(defStore, defDoc, NAME, nameValue, false);
}
}, new TxJob() {
@Override
public void run(IndexTransaction tx) {
tx.delete(defStore, defDoc, TEXT, ImmutableMap.of(), true);
}
});
//neither should be visible
checkResult(new IndexQuery(defStore, PredicateCondition.of(TEXT, Text.CONTAINS, "brown")),null);
checkResult(new IndexQuery(defStore, PredicateCondition.of(NAME, Cmp.EQUAL, nameValue)),null);
}
@Test
public void testConflictingAdd() throws Exception {
final String doc2 = "docy2";
runConflictingTx(new TxJob() {
@Override
public void run(IndexTransaction tx) {
Multimap<String, Object> initialProps = ImmutableMultimap.<String, Object>of(TEXT, "sugar sugar");
add(defStore, doc2, initialProps, true);
}
}, new TxJob() {
@Override
public void run(IndexTransaction tx) {
Multimap<String, Object> initialProps = ImmutableMultimap.<String, Object>of(TEXT, "honey honey");
add(defStore, doc2, initialProps, true);
}
});
//only last write should be visible
checkResult(new IndexQuery(defStore, PredicateCondition.of(TEXT, Text.CONTAINS, "brown")),defDoc);
checkResult(new IndexQuery(defStore, PredicateCondition.of(TEXT, Text.CONTAINS, "sugar")),null);
checkResult(new IndexQuery(defStore, PredicateCondition.of(TEXT, Text.CONTAINS, "honey")),doc2);
}
@Test
public void testLastWriteWins() throws Exception {
runConflictingTx(new TxJob() {
@Override
public void run(IndexTransaction tx) {
tx.delete(defStore, defDoc, TEXT, defTextValue, false);
tx.add(defStore, defDoc, TEXT, "sugar sugar", false);
}
}, new TxJob() {
@Override
public void run(IndexTransaction tx) {
tx.delete(defStore, defDoc, TEXT, defTextValue, false);
tx.add(defStore, defDoc, TEXT, "honey honey", false);
}
});
//only last write should be visible
checkResult(new IndexQuery(defStore, PredicateCondition.of(TEXT, Text.CONTAINS, "brown")),null);
checkResult(new IndexQuery(defStore, PredicateCondition.of(TEXT, Text.CONTAINS, "sugar")),null);
checkResult(new IndexQuery(defStore, PredicateCondition.of(TEXT, Text.CONTAINS, "honey")),defDoc);
}
/**
* Test overwriting a single existing field on an existing document
* (isNew=false). Non-contentious test.
*
*/
@Test
public void testUpdateAddition() throws Exception {
final String revisedText = "its a sunny day";
runConflictingTx(new TxJob() {
@Override
public void run(IndexTransaction tx) {
tx.add(defStore, defDoc, TEXT, revisedText, false);
}
}, new TxJob() {
@Override
public void run(IndexTransaction tx) {
//do nothing
}
});
// Should no longer return old text
checkResult(new IndexQuery(defStore, PredicateCondition.of(TEXT, Text.CONTAINS, "brown")), null);
// but new one
checkResult(new IndexQuery(defStore, PredicateCondition.of(TEXT, Text.CONTAINS, "sunny")),defDoc);
}
/**
* Test deleting a single field from a single document (deleteAll=false).
* Non-contentious test.
*
*/
@Test
public void testUpdateDeletion() throws Exception {
runConflictingTx(new TxJob() {
@Override
public void run(IndexTransaction tx) {
tx.delete(defStore, defDoc, TEXT, ImmutableMap.of(), false);
}
}, new TxJob() {
@Override
public void run(IndexTransaction tx) {
//do nothing
}
});
// Should no longer return deleted text
checkResult(new IndexQuery(defStore, PredicateCondition.of(TEXT, Text.CONTAINS, "brown")),null);
}
/* ==================================================================================
HELPER METHODS
==================================================================================*/
protected void initialize(String store) throws BackendException {
for (Map.Entry<String,KeyInformation> info : allKeys.entrySet()) {
KeyInformation keyInfo = info.getValue();
if (index.supports(keyInfo)) index.register(store,info.getKey(),keyInfo,tx);
}
}
protected void add(String store, String docid, Multimap<String, Object> doc, boolean isNew) {
add(store, docid, doc, isNew, 0);
}
private void add(String store, String docid, Multimap<String, Object> doc, boolean isNew, int ttlInSeconds) {
for (Map.Entry<String, Object> kv : doc.entries()) {
if (!index.supports(allKeys.get(kv.getKey())))
continue;
IndexEntry idx = new IndexEntry(kv.getKey(), kv.getValue());
if (ttlInSeconds > 0)
idx.setMetaData(EntryMetaData.TTL, ttlInSeconds);
tx.add(store, docid, idx, isNew);
}
}
private void remove(String store, String docid, Multimap<String, Object> doc, boolean deleteAll) {
for (Map.Entry<String, Object> kv : doc.entries()) {
if (index.supports(allKeys.get(kv.getKey()))) {
tx.delete(store, docid, kv.getKey(), kv.getValue(), deleteAll);
}
}
}
public Multimap<String, Object> getDocument(final String txt, final long time, final double weight, final Geoshape location, final Geoshape boundary, List<String> phoneList, Set<String> phoneSet, Instant date) {
HashMultimap<String, Object> values = HashMultimap.create();
values.put(TEXT, txt);
values.put(NAME, txt);
values.put(TIME, time);
values.put(WEIGHT, weight);
values.put(LOCATION, location);
values.put(BOUNDARY, boundary);
values.put(DATE, date);
if(indexFeatures.supportsCardinality(Cardinality.LIST)) {
for (String phone : phoneList) {
values.put(PHONE_LIST, phone);
}
}
if(indexFeatures.supportsCardinality(Cardinality.SET)) {
for (String phone : phoneSet) {
values.put(PHONE_SET, phone);
}
}
return values;
}
public static Multimap<String, Object> getRandomDocument() {
final StringBuilder s = new StringBuilder();
for (int i = 0; i < 3; i++) s.append(RandomGenerator.randomString(5, 8)).append(" ");
Multimap values = HashMultimap.create();
values.put(TEXT, s.toString());
values.put(NAME, s.toString());
values.put(TIME, Math.abs(random.nextLong()));
values.put(WEIGHT, random.nextDouble());
values.put(LOCATION, Geoshape.point(random.nextDouble() * 180 - 90, random.nextDouble() * 360 - 180));
return values;
}
public static void printResult(Iterable<RawQuery.Result<String>> result) {
for (RawQuery.Result<String> r : result) {
System.out.println(r.getResult() + ":"+r.getScore());
}
}
}