/*
* Copyright 2015-2016 Cask Data, Inc.
*
* 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 co.cask.cdap.data2.metadata.dataset;
import co.cask.cdap.api.artifact.ArtifactId;
import co.cask.cdap.api.artifact.ArtifactScope;
import co.cask.cdap.api.artifact.ArtifactVersion;
import co.cask.cdap.api.dataset.DatasetProperties;
import co.cask.cdap.data2.datafabric.dataset.DatasetsUtil;
import co.cask.cdap.data2.dataset2.DatasetFrameworkTestUtil;
import co.cask.cdap.data2.metadata.indexer.Indexer;
import co.cask.cdap.proto.Id;
import co.cask.cdap.proto.ProgramType;
import co.cask.cdap.proto.metadata.MetadataSearchTargetType;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* Test class for {@link MetadataDataset} class.
*/
public class MetadataDatasetTest {
@ClassRule
public static DatasetFrameworkTestUtil dsFrameworkUtil = new DatasetFrameworkTestUtil();
private static final Id.DatasetInstance datasetInstance =
Id.DatasetInstance.from(DatasetFrameworkTestUtil.NAMESPACE_ID, "meta");
private MetadataDataset dataset;
private final Id.Application app1 = Id.Application.from("ns1", "app1");
private final Id.Application appNs2 = Id.Application.from("ns2", "app1");
// Have to use Id.Program for comparison here because the MetadataDataset APIs return Id.Program.
private final Id.Program flow1 = Id.Program.from("ns1", "app1", ProgramType.FLOW, "flow1");
private final Id.DatasetInstance dataset1 = Id.DatasetInstance.from("ns1", "ds1");
private final Id.Stream stream1 = Id.Stream.from("ns1", "s1");
private final Id.Stream.View view1 = Id.Stream.View.from(stream1, "v1");
private final Id.Artifact artifact1 = Id.Artifact.from(Id.Namespace.from("ns1"), "a1", "1.0.0");
@Before
public void before() throws Exception {
dataset = getDataset(datasetInstance);
}
@After
public void after() throws Exception {
dataset = null;
}
@Test
public void testProperties() throws Exception {
Assert.assertEquals(0, dataset.getProperties(app1).size());
Assert.assertEquals(0, dataset.getProperties(flow1).size());
Assert.assertEquals(0, dataset.getProperties(dataset1).size());
Assert.assertEquals(0, dataset.getProperties(stream1).size());
Assert.assertEquals(0, dataset.getProperties(view1).size());
Assert.assertEquals(0, dataset.getProperties(artifact1).size());
// Set some properties
dataset.setProperty(app1, "akey1", "avalue1");
dataset.setProperty(flow1, "fkey1", "fvalue1");
dataset.setProperty(flow1, "fK", "fV");
dataset.setProperty(dataset1, "dkey1", "dvalue1");
dataset.setProperty(stream1, "skey1", "svalue1");
dataset.setProperty(stream1, "skey2", "svalue2");
dataset.setProperty(view1, "vkey1", "vvalue1");
dataset.setProperty(view1, "vkey2", "vvalue2");
dataset.setProperty(artifact1, "rkey1", "rvalue1");
dataset.setProperty(artifact1, "rkey2", "rvalue2");
// verify
Map<String, String> properties = dataset.getProperties(app1);
Assert.assertEquals(ImmutableMap.of("akey1", "avalue1"), properties);
dataset.removeProperties(app1, "akey1");
Assert.assertNull(dataset.getProperty(app1, "akey1"));
MetadataEntry result = dataset.getProperty(flow1, "fkey1");
MetadataEntry expected = new MetadataEntry(flow1, "fkey1", "fvalue1");
Assert.assertEquals(expected, result);
Assert.assertEquals(ImmutableMap.of("fkey1", "fvalue1", "fK", "fV"), dataset.getProperties(flow1));
dataset.removeProperties(flow1, "fkey1");
properties = dataset.getProperties(flow1);
Assert.assertEquals(1, properties.size());
Assert.assertEquals("fV", properties.get("fK"));
dataset.removeProperties(flow1);
Assert.assertEquals(0, dataset.getProperties(flow1).size());
expected = new MetadataEntry(dataset1, "dkey1", "dvalue1");
Assert.assertEquals(expected, dataset.getProperty(dataset1, "dkey1"));
Assert.assertEquals(ImmutableMap.of("skey1", "svalue1", "skey2", "svalue2"), dataset.getProperties(stream1));
properties = dataset.getProperties(artifact1);
Assert.assertEquals(ImmutableMap.of("rkey1", "rvalue1", "rkey2", "rvalue2"), properties);
result = dataset.getProperty(artifact1, "rkey2");
expected = new MetadataEntry(artifact1, "rkey2", "rvalue2");
Assert.assertEquals(expected, result);
properties = dataset.getProperties(view1);
Assert.assertEquals(ImmutableMap.of("vkey1", "vvalue1", "vkey2", "vvalue2"), properties);
result = dataset.getProperty(view1, "vkey2");
expected = new MetadataEntry(view1, "vkey2", "vvalue2");
Assert.assertEquals(expected, result);
// reset a property
dataset.setProperty(stream1, "skey1", "sv1");
Assert.assertEquals(ImmutableMap.of("skey1", "sv1", "skey2", "svalue2"), dataset.getProperties(stream1));
// cleanup
dataset.removeProperties(app1);
dataset.removeProperties(flow1);
dataset.removeProperties(dataset1);
dataset.removeProperties(stream1);
dataset.removeProperties(artifact1);
dataset.removeProperties(view1);
Assert.assertEquals(0, dataset.getProperties(app1).size());
Assert.assertEquals(0, dataset.getProperties(flow1).size());
Assert.assertEquals(0, dataset.getProperties(dataset1).size());
Assert.assertEquals(0, dataset.getProperties(stream1).size());
Assert.assertEquals(0, dataset.getProperties(view1).size());
Assert.assertEquals(0, dataset.getProperties(artifact1).size());
}
@Test
public void testTags() {
Assert.assertEquals(0, dataset.getTags(app1).size());
Assert.assertEquals(0, dataset.getTags(flow1).size());
Assert.assertEquals(0, dataset.getTags(dataset1).size());
Assert.assertEquals(0, dataset.getTags(stream1).size());
Assert.assertEquals(0, dataset.getTags(view1).size());
Assert.assertEquals(0, dataset.getTags(artifact1).size());
dataset.addTags(app1, "tag1", "tag2", "tag3");
dataset.addTags(flow1, "tag1");
dataset.addTags(dataset1, "tag3", "tag2");
dataset.addTags(stream1, "tag2");
dataset.addTags(view1, "tag4");
dataset.addTags(artifact1, "tag3");
Set<String> tags = dataset.getTags(app1);
Assert.assertEquals(3, tags.size());
Assert.assertTrue(tags.contains("tag1"));
Assert.assertTrue(tags.contains("tag2"));
Assert.assertTrue(tags.contains("tag3"));
// add the same tag again
dataset.addTags(app1, "tag1");
Assert.assertEquals(3, dataset.getTags(app1).size());
tags = dataset.getTags(flow1);
Assert.assertEquals(1, tags.size());
Assert.assertTrue(tags.contains("tag1"));
tags = dataset.getTags(dataset1);
Assert.assertEquals(2, tags.size());
Assert.assertTrue(tags.contains("tag3"));
Assert.assertTrue(tags.contains("tag2"));
tags = dataset.getTags(stream1);
Assert.assertEquals(1, tags.size());
Assert.assertTrue(tags.contains("tag2"));
tags = dataset.getTags(view1);
Assert.assertEquals(1, tags.size());
Assert.assertTrue(tags.contains("tag4"));
dataset.removeTags(app1, "tag1", "tag2");
tags = dataset.getTags(app1);
Assert.assertEquals(1, tags.size());
Assert.assertTrue(tags.contains("tag3"));
dataset.removeTags(dataset1, "tag3");
tags = dataset.getTags(dataset1);
Assert.assertEquals(1, tags.size());
Assert.assertTrue(tags.contains("tag2"));
tags = dataset.getTags(artifact1);
Assert.assertEquals(1, tags.size());
Assert.assertTrue(tags.contains("tag3"));
// cleanup
dataset.removeTags(app1);
dataset.removeTags(flow1);
dataset.removeTags(dataset1);
dataset.removeTags(stream1);
dataset.removeTags(view1);
dataset.removeTags(artifact1);
Assert.assertEquals(0, dataset.getTags(app1).size());
Assert.assertEquals(0, dataset.getTags(flow1).size());
Assert.assertEquals(0, dataset.getTags(dataset1).size());
Assert.assertEquals(0, dataset.getTags(stream1).size());
Assert.assertEquals(0, dataset.getTags(view1).size());
Assert.assertEquals(0, dataset.getTags(artifact1).size());
}
@Test
public void testSearchOnTags() throws Exception {
Assert.assertEquals(0, dataset.getTags(app1).size());
Assert.assertEquals(0, dataset.getTags(appNs2).size());
Assert.assertEquals(0, dataset.getTags(flow1).size());
Assert.assertEquals(0, dataset.getTags(dataset1).size());
Assert.assertEquals(0, dataset.getTags(stream1).size());
dataset.addTags(app1, "tag1", "tag2", "tag3");
dataset.addTags(appNs2, "tag1", "tag2", "tag3_more");
dataset.addTags(flow1, "tag1");
dataset.addTags(dataset1, "tag3", "tag2", "tag12-tag33");
dataset.addTags(stream1, "tag2, tag4");
// Try to search on all tags
List<MetadataEntry> results =
dataset.search("ns1", "tags:*", ImmutableSet.of(MetadataSearchTargetType.ALL));
// results for dataset1 - ns1:tags:tag12, ns1:tags:tag2, ns1:tags:tag3, ns1:tags:tag33, ns1:tags:tag12-tag33
Assert.assertEquals(11, results.size());
// Try to search for tag1*
results = dataset.search("ns1", "tags:tag1*", ImmutableSet.of(MetadataSearchTargetType.ALL));
Assert.assertEquals(4, results.size());
// Try to search for tag1 with spaces in search query and mixed case of tags keyword
results = dataset.search("ns1", " tAGS : tag1 ", ImmutableSet.of(MetadataSearchTargetType.ALL));
Assert.assertEquals(2, results.size());
// Try to search for tag4
results = dataset.search("ns1", "tags:tag4", ImmutableSet.of(MetadataSearchTargetType.ALL));
Assert.assertEquals(1, results.size());
// Try to search for tag33
results = dataset.search("ns1", "tags:tag33", ImmutableSet.of(MetadataSearchTargetType.ALL));
Assert.assertEquals(1, results.size());
// Try to search for a tag which has - in it
results = dataset.search("ns1", "tag12-tag33", ImmutableSet.of(MetadataSearchTargetType.ALL));
Assert.assertEquals(1, results.size());
// Try to search for tag33 with spaces in query
results = dataset.search("ns1", " tag33 ", ImmutableSet.of(MetadataSearchTargetType.ALL));
Assert.assertEquals(1, results.size());
// Try to search for tag3
results = dataset.search("ns1", "tags:tag3*", ImmutableSet.of(MetadataSearchTargetType.ALL));
Assert.assertEquals(3, results.size());
// try search in another namespace
results = dataset.search("ns2", "tags:tag1", ImmutableSet.of(MetadataSearchTargetType.ALL));
Assert.assertEquals(1, results.size());
results = dataset.search("ns2", "tag3", ImmutableSet.of(MetadataSearchTargetType.ALL));
Assert.assertEquals(1, results.size());
results = dataset.search("ns2", "tag*", ImmutableSet.of(MetadataSearchTargetType.APP));
// 9 due to matches of type ns2:tag1, ns2:tags:tag1, and splitting of tag3_more
Assert.assertEquals(9, results.size());
// cleanup
dataset.removeTags(app1);
dataset.removeTags(flow1);
dataset.removeTags(dataset1);
dataset.removeTags(stream1);
// Search should be empty after deleting tags
results = dataset.search("ns1", "tags:tag3*", ImmutableSet.of(MetadataSearchTargetType.ALL));
Assert.assertEquals(0, results.size());
Assert.assertEquals(0, dataset.getTags(app1).size());
Assert.assertEquals(0, dataset.getTags(flow1).size());
Assert.assertEquals(0, dataset.getTags(dataset1).size());
Assert.assertEquals(0, dataset.getTags(stream1).size());
}
@Test
public void testSearchOnValue() throws Exception {
// Add some metadata
MetadataEntry entry = new MetadataEntry(flow1, "key1", "value1");
String multiWordValue = "aV1 av2 , - , av3 - av4_av5 av6";
MetadataEntry multiWordEntry = new MetadataEntry(flow1, "multiword", multiWordValue);
dataset.setProperty(flow1, "key1", "value1");
dataset.setProperty(flow1, "key2", "value2");
dataset.setProperty(flow1, "multiword", multiWordValue);
// Search for it based on value
List<MetadataEntry> results =
dataset.search("ns1", "value1", ImmutableSet.of(MetadataSearchTargetType.PROGRAM));
Assert.assertEquals(ImmutableList.of(entry), results);
// Search for it based on a word in value with spaces in search query
results = dataset.search("ns1", " aV1 ", ImmutableSet.of(MetadataSearchTargetType.PROGRAM));
Assert.assertEquals(ImmutableList.of(multiWordEntry), results);
// Search for it based split patterns to make sure nothing is matched
results = dataset.search("ns1", "-", ImmutableSet.of(MetadataSearchTargetType.ALL));
Assert.assertTrue(results.isEmpty());
results = dataset.search("ns1", ",", ImmutableSet.of(MetadataSearchTargetType.ALL));
Assert.assertTrue(results.isEmpty());
results = dataset.search("ns1", "_", ImmutableSet.of(MetadataSearchTargetType.ALL));
Assert.assertTrue(results.isEmpty());
results = dataset.search("ns1", ", ,", ImmutableSet.of(MetadataSearchTargetType.ALL));
Assert.assertTrue(results.isEmpty());
results = dataset.search("ns1", ", - ,", ImmutableSet.of(MetadataSearchTargetType.ALL));
Assert.assertTrue(results.isEmpty());
// Search for it based on a word in value
results = dataset.search("ns1", "av5", ImmutableSet.of(MetadataSearchTargetType.PROGRAM));
Assert.assertEquals(ImmutableList.of(multiWordEntry), results);
// Case insensitive
results = dataset.search("ns1", "ValUe1", ImmutableSet.of(MetadataSearchTargetType.PROGRAM));
Assert.assertEquals(ImmutableList.of(entry), results);
// Search based on value
dataset.setProperty(flow1, "key3", "value1");
results = dataset.search("ns1", "value1", ImmutableSet.of(MetadataSearchTargetType.PROGRAM));
Assert.assertEquals(2, results.size());
for (MetadataEntry result : results) {
Assert.assertEquals("value1", result.getValue());
}
// Search based on value prefix
dataset.setProperty(stream1, "key21", "value21");
results = dataset.search("ns1", "value2*", ImmutableSet.of(MetadataSearchTargetType.ALL));
Assert.assertEquals(2, results.size());
for (MetadataEntry result : results) {
Assert.assertTrue(result.getValue().startsWith("value2"));
}
// Search based on value prefix in the wrong namespace
results = dataset.search("ns12", "value2*", ImmutableSet.of(MetadataSearchTargetType.ALL));
Assert.assertTrue(results.isEmpty());
}
@Test
public void testSearchOnKeyValue() throws Exception {
// Add some properties to flow1
MetadataEntry flowEntry1 = new MetadataEntry(flow1, "key1", "value1");
MetadataEntry flowEntry2 = new MetadataEntry(flow1, "key2", "value2");
dataset.setProperty(flow1, "key1", "value1");
dataset.setProperty(flow1, "key2", "value2");
// add a multi word value
String multiWordKey = "multiword";
String multiWordValue = "aV1 av2 , - , av3 - av4_av5 av6";
dataset.setProperty(flow1, multiWordKey, multiWordValue);
MetadataEntry streamEntry1 = new MetadataEntry(stream1, "Key1", "Value1");
MetadataEntry streamEntry2 = new MetadataEntry(stream1, "sKey1", "sValue1");
dataset.setProperty(stream1, "sKey1", "sValue1");
dataset.setProperty(stream1, "Key1", "Value1");
// Search for it based on value
List<MetadataEntry> results =
dataset.search("ns1", "key1" + MetadataDataset.KEYVALUE_SEPARATOR + "value1",
ImmutableSet.of(MetadataSearchTargetType.PROGRAM));
Assert.assertEquals(ImmutableList.of(flowEntry1), results);
// Search for it based on a word in value with spaces in search query
results = dataset.search("ns1", " multiword" + MetadataDataset.KEYVALUE_SEPARATOR + "aV1 ",
ImmutableSet.of(MetadataSearchTargetType.PROGRAM));
MetadataEntry flowMultiWordEntry = new MetadataEntry(flow1, multiWordKey, multiWordValue);
Assert.assertEquals(ImmutableList.of(flowMultiWordEntry), results);
// Search for it based on a word in value
results =
dataset.search("ns1", multiWordKey + MetadataDataset.KEYVALUE_SEPARATOR + "aV5",
ImmutableSet.of(MetadataSearchTargetType.PROGRAM));
Assert.assertEquals(ImmutableList.of(flowMultiWordEntry), results);
dataset.removeProperties(flow1, multiWordKey);
results = dataset.search("ns1", multiWordKey + MetadataDataset.KEYVALUE_SEPARATOR + "aV5",
ImmutableSet.of(MetadataSearchTargetType.PROGRAM));
// search results should be empty after removing this key as the indexes are deleted
Assert.assertTrue(results.isEmpty());
// Test wrong ns
List<MetadataEntry> results2 =
dataset.search("ns12", "key1" + MetadataDataset.KEYVALUE_SEPARATOR + "value1",
ImmutableSet.of(MetadataSearchTargetType.PROGRAM));
Assert.assertTrue(results2.isEmpty());
// Test multi word query
results = dataset.search("ns1", " value1 av2 ", ImmutableSet.of(MetadataSearchTargetType.ALL));
Assert.assertEquals(Sets.newHashSet(flowEntry1, streamEntry1), Sets.newHashSet(results));
results = dataset.search("ns1", " value1 sValue1 ", ImmutableSet.of(MetadataSearchTargetType.ALL));
Assert.assertEquals(Sets.newHashSet(flowEntry1, streamEntry1, streamEntry2), Sets.newHashSet(results));
results = dataset.search("ns1", " valu* sVal* ", ImmutableSet.of(MetadataSearchTargetType.ALL));
Assert.assertEquals(Sets.newHashSet(flowEntry1, flowEntry2, streamEntry1, streamEntry2), Sets.newHashSet(results));
// Using empty filter should also search for all target types
results = dataset.search("ns1", " valu* sVal* ", ImmutableSet.<MetadataSearchTargetType>of());
Assert.assertEquals(Sets.newHashSet(flowEntry1, flowEntry2, streamEntry1, streamEntry2), Sets.newHashSet(results));
}
@Test
public void testSearchIncludesSystemEntities() {
// Use the same artifact in two different namespaces - system and ns2
Id.Artifact sysArtifact = Id.Artifact.from(
Id.Namespace.SYSTEM, new ArtifactId("artifact", new ArtifactVersion("1.0"), ArtifactScope.SYSTEM));
Id.Artifact ns2Artifact = Id.Artifact.from(Id.Namespace.from("ns2"), "artifact", "1.0");
String multiWordKey = "multiword";
String multiWordValue = "aV1 av2 , - , av3 - av4_av5 av6";
dataset.setProperty(flow1, multiWordKey, multiWordValue);
dataset.setProperty(sysArtifact, multiWordKey, multiWordValue);
dataset.setProperty(ns2Artifact, multiWordKey, multiWordValue);
// perform the exact same multiword search in the 'ns1' namespace. It should return the system artifact along with
// matched entities in the 'ns1' namespace
MetadataEntry flowMultiWordEntry = new MetadataEntry(flow1, multiWordKey, multiWordValue);
MetadataEntry systemArtifactEntry = new MetadataEntry(sysArtifact, multiWordKey, multiWordValue);
MetadataEntry ns2ArtifactEntry = new MetadataEntry(ns2Artifact, multiWordKey, multiWordValue);
List<MetadataEntry> results = dataset.search("ns1", "aV5", ImmutableSet.of(MetadataSearchTargetType.ALL));
Assert.assertEquals(Sets.newHashSet(flowMultiWordEntry, systemArtifactEntry), Sets.newHashSet(results));
// search only programs - should only return flow
results = dataset.search("ns1", multiWordKey + MetadataDataset.KEYVALUE_SEPARATOR + "aV5",
ImmutableSet.of(MetadataSearchTargetType.PROGRAM));
Assert.assertEquals(ImmutableList.of(flowMultiWordEntry), results);
// search only artifacts - should only return system artifact
results = dataset.search("ns1", multiWordKey + MetadataDataset.KEYVALUE_SEPARATOR + multiWordValue,
ImmutableSet.of(MetadataSearchTargetType.ARTIFACT));
// this query returns the system artifact 4 times, since the dataset returns a list with duplicates for scoring
// purposes. Convert to a Set for comparison.
Assert.assertEquals(Sets.newHashSet(systemArtifactEntry), Sets.newHashSet(results));
// search all entities in namespace 'ns2' - should return the system artifact and the same artifact in ns2
results = dataset.search("ns2", multiWordKey + MetadataDataset.KEYVALUE_SEPARATOR + "aV4",
ImmutableSet.of(MetadataSearchTargetType.ALL));
Assert.assertEquals(Sets.newHashSet(systemArtifactEntry, ns2ArtifactEntry), Sets.newHashSet(results));
// search only programs in a namespace 'ns2'. Should return empty
results = dataset.search("ns2", "aV*", ImmutableSet.of(MetadataSearchTargetType.PROGRAM));
Assert.assertTrue(results.isEmpty());
// search all entities in namespace 'ns3'. Should return only the system artifact
results = dataset.search("ns3", "av*", ImmutableSet.of(MetadataSearchTargetType.ALL));
Assert.assertEquals(Sets.newHashSet(systemArtifactEntry), Sets.newHashSet(results));
// search the system namespace for all entities. Should return only the system artifact
results = dataset.search(Id.Namespace.SYSTEM.getId(), "av*", ImmutableSet.of(MetadataSearchTargetType.ALL));
Assert.assertEquals(Sets.newHashSet(systemArtifactEntry), Sets.newHashSet(results));
// clean up
dataset.removeProperties(flow1);
dataset.removeProperties(sysArtifact);
}
@Test
public void testUpdateSearch() throws Exception {
dataset.setProperty(flow1, "key1", "value1");
dataset.setProperty(flow1, "key2", "value2");
dataset.addTags(flow1, "tag1", "tag2");
Assert.assertEquals(ImmutableList.of(new MetadataEntry(flow1, "key1", "value1")),
dataset.search(flow1.getNamespaceId(), "value1", ImmutableSet.<MetadataSearchTargetType>of()));
Assert.assertEquals(ImmutableList.of(new MetadataEntry(flow1, "key2", "value2")),
dataset.search(flow1.getNamespaceId(), "value2", ImmutableSet.<MetadataSearchTargetType>of()));
Assert.assertEquals(ImmutableList.of(new MetadataEntry(flow1, MetadataDataset.TAGS_KEY, "tag1,tag2")),
dataset.search(flow1.getNamespaceId(), "tag2", ImmutableSet.<MetadataSearchTargetType>of()));
// Update key1
dataset.setProperty(flow1, "key1", "value3");
dataset.removeProperties(flow1, "key2");
dataset.removeTags(flow1, "tag2");
// Searching for value1 should be empty
Assert.assertEquals(ImmutableList.of(),
dataset.search(flow1.getNamespaceId(), "value1", ImmutableSet.<MetadataSearchTargetType>of()));
// Instead key1 has value value3 now
Assert.assertEquals(ImmutableList.of(new MetadataEntry(flow1, "key1", "value3")),
dataset.search(flow1.getNamespaceId(), "value3", ImmutableSet.<MetadataSearchTargetType>of()));
// key2 was deleted
Assert.assertEquals(ImmutableList.of(),
dataset.search(flow1.getNamespaceId(), "value2", ImmutableSet.<MetadataSearchTargetType>of()));
// tag2 was deleted
Assert.assertEquals(ImmutableList.of(),
dataset.search(flow1.getNamespaceId(), "tag2", ImmutableSet.<MetadataSearchTargetType>of()));
Assert.assertEquals(ImmutableList.of(new MetadataEntry(flow1, MetadataDataset.TAGS_KEY, "tag1")),
dataset.search(flow1.getNamespaceId(), "tag1", ImmutableSet.<MetadataSearchTargetType>of()));
}
@Test
public void testMultiGet() throws Exception {
Map<Id.NamespacedId, Metadata> allMetadata = new HashMap<>();
allMetadata.put(flow1, new Metadata(flow1,
ImmutableMap.of("key1", "value1", "key2", "value2"),
ImmutableSet.of("tag1", "tag2", "tag3")));
allMetadata.put(dataset1, new Metadata(dataset1,
ImmutableMap.of("key10", "value10", "key11", "value11"),
ImmutableSet.<String>of()));
allMetadata.put(app1, new Metadata(app1,
ImmutableMap.of("key20", "value20", "key21", "value21"),
ImmutableSet.<String>of()));
allMetadata.put(stream1, new Metadata(stream1,
ImmutableMap.of("key30", "value30", "key31", "value31", "key32", "value32"),
ImmutableSet.<String>of()));
allMetadata.put(artifact1, new Metadata(artifact1,
ImmutableMap.of("key40", "value41"),
ImmutableSet.<String>of()));
allMetadata.put(view1, new Metadata(view1,
ImmutableMap.of("key50", "value50", "key51", "value51"),
ImmutableSet.of("tag51")));
for (Map.Entry<Id.NamespacedId, Metadata> entry : allMetadata.entrySet()) {
Metadata metadata = entry.getValue();
for (Map.Entry<String, String> props : metadata.getProperties().entrySet()) {
dataset.setProperty(metadata.getEntityId(), props.getKey(), props.getValue());
}
dataset.addTags(metadata.getEntityId(), metadata.getTags().toArray(new String[metadata.getTags().size()]));
}
ImmutableSet<Metadata> expected =
ImmutableSet.<Metadata>builder()
.add(allMetadata.get(flow1))
.add(allMetadata.get(app1))
.build();
Assert.assertEquals(expected, dataset.getMetadata(ImmutableSet.of(flow1, app1)));
expected =
ImmutableSet.<Metadata>builder()
.add(allMetadata.get(view1))
.add(allMetadata.get(stream1))
.add(allMetadata.get(dataset1))
.add(allMetadata.get(artifact1))
.build();
Assert.assertEquals(expected, dataset.getMetadata(ImmutableSet.of(view1, stream1, dataset1, artifact1)));
expected =
ImmutableSet.<Metadata>builder()
.add(allMetadata.get(artifact1))
.build();
Assert.assertEquals(expected, dataset.getMetadata(ImmutableSet.of(artifact1)));
expected = ImmutableSet.of();
Assert.assertEquals(expected, dataset.getMetadata(ImmutableSet.<Id.NamespacedId>of()));
}
@Test
public void testDelete() throws Exception {
dataset.setProperty(flow1, "key1", "value1");
dataset.setProperty(flow1, "key2", "value2");
dataset.addTags(flow1, "tag1", "tag2");
dataset.setProperty(app1, "key10", "value10");
dataset.setProperty(app1, "key12", "value12");
dataset.addTags(app1, "tag11", "tag12");
Assert.assertEquals(ImmutableMap.of("key1", "value1", "key2", "value2"), dataset.getProperties(flow1));
Assert.assertEquals(ImmutableSet.of("tag1", "tag2"), dataset.getTags(flow1));
Assert.assertEquals(ImmutableMap.of("key10", "value10", "key12", "value12"), dataset.getProperties(app1));
Assert.assertEquals(ImmutableSet.of("tag11", "tag12"), dataset.getTags(app1));
// Delete all tags for flow1, and delete all properties for app1
dataset.removeTags(flow1);
dataset.removeProperties(app1);
Assert.assertEquals(ImmutableMap.of("key1", "value1", "key2", "value2"), dataset.getProperties(flow1));
Assert.assertEquals(ImmutableSet.of(), dataset.getTags(flow1));
Assert.assertEquals(ImmutableMap.of(), dataset.getProperties(app1));
Assert.assertEquals(ImmutableSet.of("tag11", "tag12"), dataset.getTags(app1));
}
@Test
public void testHistory() throws Exception {
MetadataDataset dataset =
getDataset(Id.DatasetInstance.from(DatasetFrameworkTestUtil.NAMESPACE_ID, "testHistory"));
doTestHistory(dataset, flow1, "f_");
doTestHistory(dataset, app1, "a_");
doTestHistory(dataset, dataset1, "d_");
doTestHistory(dataset, stream1, "s_");
}
@Test
public void testIndexRebuilding() throws Exception {
MetadataDataset dataset =
getDataset(Id.DatasetInstance.from(DatasetFrameworkTestUtil.NAMESPACE_ID, "testIndexRebuilding"));
dataset.setProperty(flow1, "flowKey", "flowValue", new ReversingIndexer());
dataset.setProperty(dataset1, "datasetKey", "datasetValue", new ReversingIndexer());
String namespaceId = flow1.getNamespaceId();
Set<MetadataSearchTargetType> targetTypes = Collections.singleton(MetadataSearchTargetType.ALL);
List<MetadataEntry> searchResults = dataset.search(namespaceId, "flowValue", targetTypes);
Assert.assertTrue(searchResults.isEmpty());
searchResults = dataset.search(namespaceId, "flowKey:flow*", targetTypes);
Assert.assertTrue(searchResults.isEmpty());
searchResults = dataset.search(namespaceId, "datasetValue", targetTypes);
Assert.assertTrue(searchResults.isEmpty());
searchResults = dataset.search(namespaceId, "datasetKey:dataset*", targetTypes);
Assert.assertTrue(searchResults.isEmpty());
// Re-build indexes. Now the default indexer should be used
byte[] startRowKeyForNextBatch = dataset.rebuildIndexes(null, 1);
Assert.assertNotNull(startRowKeyForNextBatch);
List<MetadataEntry> flowSearchResults = dataset.search(namespaceId, "flowValue", targetTypes);
List<MetadataEntry> dsSearchResults = dataset.search(namespaceId, "datasetValue", targetTypes);
if (!flowSearchResults.isEmpty()) {
Assert.assertEquals(1, flowSearchResults.size());
flowSearchResults = dataset.search(namespaceId, "flowKey:flow*", targetTypes);
Assert.assertEquals(1, flowSearchResults.size());
Assert.assertTrue(dsSearchResults.isEmpty());
dsSearchResults = dataset.search(namespaceId, "datasetKey:dataset*", targetTypes);
Assert.assertTrue(dsSearchResults.isEmpty());
} else {
flowSearchResults = dataset.search(namespaceId, "flowKey:flow*", targetTypes);
Assert.assertTrue(flowSearchResults.isEmpty());
Assert.assertEquals(1, dsSearchResults.size());
dsSearchResults = dataset.search(namespaceId, "datasetKey:dataset*", targetTypes);
Assert.assertEquals(1, dsSearchResults.size());
}
startRowKeyForNextBatch = dataset.rebuildIndexes(startRowKeyForNextBatch, 1);
Assert.assertNull(startRowKeyForNextBatch);
searchResults = dataset.search(namespaceId, "flowValue", targetTypes);
Assert.assertEquals(1, searchResults.size());
searchResults = dataset.search(namespaceId, "flowKey:flow*", targetTypes);
Assert.assertEquals(1, searchResults.size());
searchResults = dataset.search(namespaceId, "datasetValue", targetTypes);
Assert.assertEquals(1, searchResults.size());
searchResults = dataset.search(namespaceId, "datasetKey:dataset*", targetTypes);
Assert.assertEquals(1, searchResults.size());
}
@Test
public void testIndexDeletion() throws Exception {
MetadataDataset dataset =
getDataset(Id.DatasetInstance.from(DatasetFrameworkTestUtil.NAMESPACE_ID, "testIndexRebuilding"));
dataset.setProperty(flow1, "flowKey", "flowValue");
dataset.setProperty(dataset1, "datasetKey", "datasetValue");
String namespaceId = flow1.getNamespaceId();
Set<MetadataSearchTargetType> targetTypes = Collections.singleton(MetadataSearchTargetType.ALL);
MetadataEntry expectedFlowEntry = new MetadataEntry(flow1, "flowKey", "flowValue");
MetadataEntry expectedDatasetEntry = new MetadataEntry(dataset1, "datasetKey", "datasetValue");
List<MetadataEntry> searchResults = dataset.search(namespaceId, "flowValue", targetTypes);
Assert.assertEquals(ImmutableList.of(expectedFlowEntry), searchResults);
searchResults = dataset.search(namespaceId, "flowKey:flow*", targetTypes);
Assert.assertEquals(ImmutableList.of(expectedFlowEntry), searchResults);
searchResults = dataset.search(namespaceId, "datasetValue", targetTypes);
Assert.assertEquals(ImmutableList.of(expectedDatasetEntry), searchResults);
searchResults = dataset.search(namespaceId, "datasetKey:dataset*", targetTypes);
Assert.assertEquals(ImmutableList.of(expectedDatasetEntry), searchResults);
// delete indexes
// 4 indexes should have been deleted - flowValue, flowKey:flowValue, datasetValue, datasetKey:datasetValue
for (int i = 0; i < 4; i++) {
Assert.assertEquals(1, dataset.deleteAllIndexes(1));
}
Assert.assertEquals(0, dataset.deleteAllIndexes(1));
}
private void doTestHistory(MetadataDataset dataset, Id.NamespacedId targetId, String prefix)
throws Exception {
// Metadata change history keyed by time in millis the change was made
Map<Long, Metadata> expected = new HashMap<>();
// No history for targetId at the beginning
Metadata completeRecord = new Metadata(targetId);
expected.put(System.currentTimeMillis(), completeRecord);
// Get history for targetId, should be empty
Assert.assertEquals(ImmutableSet.of(completeRecord),
dataset.getSnapshotBeforeTime(ImmutableSet.of(targetId), System.currentTimeMillis()));
// Also, the metadata itself should be equal to the last recorded snapshot
Assert.assertEquals(getFirst(dataset.getSnapshotBeforeTime(ImmutableSet.of(targetId), System.currentTimeMillis())),
new Metadata(targetId, dataset.getProperties(targetId), dataset.getTags(targetId)));
// Since the key to expected map is time in millis, sleep for a millisecond to make sure the key is distinct
TimeUnit.MILLISECONDS.sleep(1);
// Add first record
completeRecord = new Metadata(targetId, toProps(prefix, "k1", "v1"), toTags(prefix, "t1", "t2"));
addMetadataHistory(dataset, completeRecord);
long time = System.currentTimeMillis();
expected.put(time, completeRecord);
// Since this is the first record, history should be the same as what was added.
Assert.assertEquals(ImmutableSet.of(completeRecord),
dataset.getSnapshotBeforeTime(ImmutableSet.of(targetId), time));
// Also, the metadata itself should be equal to the last recorded snapshot
Assert.assertEquals(getFirst(dataset.getSnapshotBeforeTime(ImmutableSet.of(targetId), System.currentTimeMillis())),
new Metadata(targetId, dataset.getProperties(targetId), dataset.getTags(targetId)));
TimeUnit.MILLISECONDS.sleep(1);
// Add a new property and a tag
dataset.setProperty(targetId, prefix + "k2", "v2");
dataset.addTags(targetId, prefix + "t3");
// Save the complete metadata record at this point
completeRecord = new Metadata(targetId, toProps(prefix, "k1", "v1", "k2", "v2"),
toTags(prefix, "t1", "t2", "t3"));
time = System.currentTimeMillis();
expected.put(time, completeRecord);
// Assert the history record with the change
Assert.assertEquals(ImmutableSet.of(completeRecord),
dataset.getSnapshotBeforeTime(ImmutableSet.of(targetId), time));
// Also, the metadata itself should be equal to the last recorded snapshot
Assert.assertEquals(getFirst(dataset.getSnapshotBeforeTime(ImmutableSet.of(targetId), System.currentTimeMillis())),
new Metadata(targetId, dataset.getProperties(targetId), dataset.getTags(targetId)));
TimeUnit.MILLISECONDS.sleep(1);
// Add another property and a tag
dataset.setProperty(targetId, prefix + "k3", "v3");
dataset.addTags(targetId, prefix + "t4");
// Save the complete metadata record at this point
completeRecord = new Metadata(targetId, toProps(prefix, "k1", "v1", "k2", "v2", "k3", "v3"),
toTags(prefix, "t1", "t2", "t3", "t4"));
time = System.currentTimeMillis();
expected.put(time, completeRecord);
// Assert the history record with the change
Assert.assertEquals(ImmutableSet.of(completeRecord),
dataset.getSnapshotBeforeTime(ImmutableSet.of(targetId), time));
// Also, the metadata itself should be equal to the last recorded snapshot
Assert.assertEquals(getFirst(dataset.getSnapshotBeforeTime(ImmutableSet.of(targetId), System.currentTimeMillis())),
new Metadata(targetId, dataset.getProperties(targetId), dataset.getTags(targetId)));
TimeUnit.MILLISECONDS.sleep(1);
// Add the same property and tag as second time
dataset.setProperty(targetId, prefix + "k2", "v2");
dataset.addTags(targetId, prefix + "t3");
// Save the complete metadata record at this point
completeRecord = new Metadata(targetId, toProps(prefix, "k1", "v1", "k2", "v2", "k3", "v3"),
toTags(prefix, "t1", "t2", "t3", "t4"));
time = System.currentTimeMillis();
expected.put(time, completeRecord);
// Assert the history record with the change
Assert.assertEquals(ImmutableSet.of(completeRecord),
dataset.getSnapshotBeforeTime(ImmutableSet.of(targetId), time));
// Also, the metadata itself should be equal to the last recorded snapshot
Assert.assertEquals(getFirst(dataset.getSnapshotBeforeTime(ImmutableSet.of(targetId), System.currentTimeMillis())),
new Metadata(targetId, dataset.getProperties(targetId), dataset.getTags(targetId)));
TimeUnit.MILLISECONDS.sleep(1);
// Remove a property and two tags
dataset.removeProperties(targetId, prefix + "k2");
dataset.removeTags(targetId, prefix + "t4");
dataset.removeTags(targetId, prefix + "t2");
// Save the complete metadata record at this point
completeRecord = new Metadata(targetId, toProps(prefix, "k1", "v1", "k3", "v3"),
toTags(prefix, "t1", "t3"));
time = System.currentTimeMillis();
expected.put(time, completeRecord);
// Assert the history record with the change
Assert.assertEquals(ImmutableSet.of(completeRecord),
dataset.getSnapshotBeforeTime(ImmutableSet.of(targetId), time));
// Also, the metadata itself should be equal to the last recorded snapshot
Assert.assertEquals(getFirst(dataset.getSnapshotBeforeTime(ImmutableSet.of(targetId), System.currentTimeMillis())),
new Metadata(targetId, dataset.getProperties(targetId), dataset.getTags(targetId)));
TimeUnit.MILLISECONDS.sleep(1);
// Remove all properties and all tags
dataset.removeProperties(targetId);
dataset.removeTags(targetId);
// Save the complete metadata record at this point
completeRecord = new Metadata(targetId);
time = System.currentTimeMillis();
expected.put(time, completeRecord);
// Assert the history record with the change
Assert.assertEquals(ImmutableSet.of(completeRecord),
dataset.getSnapshotBeforeTime(ImmutableSet.of(targetId), time));
// Also, the metadata itself should be equal to the last recorded snapshot
Assert.assertEquals(getFirst(dataset.getSnapshotBeforeTime(ImmutableSet.of(targetId), System.currentTimeMillis())),
new Metadata(targetId, dataset.getProperties(targetId), dataset.getTags(targetId)));
TimeUnit.MILLISECONDS.sleep(1);
// Add one more property and a tag
dataset.setProperty(targetId, prefix + "k2", "v2");
dataset.addTags(targetId, prefix + "t2");
// Save the complete metadata record at this point
completeRecord = new Metadata(targetId, toProps(prefix, "k2", "v2"),
toTags(prefix, "t2"));
time = System.currentTimeMillis();
expected.put(time, completeRecord);
// Assert the history record with the change
Assert.assertEquals(ImmutableSet.of(completeRecord),
dataset.getSnapshotBeforeTime(ImmutableSet.of(targetId), time));
// Also, the metadata itself should be equal to the last recorded snapshot
Assert.assertEquals(getFirst(dataset.getSnapshotBeforeTime(ImmutableSet.of(targetId), System.currentTimeMillis())),
new Metadata(targetId, dataset.getProperties(targetId), dataset.getTags(targetId)));
TimeUnit.MILLISECONDS.sleep(1);
// Now assert all history
for (Map.Entry<Long, Metadata> entry : expected.entrySet()) {
Assert.assertEquals(entry.getValue(),
getFirst(dataset.getSnapshotBeforeTime(ImmutableSet.of(targetId), entry.getKey())));
}
// Asserting for current time should give the latest record
Assert.assertEquals(ImmutableSet.of(completeRecord),
dataset.getSnapshotBeforeTime(ImmutableSet.of(targetId), System.currentTimeMillis()));
// Also, the metadata itself should be equal to the last recorded snapshot
Assert.assertEquals(getFirst(dataset.getSnapshotBeforeTime(ImmutableSet.of(targetId), System.currentTimeMillis())),
new Metadata(targetId, dataset.getProperties(targetId), dataset.getTags(targetId)));
}
private void addMetadataHistory(MetadataDataset dataset, Metadata record) {
for (Map.Entry<String, String> entry : record.getProperties().entrySet()) {
dataset.setProperty(record.getEntityId(), entry.getKey(), entry.getValue());
}
//noinspection ToArrayCallWithZeroLengthArrayArgument
dataset.addTags(record.getEntityId(), record.getTags().toArray(new String[0]));
}
private Map<String, String> toProps(String prefix, String k1, String v1) {
return ImmutableMap.of(prefix + k1, v1);
}
private Map<String, String> toProps(String prefix, String k1, String v1, String k2, String v2) {
return ImmutableMap.of(prefix + k1, v1, prefix + k2, v2);
}
private Map<String, String> toProps(String prefix, String k1, String v1, String k2, String v2, String k3, String v3) {
return ImmutableMap.of(prefix + k1, v1, prefix + k2, v2, prefix + k3, v3);
}
private Set<String> toTags(String prefix, String... tags) {
ImmutableSet.Builder<String> builder = new ImmutableSet.Builder<>();
for (String tag : tags) {
builder.add(prefix + tag);
}
return builder.build();
}
private <T> T getFirst(Iterable<T> iterable) {
Assert.assertEquals(1, Iterables.size(iterable));
return iterable.iterator().next();
}
private static MetadataDataset getDataset(Id.DatasetInstance instance) throws Exception {
return DatasetsUtil.getOrCreateDataset(dsFrameworkUtil.getFramework(), instance,
MetadataDataset.class.getName(),
DatasetProperties.EMPTY, null, null);
}
private static final class ReversingIndexer implements Indexer {
@Override
public Set<String> getIndexes(MetadataEntry entry) {
return ImmutableSet.of(reverse(entry.getKey()), reverse(entry.getValue()));
}
private String reverse(String toReverse) {
return new StringBuilder(toReverse).reverse().toString();
}
}
}