/*
Copyright 2013 Red Hat, Inc. and/or its affiliates.
This file is part of lightblue.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.redhat.lightblue.mindex;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;
import java.util.stream.Collectors;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.redhat.lightblue.metadata.EntityMetadata;
import com.redhat.lightblue.metadata.PredefinedFields;
import com.redhat.lightblue.metadata.parser.Extensions;
import com.redhat.lightblue.metadata.parser.JSONMetadataParser;
import com.redhat.lightblue.metadata.TypeResolver;
import com.redhat.lightblue.metadata.FieldTreeNode;
import com.redhat.lightblue.metadata.ArrayField;
import com.redhat.lightblue.metadata.Metadata;
import com.redhat.lightblue.metadata.types.DefaultTypes;
import com.redhat.lightblue.metadata.test.DatabaseMetadata;
import com.redhat.lightblue.assoc.QueryFieldInfo;
import com.redhat.lightblue.query.*;
import com.redhat.lightblue.util.JsonDoc;
import com.redhat.lightblue.util.JsonUtils;
import com.redhat.lightblue.util.Path;
import com.redhat.lightblue.util.test.AbstractJsonSchemaTest;
import com.redhat.lightblue.TestDataStoreParser;
public class MemDocIndexTest extends AbstractJsonSchemaTest {
private EntityMetadata getMd(String fname) {
try {
JsonNode node = loadJsonNode(fname);
Extensions<JsonNode> extensions = new Extensions<>();
extensions.addDefaultExtensions();
extensions.registerDataStoreParser("mongo", new TestDataStoreParser<JsonNode>());
TypeResolver resolver = new DefaultTypes();
JSONMetadataParser parser = new JSONMetadataParser(extensions, resolver, JsonNodeFactory.instance);
EntityMetadata md = parser.parseEntityMetadata(node);
PredefinedFields.ensurePredefinedFields(md);
return md;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private QueryExpression query(String s) throws Exception {
return QueryExpression.fromJson(JsonUtils.json(s.replaceAll("\'", "\"")));
}
/**
* field1: "field1:i"
* field2: "field2:i"
* field3: null
" field4: null for odd docs, "1" for even docs
* field7.i.elemf1: "doc:i elemf1:j"
* field7.i.elemf2: "doc:i elemf2:j" fot even i, null for others
*/
private List<JsonDoc> fill() {
// Build doc list
List<JsonDoc> docs=new ArrayList<>();
for(int i=0;i<100;i++) {
JsonDoc doc=new JsonDoc(JsonNodeFactory.instance.objectNode());
doc.modify(new Path("objectType"),JsonNodeFactory.instance.textNode("test"),true);
doc.modify(new Path("field1"),JsonNodeFactory.instance.textNode("field1:"+i),true);
doc.modify(new Path("field2"),JsonNodeFactory.instance.textNode("field2:"+i),true);
doc.modify(new Path("field2"),JsonNodeFactory.instance.textNode("field2:"+i),true);
if(i%2==0) {
doc.modify(new Path("field4"),JsonNodeFactory.instance.numberNode(i),true);
}
for(int j=0;j<100;j++) {
doc.modify(new Path("field7."+j+".elemf1"),JsonNodeFactory.instance.textNode("doc:"+i+" elemf1:"+j),true);
if(i%2==0) {
doc.modify(new Path("field7."+j+".elemf2"),JsonNodeFactory.instance.textNode("doc:"+i+" elemf2:"+j),true);
}
}
docs.add(doc);
}
return docs;
}
public QueryFieldInfo qfi(EntityMetadata md,
String entityRelativeFieldName,
String entityRelativeFieldNameWithContext) {
return new QueryFieldInfo(null,
null,
md.resolve(new Path(entityRelativeFieldNameWithContext)),
null,
new Path(entityRelativeFieldName),
new Path(entityRelativeFieldNameWithContext),
null,
true);
}
@Test
public void simpleValueLookupTest() throws Exception {
EntityMetadata md=getMd("testMetadata.json");
List<JsonDoc> docs=fill();
// Simple indexing using field1
SimpleKeySpec spec=new SimpleKeySpec(qfi(md,"field1","field1"));
MemDocIndex index=new MemDocIndex(spec);
// Add all docs
for(JsonDoc doc:docs)
index.add(doc);
// Search
Set<JsonDoc> results=index.find(new ValueLookupSpec(spec,"field1:10"));
Assert.assertEquals(1,results.size());
Assert.assertEquals("field1:10",results.iterator().next().get(new Path("field1")).asText());
results=index.find(new ValueLookupSpec(spec,"field1:1011"));
Assert.assertEquals(0,results.size());
}
@Test
public void simpleValueNullLookupTest() throws Exception {
EntityMetadata md=getMd("testMetadata.json");
List<JsonDoc> docs=fill();
// Simple indexing using field4 - it has nulls
SimpleKeySpec spec=new SimpleKeySpec(qfi(md,"field4","field4"));
MemDocIndex index=new MemDocIndex(spec);
// Add all docs
for(JsonDoc doc:docs)
index.add(doc);
// Search
Set<JsonDoc> results=index.find(new ValueLookupSpec(spec,new Integer(10)));
Assert.assertEquals(1,results.size());
Assert.assertEquals("field1:10",results.iterator().next().get(new Path("field1")).asText());
results=index.find(new ValueLookupSpec(spec,new Integer(11)));
Assert.assertEquals(0,results.size());
results=index.find(new ValueLookupSpec(spec,null));
Assert.assertEquals(50,results.size());
}
@Test
public void multiValueLookupTest() throws Exception {
EntityMetadata md=getMd("testMetadata.json");
List<JsonDoc> docs=fill();
// Simple indexing using field1
SimpleKeySpec spec=new SimpleKeySpec(qfi(md,"field1","field1"));
MemDocIndex index=new MemDocIndex(spec);
// Add all docs
for(JsonDoc doc:docs)
index.add(doc);
// Search
HashSet<Object> values=new HashSet<>();
values.add("field1:10");
values.add("field1:11");
values.add("field1:1101");
Set<JsonDoc> results=index.find(new MultiValueLookupSpec(spec,values));
Assert.assertEquals(2,results.size());
Assert.assertTrue(results.stream().map(d->d.get(new Path("field1")).asText()).collect(Collectors.toSet()).contains("field1:10"));
}
@Test
public void rangeValueLookupTest() throws Exception {
EntityMetadata md=getMd("testMetadata.json");
List<JsonDoc> docs=fill();
// Simple indexeing using field1
SimpleKeySpec spec=new SimpleKeySpec(qfi(md,"field1","field1"));
MemDocIndex index=new MemDocIndex(spec);
// Add all docs
for(JsonDoc doc:docs)
index.add(doc);
Set<JsonDoc> results=index.find(new RangeLookupSpec(spec,"field1:10","field1:15"));
Assert.assertEquals(6,results.size());
Assert.assertTrue(results.stream().map(d->d.get(new Path("field1")).asText()).collect(Collectors.toSet()).contains("field1:10"));
Assert.assertTrue(results.stream().map(d->d.get(new Path("field1")).asText()).collect(Collectors.toSet()).contains("field1:11"));
Assert.assertTrue(results.stream().map(d->d.get(new Path("field1")).asText()).collect(Collectors.toSet()).contains("field1:12"));
Assert.assertTrue(results.stream().map(d->d.get(new Path("field1")).asText()).collect(Collectors.toSet()).contains("field1:13"));
Assert.assertTrue(results.stream().map(d->d.get(new Path("field1")).asText()).collect(Collectors.toSet()).contains("field1:14"));
Assert.assertTrue(results.stream().map(d->d.get(new Path("field1")).asText()).collect(Collectors.toSet()).contains("field1:15"));
}
@Test
public void prefixLookupTest() throws Exception {
EntityMetadata md=getMd("testMetadata.json");
List<JsonDoc> docs=fill();
// Simple indexeing using field1
SimpleKeySpec spec=new SimpleKeySpec(qfi(md,"field1","field1"));
MemDocIndex index=new MemDocIndex(spec);
// Add all docs
for(JsonDoc doc:docs)
index.add(doc);
Set<JsonDoc> results=index.find(new PrefixLookupSpec(spec,"field1:1",true));
Assert.assertEquals(11,results.size());
Assert.assertTrue(results.stream().map(d->d.get(new Path("field1")).asText()).collect(Collectors.toSet()).contains("field1:1"));
Assert.assertTrue(results.stream().map(d->d.get(new Path("field1")).asText()).collect(Collectors.toSet()).contains("field1:10"));
Assert.assertTrue(results.stream().map(d->d.get(new Path("field1")).asText()).collect(Collectors.toSet()).contains("field1:11"));
Assert.assertTrue(results.stream().map(d->d.get(new Path("field1")).asText()).collect(Collectors.toSet()).contains("field1:12"));
Assert.assertTrue(results.stream().map(d->d.get(new Path("field1")).asText()).collect(Collectors.toSet()).contains("field1:13"));
Assert.assertTrue(results.stream().map(d->d.get(new Path("field1")).asText()).collect(Collectors.toSet()).contains("field1:14"));
Assert.assertTrue(results.stream().map(d->d.get(new Path("field1")).asText()).collect(Collectors.toSet()).contains("field1:15"));
Assert.assertTrue(results.stream().map(d->d.get(new Path("field1")).asText()).collect(Collectors.toSet()).contains("field1:16"));
Assert.assertTrue(results.stream().map(d->d.get(new Path("field1")).asText()).collect(Collectors.toSet()).contains("field1:17"));
Assert.assertTrue(results.stream().map(d->d.get(new Path("field1")).asText()).collect(Collectors.toSet()).contains("field1:18"));
Assert.assertTrue(results.stream().map(d->d.get(new Path("field1")).asText()).collect(Collectors.toSet()).contains("field1:19"));
}
@Test
public void twoSimpleValuesLookupTest() throws Exception {
EntityMetadata md=getMd("testMetadata.json");
List<JsonDoc> docs=fill();
// indexeing using field1 and field2
SimpleKeySpec spec1=new SimpleKeySpec(qfi(md,"field1","field1"));
SimpleKeySpec spec2=new SimpleKeySpec(qfi(md,"field2","field2"));
CompositeKeySpec aspec=new CompositeKeySpec(new KeySpec[] {spec1,spec2});
MemDocIndex index=new MemDocIndex(aspec);
// Add all docs
for(JsonDoc doc:docs)
index.add(doc);
// Search
Set<JsonDoc> results=index.find(new CompositeLookupSpec(new LookupSpec[] {new ValueLookupSpec(spec1,"field1:10"),
new ValueLookupSpec(spec2,"field2:10")}));
Assert.assertEquals(1,results.size());
Assert.assertEquals("field1:10",results.iterator().next().get(new Path("field1")).asText());
results=index.find(new CompositeLookupSpec(new LookupSpec[] {new ValueLookupSpec(spec1,"field1:10"),
new ValueLookupSpec(spec2,"field2:11")}));
Assert.assertEquals(0,results.size());
}
@Test
public void twoMultiValueLookupTest() throws Exception {
EntityMetadata md=getMd("testMetadata.json");
List<JsonDoc> docs=fill();
// indexing using field1 and field2
SimpleKeySpec spec1=new SimpleKeySpec(qfi(md,"field1","field1"));
SimpleKeySpec spec2=new SimpleKeySpec(qfi(md,"field2","field2"));
CompositeKeySpec aspec=new CompositeKeySpec(new KeySpec[] {spec1,spec2});
MemDocIndex index=new MemDocIndex(aspec);
// Add all docs
for(JsonDoc doc:docs)
index.add(doc);
// Search
HashSet<Object> values1=new HashSet<>();
values1.add("field1:10");
values1.add("field1:11");
values1.add("field1:1101");
Set<JsonDoc> results=index.find(new CompositeLookupSpec(new LookupSpec[] {new MultiValueLookupSpec(spec1,values1),
new ValueLookupSpec(spec2,"field2:10")}));
Assert.assertEquals(1,results.size());
Assert.assertEquals("field1:10",results.iterator().next().get(new Path("field1")).asText());
}
@Test
public void twoRangeValueLookupTest() throws Exception {
EntityMetadata md=getMd("testMetadata.json");
List<JsonDoc> docs=fill();
// indexeing using field1 and field2
SimpleKeySpec spec1=new SimpleKeySpec(qfi(md,"field1","field1"));
SimpleKeySpec spec2=new SimpleKeySpec(qfi(md,"field2","field2"));
CompositeKeySpec aspec=new CompositeKeySpec(new KeySpec[] {spec1,spec2});
MemDocIndex index=new MemDocIndex(aspec);
// Add all docs
for(JsonDoc doc:docs)
index.add(doc);
Set<JsonDoc> results=index.find(new CompositeLookupSpec(new LookupSpec[] {new RangeLookupSpec(spec1,"field1:10","field1:15"),
new ValueLookupSpec(spec2,"field2:10")}));
Assert.assertEquals(1,results.size());
Assert.assertTrue(results.stream().map(d->d.get(new Path("field1")).asText()).collect(Collectors.toSet()).contains("field1:10"));
}
@Test
public void twoPrefixLookupTest() throws Exception {
EntityMetadata md=getMd("testMetadata.json");
List<JsonDoc> docs=fill();
// indexeing using field1 and field2
SimpleKeySpec spec1=new SimpleKeySpec(qfi(md,"field1","field1"));
SimpleKeySpec spec2=new SimpleKeySpec(qfi(md,"field2","field2"));
CompositeKeySpec aspec=new CompositeKeySpec(new KeySpec[] {spec1,spec2});
MemDocIndex index=new MemDocIndex(aspec);
// Add all docs
for(JsonDoc doc:docs)
index.add(doc);
Set<JsonDoc> results=index.find(new CompositeLookupSpec(new LookupSpec[] {new PrefixLookupSpec(spec1,"FIELD1:1",true),
new ValueLookupSpec(spec2,"field2:10")}));
Assert.assertEquals(1,results.size());
Assert.assertTrue(results.stream().map(d->d.get(new Path("field1")).asText()).collect(Collectors.toSet()).contains("field1:10"));
}
@Test
public void simpleArrayLookupTest() throws Exception {
EntityMetadata md=getMd("testMetadata.json");
List<JsonDoc> docs=fill();
// Find elemf1:
SimpleKeySpec spec=new SimpleKeySpec(qfi(md,"field7.*.elemf1","field7.*.elemf1"));
MemDocIndex index=new MemDocIndex(spec);
// Add all docs
for(JsonDoc doc:docs)
index.add(doc);
// Search
Set<JsonDoc> results=index.find(new ValueLookupSpec(spec,"doc:10 elemf1:20"));
Assert.assertEquals(1,results.size());
Assert.assertEquals("field1:10",results.iterator().next().get(new Path("field1")).asText());
}
@Test
public void doubleSimpleArrayLookupTest() throws Exception {
EntityMetadata md=getMd("testMetadata.json");
List<JsonDoc> docs=fill();
QueryFieldInfo array=qfi(md,"field7","field7");
SimpleKeySpec spec1=new SimpleKeySpec(qfi(md,"elemf1","field7.*.elemf1"));
SimpleKeySpec spec2=new SimpleKeySpec(qfi(md,"elemf2","field7.*.elemf2"));
ArrayKeySpec aspec=new ArrayKeySpec(array,new SimpleKeySpec[] {spec1,spec2});
MemDocIndex index=new MemDocIndex(aspec);
// Add all docs
for(JsonDoc doc:docs)
index.add(doc);
// Search
Set<JsonDoc> results=index.find(new ArrayLookupSpec(new LookupSpec[]{new ValueLookupSpec(spec1,"doc:2 elemf1:10"),
new ValueLookupSpec(spec2,"doc:2 elemf2:10")}));
Assert.assertEquals(1,results.size());
Assert.assertEquals("field1:2",results.iterator().next().get(new Path("field1")).asText());
}
}