/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.rya.indexing.entity.storage.mongo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.HashSet; import java.util.Optional; import java.util.Set; import org.apache.rya.api.domain.RyaType; import org.apache.rya.api.domain.RyaIRI; import org.apache.rya.indexing.entity.model.Entity; import org.apache.rya.indexing.entity.model.Property; import org.apache.rya.indexing.entity.model.Type; import org.apache.rya.indexing.entity.model.TypedEntity; import org.apache.rya.indexing.entity.storage.EntityStorage; import org.apache.rya.indexing.entity.storage.EntityStorage.EntityAlreadyExistsException; import org.apache.rya.indexing.entity.storage.EntityStorage.EntityStorageException; import org.apache.rya.indexing.entity.storage.EntityStorage.StaleUpdateException; import org.apache.rya.test.mongo.MongoITBase; import org.eclipse.rdf4j.model.vocabulary.XMLSchema; import org.junit.Test; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; /** * Integration tests the methods of {@link MongoEntityStorage}. */ public class MongoEntityStorageIT extends MongoITBase { private static final String RYA_INSTANCE_NAME = "testInstance"; @Test public void create_and_get() throws Exception { // An Entity that will be stored. final Entity entity = Entity.builder() .setSubject(new RyaIRI("urn:GTIN-14/00012345600012")) .setExplicitType(new RyaIRI("urn:icecream")) .setProperty(new RyaIRI("urn:icecream"), new Property(new RyaIRI("urn:brand"), new RyaType(XMLSchema.STRING, "Awesome Icecream"))) .setProperty(new RyaIRI("urn:icecream"), new Property(new RyaIRI("urn:flavor"), new RyaType(XMLSchema.STRING, "Chocolate"))) .build(); // Create it. final EntityStorage storage = new MongoEntityStorage(super.getMongoClient(), RYA_INSTANCE_NAME); storage.create(entity); // Get it. final Optional<Entity> storedEntity = storage.get(new RyaIRI("urn:GTIN-14/00012345600012")); // Verify the correct value was returned. assertEquals(entity, storedEntity.get()); } @Test public void can_not_create_with_same_subject() throws Exception { // A Type that will be stored. final Entity entity = Entity.builder() .setSubject(new RyaIRI("urn:GTIN-14/00012345600012")) .setExplicitType(new RyaIRI("urn:icecream")) .setProperty(new RyaIRI("urn:icecream"), new Property(new RyaIRI("urn:brand"), new RyaType(XMLSchema.STRING, "Awesome Icecream"))) .setProperty(new RyaIRI("urn:icecream"), new Property(new RyaIRI("urn:flavor"), new RyaType(XMLSchema.STRING, "Chocolate"))) .build(); // Create it. final EntityStorage storage = new MongoEntityStorage(super.getMongoClient(), RYA_INSTANCE_NAME); storage.create(entity); // Try to create it again. This will fail. boolean failed = false; try { storage.create(entity); } catch(final EntityAlreadyExistsException e) { failed = true; } assertTrue(failed); } @Test public void get_noneExisting() throws Exception { // Get a Type that hasn't been created. final EntityStorage storage = new MongoEntityStorage(super.getMongoClient(), RYA_INSTANCE_NAME); final Optional<Entity> storedEntity = storage.get(new RyaIRI("urn:GTIN-14/00012345600012")); // Verify nothing was returned. assertFalse(storedEntity.isPresent()); } @Test public void delete() throws Exception { // An Entity that will be stored. final Entity entity = Entity.builder() .setSubject(new RyaIRI("urn:GTIN-14/00012345600012")) .setExplicitType(new RyaIRI("urn:icecream")) .setProperty(new RyaIRI("urn:icecream"), new Property(new RyaIRI("urn:brand"), new RyaType(XMLSchema.STRING, "Awesome Icecream"))) .setProperty(new RyaIRI("urn:icecream"), new Property(new RyaIRI("urn:flavor"), new RyaType(XMLSchema.STRING, "Chocolate"))) .build(); // Create it. final EntityStorage storage = new MongoEntityStorage(super.getMongoClient(), RYA_INSTANCE_NAME); storage.create(entity); // Delete it. final boolean deleted = storage.delete( new RyaIRI("urn:GTIN-14/00012345600012") ); // Verify a document was deleted. assertTrue( deleted ); } @Test public void delete_nonExisting() throws Exception { // Delete an Entity that has not been created. final EntityStorage storage = new MongoEntityStorage(super.getMongoClient(), RYA_INSTANCE_NAME); final boolean deleted = storage.delete( new RyaIRI("urn:GTIN-14/00012345600012") ); // Verify no document was deleted. assertFalse( deleted ); } @Test public void search_byDataType() throws Exception { final EntityStorage storage = new MongoEntityStorage(super.getMongoClient(), RYA_INSTANCE_NAME); // The Type we will search by. final Type icecreamType = new Type(new RyaIRI("urn:icecream"), ImmutableSet.<RyaIRI>builder() .add(new RyaIRI("urn:brand")) .add(new RyaIRI("urn:flavor")) .add(new RyaIRI("urn:cost")) .build()); // Some Person typed entities. final Entity alice = Entity.builder() .setSubject( new RyaIRI("urn:SSN/111-11-1111") ) .setExplicitType(new RyaIRI("urn:person")) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:name"), new RyaType(XMLSchema.STRING, "Alice"))) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:age"), new RyaType(XMLSchema.INT, "30"))) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:eye"), new RyaType(XMLSchema.STRING, "blue"))) .build(); final Entity bob = Entity.builder() .setSubject( new RyaIRI("urn:SSN/222-22-2222") ) .setExplicitType(new RyaIRI("urn:person")) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:name"), new RyaType(XMLSchema.STRING, "Bob"))) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:age"), new RyaType(XMLSchema.INT, "57"))) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:eye"), new RyaType(XMLSchema.STRING, "blue"))) .build(); // Some Icecream typed objects. final Entity chocolateIcecream = Entity.builder() .setSubject(new RyaIRI("urn:GTIN-14/00012345600012")) .setExplicitType(new RyaIRI("urn:icecream")) .setProperty(new RyaIRI("urn:icecream"), new Property(new RyaIRI("urn:brand"), new RyaType(XMLSchema.STRING, "Awesome Icecream"))) .setProperty(new RyaIRI("urn:icecream"), new Property(new RyaIRI("urn:flavor"), new RyaType(XMLSchema.STRING, "Chocolate"))) .build(); final Entity vanillaIcecream = Entity.builder() .setSubject( new RyaIRI("urn:GTIN-14/22356325213432") ) .setExplicitType(new RyaIRI("urn:icecream")) .setProperty(new RyaIRI("urn:icecream"), new Property(new RyaIRI("urn:brand"), new RyaType(XMLSchema.STRING, "Awesome Icecream"))) .setProperty(new RyaIRI("urn:icecream"), new Property(new RyaIRI("urn:flavor"), new RyaType(XMLSchema.STRING, "Vanilla"))) .build(); final Entity strawberryIcecream = Entity.builder() .setSubject( new RyaIRI("urn:GTIN-14/77544325436721") ) .setProperty(new RyaIRI("urn:icecream"), new Property(new RyaIRI("urn:brand"), new RyaType(XMLSchema.STRING, "Awesome Icecream"))) .setProperty(new RyaIRI("urn:icecream"), new Property(new RyaIRI("urn:flavor"), new RyaType(XMLSchema.STRING, "Strawberry"))) .build(); // Create the objects in the storage. storage.create(alice); storage.create(bob); storage.create(chocolateIcecream); storage.create(vanillaIcecream); storage.create(strawberryIcecream); // Search for all icecreams. final Set<TypedEntity> objects = new HashSet<>(); try(final ConvertingCursor<TypedEntity> it = storage.search(Optional.empty(), icecreamType, new HashSet<>())) { while(it.hasNext()) { objects.add(it.next()); } } // Verify the expected results were returned. final Set<TypedEntity> expected = Sets.newHashSet( chocolateIcecream.makeTypedEntity(new RyaIRI("urn:icecream")).get(), vanillaIcecream.makeTypedEntity(new RyaIRI("urn:icecream")).get()); assertEquals(expected, objects); } @Test public void search_byFields() throws Exception { final EntityStorage storage = new MongoEntityStorage(super.getMongoClient(), RYA_INSTANCE_NAME); // A Type that defines a Person. final Type personType = new Type(new RyaIRI("urn:person"), ImmutableSet.<RyaIRI>builder() .add(new RyaIRI("urn:name")) .add(new RyaIRI("urn:age")) .add(new RyaIRI("urn:eye")) .build()); // Some Person typed objects. final Entity alice = Entity.builder() .setSubject( new RyaIRI("urn:SSN/111-11-1111") ) .setExplicitType(new RyaIRI("urn:person")) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:name"), new RyaType(XMLSchema.STRING, "Alice"))) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:age"), new RyaType(XMLSchema.INT, "30"))) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:eye"), new RyaType(XMLSchema.STRING, "blue"))) .build(); final Entity bob = Entity.builder() .setSubject( new RyaIRI("urn:SSN/222-22-2222") ) .setExplicitType(new RyaIRI("urn:person")) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:name"), new RyaType(XMLSchema.STRING, "Bob"))) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:age"), new RyaType(XMLSchema.INT, "57"))) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:eye"), new RyaType(XMLSchema.STRING, "blue"))) .build(); final Entity charlie = Entity.builder() .setSubject( new RyaIRI("urn:SSN/333-33-3333") ) .setExplicitType( new RyaIRI("urn:person") ) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:name"), new RyaType(XMLSchema.STRING, "Charlie"))) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:age"), new RyaType(XMLSchema.INT, "30"))) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:eye"), new RyaType(XMLSchema.STRING, "blue"))) .build(); final Entity david = Entity.builder() .setSubject( new RyaIRI("urn:SSN/444-44-4444") ) .setExplicitType( new RyaIRI("urn:person") ) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:name"), new RyaType(XMLSchema.STRING, "David"))) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:age"), new RyaType(XMLSchema.INT, "30"))) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:eye"), new RyaType(XMLSchema.STRING, "brown"))) .build(); final Entity eve = Entity.builder() .setSubject( new RyaIRI("urn:SSN/555-55-5555") ) .setExplicitType( new RyaIRI("urn:person") ) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:name"), new RyaType(XMLSchema.STRING, "Eve"))) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:age"), new RyaType(XMLSchema.INT, "30"))) .build(); final Entity frank = Entity.builder() .setSubject( new RyaIRI("urn:SSN/666-66-6666") ) .setExplicitType( new RyaIRI("urn:person") ) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:name"), new RyaType(XMLSchema.STRING, "Frank"))) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:eye"), new RyaType(XMLSchema.STRING, "blue"))) .setProperty(new RyaIRI("urn:someOtherType"), new Property(new RyaIRI("urn:age"), new RyaType(XMLSchema.INT, "30"))) .build(); final Entity george = Entity.builder() .setSubject( new RyaIRI("urn:SSN/777-77-7777") ) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:name"), new RyaType(XMLSchema.STRING, "George"))) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:age"), new RyaType(XMLSchema.INT, "30"))) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:eye"), new RyaType(XMLSchema.STRING, "blue"))) .build(); // Create the objects in the storage. storage.create(alice); storage.create(bob); storage.create(charlie); storage.create(david); storage.create(eve); storage.create(frank); storage.create(george); // Search for all people who are 30 and have blue eyes. final Set<TypedEntity> objects = new HashSet<>(); final Set<Property> searchValues = Sets.newHashSet( new Property(new RyaIRI("urn:eye"), new RyaType(XMLSchema.STRING, "blue")), new Property(new RyaIRI("urn:age"), new RyaType(XMLSchema.INT, "30"))); try(final ConvertingCursor<TypedEntity> it = storage.search(Optional.empty(), personType, searchValues)) { while(it.hasNext()) { objects.add(it.next()); } } // Verify the expected results were returned. assertEquals(2, objects.size()); assertTrue(objects.contains(alice.makeTypedEntity(new RyaIRI("urn:person")).get())); assertTrue(objects.contains(charlie.makeTypedEntity(new RyaIRI("urn:person")).get())); } @Test public void update() throws Exception { final EntityStorage storage = new MongoEntityStorage(super.getMongoClient(), RYA_INSTANCE_NAME); // Store Alice in the repository. final Entity alice = Entity.builder() .setSubject( new RyaIRI("urn:SSN/111-11-1111") ) .setExplicitType(new RyaIRI("urn:person")) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:name"), new RyaType(XMLSchema.STRING, "Alice"))) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:age"), new RyaType(XMLSchema.INT, "30"))) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:eye"), new RyaType(XMLSchema.STRING, "blue"))) .build(); storage.create(alice); // Show Alice was stored. Optional<Entity> latest = storage.get(new RyaIRI("urn:SSN/111-11-1111")); assertEquals(alice, latest.get()); // Change Alice's eye color to brown. final Entity updated = Entity.builder(alice) .setVersion(latest.get().getVersion() + 1) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:eye"), new RyaType(XMLSchema.STRING, "brown"))) .build(); storage.update(alice, updated); // Fetch the Alice object and ensure it has the new value. latest = storage.get(new RyaIRI("urn:SSN/111-11-1111")); assertEquals(updated, latest.get()); } @Test(expected = StaleUpdateException.class) public void update_stale() throws Exception { final EntityStorage storage = new MongoEntityStorage(super.getMongoClient(), RYA_INSTANCE_NAME); // Store Alice in the repository. final Entity alice = Entity.builder() .setSubject( new RyaIRI("urn:SSN/111-11-1111") ) .setExplicitType(new RyaIRI("urn:person")) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:name"), new RyaType(XMLSchema.STRING, "Alice"))) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:age"), new RyaType(XMLSchema.INT, "30"))) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:eye"), new RyaType(XMLSchema.STRING, "blue"))) .build(); storage.create(alice); // Show Alice was stored. final Optional<Entity> latest = storage.get(new RyaIRI("urn:SSN/111-11-1111")); assertEquals(alice, latest.get()); // Create the wrong old state and try to change Alice's eye color to brown. final Entity wrongOld = Entity.builder(alice) .setVersion(500) .build(); final Entity updated = Entity.builder(alice) .setVersion(501) .setProperty(new RyaIRI("urn:person"), new Property(new RyaIRI("urn:eye"), new RyaType(XMLSchema.STRING, "brown"))) .build(); storage.update(wrongOld, updated); } @Test(expected = EntityStorageException.class) public void update_differentSubjects() throws Exception { // Two objects that do not have the same Subjects. final Entity old = Entity.builder() .setSubject( new RyaIRI("urn:SSN/111-11-1111") ) .setExplicitType( new RyaIRI("urn:person") ) .build(); final Entity updated = Entity.builder() .setSubject( new RyaIRI("urn:SSN/222-22-2222") ) .setExplicitType( new RyaIRI("urn:person") ) .build(); // The update will fail. final EntityStorage storage = new MongoEntityStorage(super.getMongoClient(), RYA_INSTANCE_NAME); storage.update(old, updated); } }