diff --git a/ass1-doc/pom.xml b/ass1-doc/pom.xml
new file mode 100644
index 0000000..3edacf9
--- /dev/null
+++ b/ass1-doc/pom.xml
@@ -0,0 +1,36 @@
+
+
+
+ 4.0.0
+
+
+ at.ac.tuwien.infosys.dst
+ dst
+ 2021.1
+ ..
+
+
+ ass1-doc
+
+ jar
+
+ DST :: Assignment 1 :: Document DB
+
+
+
+ at.ac.tuwien.infosys.dst
+ ass1-jpa
+ ${project.version}
+
+
+
+ org.mongodb
+ mongodb-driver
+
+
+ de.flapdoodle.embed
+ de.flapdoodle.embed.mongo
+
+
+
diff --git a/ass1-doc/src/main/java/dst/ass1/doc/IDocumentQuery.java b/ass1-doc/src/main/java/dst/ass1/doc/IDocumentQuery.java
new file mode 100644
index 0000000..c0bf921
--- /dev/null
+++ b/ass1-doc/src/main/java/dst/ass1/doc/IDocumentQuery.java
@@ -0,0 +1,15 @@
+package dst.ass1.doc;
+
+import org.bson.Document;
+
+import java.util.List;
+
+public interface IDocumentQuery {
+
+ Document findLocationById(Long locationId);
+
+ List findIdsByNameAndRadius(String name, double longitude, double latitude, double radius);
+
+ List getDocumentStatistics();
+
+}
diff --git a/ass1-doc/src/main/java/dst/ass1/doc/IDocumentRepository.java b/ass1-doc/src/main/java/dst/ass1/doc/IDocumentRepository.java
new file mode 100644
index 0000000..c2a8679
--- /dev/null
+++ b/ass1-doc/src/main/java/dst/ass1/doc/IDocumentRepository.java
@@ -0,0 +1,11 @@
+package dst.ass1.doc;
+
+import dst.ass1.jpa.model.ILocation;
+
+import java.util.Map;
+
+public interface IDocumentRepository {
+
+ void insert(ILocation location, Map locationProperties);
+
+}
diff --git a/ass1-doc/src/main/java/dst/ass1/doc/IDocumentServiceFactory.java b/ass1-doc/src/main/java/dst/ass1/doc/IDocumentServiceFactory.java
new file mode 100644
index 0000000..1ab0da1
--- /dev/null
+++ b/ass1-doc/src/main/java/dst/ass1/doc/IDocumentServiceFactory.java
@@ -0,0 +1,11 @@
+package dst.ass1.doc;
+
+import com.mongodb.client.MongoDatabase;
+
+public interface IDocumentServiceFactory {
+
+ IDocumentQuery createDocumentQuery(MongoDatabase db);
+
+ IDocumentRepository createDocumentRepository();
+
+}
diff --git a/ass1-doc/src/main/java/dst/ass1/doc/impl/DocumentServiceFactory.java b/ass1-doc/src/main/java/dst/ass1/doc/impl/DocumentServiceFactory.java
new file mode 100644
index 0000000..823f48e
--- /dev/null
+++ b/ass1-doc/src/main/java/dst/ass1/doc/impl/DocumentServiceFactory.java
@@ -0,0 +1,21 @@
+package dst.ass1.doc.impl;
+
+import com.mongodb.client.MongoDatabase;
+import dst.ass1.doc.IDocumentQuery;
+import dst.ass1.doc.IDocumentRepository;
+import dst.ass1.doc.IDocumentServiceFactory;
+
+public class DocumentServiceFactory implements IDocumentServiceFactory {
+
+ @Override
+ public IDocumentQuery createDocumentQuery(MongoDatabase db) {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public IDocumentRepository createDocumentRepository() {
+ // TODO
+ return null;
+ }
+}
diff --git a/ass1-doc/src/test/java/dst/ass1/doc/DocumentTestData.java b/ass1-doc/src/test/java/dst/ass1/doc/DocumentTestData.java
new file mode 100644
index 0000000..dd72ab4
--- /dev/null
+++ b/ass1-doc/src/test/java/dst/ass1/doc/DocumentTestData.java
@@ -0,0 +1,45 @@
+package dst.ass1.doc;
+
+import com.mongodb.client.MongoDatabase;
+import dst.ass1.jpa.util.Constants;
+import org.bson.Document;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.List;
+import java.util.Objects;
+import java.util.Scanner;
+
+public class DocumentTestData implements IDocumentTestData {
+
+ private String documentResource;
+
+ public DocumentTestData() {
+ this("documents.json");
+ }
+
+ public DocumentTestData(String documentResource) {
+ this.documentResource = documentResource;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void insertTestData(MongoDatabase db) throws IOException {
+ URL resource = Objects.requireNonNull(getDocumentsResource());
+
+ String testDataJson = readFully(resource);
+ List documents = Document.parse(testDataJson).get("documents", List.class);
+ db.getCollection(Constants.COLL_LOCATION_DATA).insertMany(documents);
+ }
+
+ private URL getDocumentsResource() {
+ return getClass().getClassLoader().getResource(documentResource);
+ }
+
+ private String readFully(URL resource) throws IOException {
+ try (Scanner scanner = new Scanner(resource.openStream())) {
+ return scanner.useDelimiter("\\Z").next();
+ }
+ }
+
+}
diff --git a/ass1-doc/src/test/java/dst/ass1/doc/EmbeddedMongo.java b/ass1-doc/src/test/java/dst/ass1/doc/EmbeddedMongo.java
new file mode 100644
index 0000000..9d01b63
--- /dev/null
+++ b/ass1-doc/src/test/java/dst/ass1/doc/EmbeddedMongo.java
@@ -0,0 +1,56 @@
+package dst.ass1.doc;
+
+import de.flapdoodle.embed.mongo.config.MongodConfig;
+import de.flapdoodle.embed.mongo.config.Net;
+import de.flapdoodle.embed.mongo.distribution.IFeatureAwareVersion;
+import de.flapdoodle.embed.mongo.distribution.Version;
+import de.flapdoodle.embed.mongo.tests.MongodForTestsFactory;
+import org.junit.rules.ExternalResource;
+
+import java.io.IOException;
+import java.net.BindException;
+import java.net.ServerSocket;
+
+/**
+ * JUnit rule that creates an in-memory instance of MongoDB using the flapdoodle Embedded MongoDB.
+ */
+public class EmbeddedMongo extends ExternalResource {
+
+ private MongodForTestsFactory mongod;
+
+ @Override
+ protected void before() throws Throwable {
+ requirePort();
+ mongod = new MongodFactory(); // starts process in constructor
+ }
+
+ @Override
+ protected void after() {
+ if (mongod != null) {
+ mongod.shutdown();
+ }
+ }
+
+ private void requirePort() throws IOException, IllegalStateException {
+ try (ServerSocket ignore = new ServerSocket(27017)) {
+ // ignore
+ } catch (BindException e) {
+ throw new IllegalStateException("Could not bind port 27017 which is necessary to run the MongoDB tests", e);
+ }
+ }
+
+ public static class MongodFactory extends MongodForTestsFactory {
+
+ public MongodFactory() throws IOException {
+ super(Version.Main.V3_5);
+ }
+
+ @Override
+ protected MongodConfig newMongodConfig(IFeatureAwareVersion version) throws IOException {
+ return MongodConfig.builder()
+ .net(new Net(27017, false))
+ .version(version)
+ .build();
+ }
+ }
+}
diff --git a/ass1-doc/src/test/java/dst/ass1/doc/IDocumentTestData.java b/ass1-doc/src/test/java/dst/ass1/doc/IDocumentTestData.java
new file mode 100644
index 0000000..f2a57a3
--- /dev/null
+++ b/ass1-doc/src/test/java/dst/ass1/doc/IDocumentTestData.java
@@ -0,0 +1,18 @@
+package dst.ass1.doc;
+
+import com.mongodb.client.MongoDatabase;
+
+// DO NOT MODIFY THIS CLASS.
+
+/**
+ * The IDocumentTestData interface works like the ITestData as introduced in ass1-jpa only for the {@link MongoService}.
+ */
+public interface IDocumentTestData {
+ /**
+ * Inserts the data into the given MongoDatabase instance.
+ *
+ * @param db the mongo database instance
+ * @throws Exception if the insertion failed for some reason
+ */
+ void insertTestData(MongoDatabase db) throws Exception;
+}
diff --git a/ass1-doc/src/test/java/dst/ass1/doc/MockLocation.java b/ass1-doc/src/test/java/dst/ass1/doc/MockLocation.java
new file mode 100644
index 0000000..4e2c79a
--- /dev/null
+++ b/ass1-doc/src/test/java/dst/ass1/doc/MockLocation.java
@@ -0,0 +1,41 @@
+package dst.ass1.doc;
+
+import dst.ass1.jpa.model.ILocation;
+
+public class MockLocation implements ILocation {
+
+ private Long id;
+ private String name;
+ private Long LocationId;
+
+
+ @Override
+ public Long getId() {
+ return id;
+ }
+
+ @Override
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public Long getLocationId() {
+ return LocationId;
+ }
+
+ @Override
+ public void setLocationId(Long locationId) {
+ LocationId = locationId;
+ }
+}
diff --git a/ass1-doc/src/test/java/dst/ass1/doc/MongoService.java b/ass1-doc/src/test/java/dst/ass1/doc/MongoService.java
new file mode 100644
index 0000000..c242133
--- /dev/null
+++ b/ass1-doc/src/test/java/dst/ass1/doc/MongoService.java
@@ -0,0 +1,124 @@
+package dst.ass1.doc;
+
+import com.mongodb.MongoClient;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoDatabase;
+import dst.ass1.doc.impl.DocumentServiceFactory;
+import dst.ass1.jpa.util.Constants;
+import org.bson.Document;
+import org.junit.rules.ExternalResource;
+
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+// DO NOT MODIFY THIS CLASS.
+
+/**
+ * The MongoService class is used as a JUnit rule, that fulfills the same tasks as the ORMService in ass1-jpa, and works
+ * very similarly.
+ */
+public class MongoService extends ExternalResource {
+
+ private IDocumentTestData testData;
+ private boolean insertTestData;
+ private boolean clearTestData;
+
+ private MongoClient mongoClient;
+ private MongoDatabase mongoDatabase;
+
+ private IDocumentServiceFactory documentServiceFactory;
+ private IDocumentRepository documentRepository;
+ private IDocumentQuery documentQuery;
+
+ public MongoService() {
+ this(null, false, true);
+ }
+
+ public MongoService(IDocumentTestData testData) {
+ this(testData, true, true);
+ }
+
+ public MongoService(IDocumentTestData testData, boolean insertTestData, boolean clearTestData) {
+ this.testData = testData;
+ this.insertTestData = insertTestData;
+ this.clearTestData = clearTestData;
+ }
+
+ public static Stream stream(MongoCollection collection) {
+ return StreamSupport.stream(collection.find().spliterator(), false);
+ }
+
+ public static Map idMap(MongoCollection collection, Function idFunction) {
+ return stream(collection).collect(Collectors.toMap(idFunction, Function.identity()));
+ }
+
+ public MongoClient getMongoClient() {
+ return mongoClient;
+ }
+
+ public MongoDatabase getMongoDatabase() {
+ return mongoDatabase;
+ }
+
+ public IDocumentServiceFactory getDocumentServiceFactory() {
+ return documentServiceFactory;
+ }
+
+ public IDocumentRepository getDocumentRepository() {
+ return documentRepository;
+ }
+
+ public IDocumentQuery getDocumentQuery() {
+ return documentQuery;
+ }
+
+ @Override
+ protected void before() throws Throwable {
+ setUpMongo();
+
+ if (insertTestData && testData != null) {
+ insertData(testData);
+ }
+ }
+
+ @Override
+ protected void after() {
+ try {
+ if (clearTestData) {
+ clearData();
+ }
+ } finally {
+ tearDownMongo();
+ }
+ }
+
+ private void setUpMongo() {
+ mongoClient = new MongoClient("127.0.0.1");
+ mongoDatabase = mongoClient.getDatabase(Constants.MONGO_DB_NAME);
+
+ documentServiceFactory = new DocumentServiceFactory();
+ documentRepository = documentServiceFactory.createDocumentRepository();
+
+ if (documentRepository == null) {
+ throw new IllegalStateException("DocumentRepository returned from factory is null");
+ }
+
+ documentQuery = documentServiceFactory.createDocumentQuery(mongoDatabase);
+ }
+
+ private void tearDownMongo() {
+ mongoClient.close();
+ }
+
+ private void insertData(IDocumentTestData testData) throws Exception {
+ testData.insertTestData(getMongoDatabase());
+ }
+
+ private void clearData() {
+ getMongoDatabase().drop();
+ }
+
+}
diff --git a/ass1-doc/src/test/java/dst/ass1/doc/tests/Ass1_4_1Test.java b/ass1-doc/src/test/java/dst/ass1/doc/tests/Ass1_4_1Test.java
new file mode 100644
index 0000000..06c16e2
--- /dev/null
+++ b/ass1-doc/src/test/java/dst/ass1/doc/tests/Ass1_4_1Test.java
@@ -0,0 +1,128 @@
+package dst.ass1.doc.tests;
+
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoDatabase;
+import dst.ass1.doc.EmbeddedMongo;
+import dst.ass1.doc.IDocumentRepository;
+import dst.ass1.doc.MockLocation;
+import dst.ass1.doc.MongoService;
+import dst.ass1.jpa.util.Constants;
+import org.bson.Document;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.*;
+
+public class Ass1_4_1Test {
+
+ public static final String LOCATION_NAME_1 = "testName1";
+ public static final String LOCATION_NAME_2 = "testName2";
+ public static final Long LOCATION_ID_1 = 1L;
+ public static final Long LOCATION_ID_2 = 2L;
+ public static final Double GEO_1_1 = 16.3699;
+ public static final Double GEO_1_2 = 48.199;
+ public static final Double GEO_2_1 = 16.368528;
+ public static final Double GEO_2_2 = 48.200939;
+ @ClassRule
+ public static EmbeddedMongo embeddedMongo = new EmbeddedMongo();
+ @Rule
+ public MongoService mongo = new MongoService();
+ private MockLocation l1;
+ private MockLocation l2;
+ private Map l1Properties;
+ private Map l2Properties;
+ private Map geo1;
+ private Map geo2;
+
+ @Before
+ public void setUp() {
+ l1 = new MockLocation();
+ l1.setName(LOCATION_NAME_1);
+ l1.setLocationId(LOCATION_ID_1);
+
+ l2 = new MockLocation();
+ l2.setName(LOCATION_NAME_2);
+ l2.setLocationId(LOCATION_ID_2);
+
+
+ l1Properties = new HashMap<>();
+
+ geo1 = new HashMap<>();
+ geo1.put("type", "Point");
+ geo1.put("coordinates", Arrays.asList(GEO_1_1, GEO_1_2));
+ l1Properties.put("type", "place");
+ l1Properties.put("geo", geo1);
+ l1Properties.put("1337", "7331");
+ l1Properties.put("Foo", "Bar");
+ l1Properties.put("Complex", Arrays.asList(1, 2, 3, 5, 8));
+
+ l2Properties = new HashMap<>();
+
+ geo2 = new HashMap<>();
+ geo2.put("type", "Point");
+ geo2.put("coordinates", Arrays.asList(GEO_2_1, GEO_2_2));
+ l2Properties.put("type", "place");
+ l2Properties.put("geo", geo2);
+ l2Properties.put("123456", "654321");
+ l2Properties.put("F00", "B@r");
+ l2Properties.put("Complex2", Arrays.asList(4, 6, 7, 9));
+ }
+
+ @Test
+ public void insert_insertsDocumentsCorrectly() throws Exception {
+ IDocumentRepository documentRepository = mongo.getDocumentRepository();
+ MongoDatabase mongoDatabase = mongo.getMongoDatabase();
+
+ documentRepository.insert(l1, l1Properties);
+ documentRepository.insert(l2, l2Properties);
+
+ MongoCollection collection = mongoDatabase.getCollection(Constants.COLL_LOCATION_DATA);
+ Map map = MongoService.idMap(collection, d -> d.getLong(Constants.I_LOCATION));
+
+ assertNotNull(map);
+ assertEquals(2, map.size());
+
+ Document document1 = map.get(1L);
+ assertEquals(LOCATION_ID_1, document1.get(Constants.I_LOCATION));
+ assertEquals(LOCATION_NAME_1, document1.get(Constants.M_LOCATION_NAME));
+
+ assertEquals("7331", document1.get("1337"));
+ assertEquals("Bar", document1.get("Foo"));
+ assertEquals(Arrays.asList(1, 2, 3, 5, 8), document1.get("Complex"));
+
+ assertEquals("place", document1.get("type"));
+
+ Map geo1Retrieved = document1.get("geo", new HashMap<>());
+ assertEquals(2, geo1Retrieved.size());
+ assertEquals("Point", geo1Retrieved.get("type"));
+ List coordinates1 = (List) geo1Retrieved.get("coordinates");
+ assertEquals(2, coordinates1.size());
+ assertThat(coordinates1, hasItems(GEO_1_1, GEO_1_2));
+
+ Document document2 = map.get(2L);
+ assertEquals(LOCATION_ID_2, document2.get(Constants.I_LOCATION));
+ assertEquals(LOCATION_NAME_2, document2.get(Constants.M_LOCATION_NAME));
+
+ assertEquals("654321", document2.get("123456"));
+ assertEquals("B@r", document2.get("F00"));
+ assertEquals(Arrays.asList(4, 6, 7, 9), document2.get("Complex2"));
+
+ assertEquals("place", document2.get("type"));
+
+ Map geo2Retrieved = document2.get("geo", new HashMap<>());
+ assertEquals(2, geo2Retrieved.size());
+ assertEquals("Point", geo2Retrieved.get("type"));
+ List coordinates2 = (List) geo2Retrieved.get("coordinates");
+ assertEquals(2, coordinates2.size());
+ assertThat(coordinates2, hasItems(GEO_2_1, GEO_2_2));
+ }
+}
diff --git a/ass1-doc/src/test/java/dst/ass1/doc/tests/Ass1_4_2aTest.java b/ass1-doc/src/test/java/dst/ass1/doc/tests/Ass1_4_2aTest.java
new file mode 100644
index 0000000..e9a60c9
--- /dev/null
+++ b/ass1-doc/src/test/java/dst/ass1/doc/tests/Ass1_4_2aTest.java
@@ -0,0 +1,53 @@
+package dst.ass1.doc.tests;
+
+import dst.ass1.doc.DocumentTestData;
+import dst.ass1.doc.EmbeddedMongo;
+import dst.ass1.doc.IDocumentQuery;
+import dst.ass1.doc.MongoService;
+import dst.ass1.jpa.util.Constants;
+import org.bson.Document;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+public class Ass1_4_2aTest {
+
+ @ClassRule
+ public static EmbeddedMongo embeddedMongo = new EmbeddedMongo();
+
+ @Rule
+ public MongoService mongo = new MongoService(new DocumentTestData());
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void findByLocationId_returnsCorrectDocument() {
+ IDocumentQuery documentQuery = mongo.getDocumentQuery();
+
+ Document location = documentQuery.findLocationById(2L);
+ assertNotNull(location);
+ assertEquals(2L, (long) location.getLong(Constants.I_LOCATION));
+ assertEquals("McDonald's", location.getString(Constants.M_LOCATION_NAME));
+ assertEquals("place", location.getString("type"));
+ assertEquals("Restaurant", location.getString("category"));
+
+ Document geo = location.get("geo", Document.class);
+ assertNotNull(geo);
+ List coordinates = geo.getList("coordinates", Double.class);
+ assertNotNull(coordinates);
+ assertEquals(2, coordinates.size());
+ assertEquals("Point", geo.getString("type"));
+ assertEquals(16.368528, coordinates.get(0), 0.0);
+ assertEquals(48.200939, coordinates.get(1), 0.0);
+ }
+
+ @Test
+ public void findByLocationId_withInvalidId_returnsNull() throws Exception {
+ Document material = mongo.getDocumentQuery().findLocationById(1337L);
+ assertNull(material);
+ }
+
+}
diff --git a/ass1-doc/src/test/java/dst/ass1/doc/tests/Ass1_4_2bTest.java b/ass1-doc/src/test/java/dst/ass1/doc/tests/Ass1_4_2bTest.java
new file mode 100644
index 0000000..85a683f
--- /dev/null
+++ b/ass1-doc/src/test/java/dst/ass1/doc/tests/Ass1_4_2bTest.java
@@ -0,0 +1,43 @@
+package dst.ass1.doc.tests;
+
+import dst.ass1.doc.DocumentTestData;
+import dst.ass1.doc.EmbeddedMongo;
+import dst.ass1.doc.MongoService;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.hamcrest.Matchers.hasItems;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.*;
+
+public class Ass1_4_2bTest {
+
+ @ClassRule
+ public static EmbeddedMongo embeddedMongo = new EmbeddedMongo();
+
+ @Rule
+ public MongoService mongo = new MongoService(new DocumentTestData());
+
+ @Test
+ public void findIdsByNameAndRadius_returnsCorrectLocationIds() throws Exception {
+ List locationIds = mongo.getDocumentQuery().findIdsByNameAndRadius("McD", 16.367873, 48.198763, 1000);
+ assertFalse(locationIds.isEmpty());
+ assertThat(locationIds, hasItems(2L, 3L, 4L));
+ }
+
+ @Test
+ public void findIdsByType_withNonExistingType_returnsNoLocationIds() throws Exception {
+ List locationIds = mongo.getDocumentQuery().findIdsByNameAndRadius("McD", 16.367873, 48.198763, 5);
+ assertNotNull(locationIds);
+ assertTrue(locationIds.isEmpty());
+
+ locationIds = mongo.getDocumentQuery().findIdsByNameAndRadius("NONEXISTING", 16.367873, 48.198763, 2000);
+
+ assertNotNull(locationIds);
+ assertTrue(locationIds.isEmpty());
+ }
+
+}
diff --git a/ass1-doc/src/test/java/dst/ass1/doc/tests/Ass1_4_3_01Test.java b/ass1-doc/src/test/java/dst/ass1/doc/tests/Ass1_4_3_01Test.java
new file mode 100644
index 0000000..1809ffe
--- /dev/null
+++ b/ass1-doc/src/test/java/dst/ass1/doc/tests/Ass1_4_3_01Test.java
@@ -0,0 +1,39 @@
+package dst.ass1.doc.tests;
+
+import dst.ass1.doc.EmbeddedMongo;
+import dst.ass1.doc.MongoService;
+import dst.ass1.jpa.util.Constants;
+import org.bson.Document;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.stream.StreamSupport;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class Ass1_4_3_01Test {
+
+ @ClassRule
+ public static EmbeddedMongo embeddedMongo = new EmbeddedMongo();
+
+ @Rule
+ public MongoService mongo = new MongoService(db -> {
+ boolean exists = StreamSupport.stream(db.listCollectionNames().spliterator(), false)
+ .anyMatch(Constants.COLL_LOCATION_DATA::equalsIgnoreCase);
+
+ if (!exists) {
+ db.createCollection(Constants.COLL_LOCATION_DATA); // make sure the empty collection exists
+ }
+ });
+
+ @Test
+ public void getDocumentStatistics_withEmptyData_returnsEmptyStatistics() throws Exception {
+ List documentStatistics = mongo.getDocumentQuery().getDocumentStatistics();
+ assertNotNull(documentStatistics);
+ assertTrue(documentStatistics.isEmpty());
+ }
+
+}
diff --git a/ass1-doc/src/test/java/dst/ass1/doc/tests/Ass1_4_3_02Test.java b/ass1-doc/src/test/java/dst/ass1/doc/tests/Ass1_4_3_02Test.java
new file mode 100644
index 0000000..d19d6b6
--- /dev/null
+++ b/ass1-doc/src/test/java/dst/ass1/doc/tests/Ass1_4_3_02Test.java
@@ -0,0 +1,46 @@
+package dst.ass1.doc.tests;
+
+import dst.ass1.doc.DocumentTestData;
+import dst.ass1.doc.EmbeddedMongo;
+import dst.ass1.doc.MongoService;
+import org.bson.Document;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.*;
+
+public class Ass1_4_3_02Test {
+
+ @ClassRule
+ public static EmbeddedMongo embeddedMongo = new EmbeddedMongo();
+
+ @Rule
+ public MongoService mongo = new MongoService(new DocumentTestData());
+
+ @Test
+ public void getDocumentStatistics_returnsCorrectStatistics() throws Exception {
+ List documentStatistics = mongo.getDocumentQuery().getDocumentStatistics();
+ assertNotNull(documentStatistics);
+ assertEquals(3, documentStatistics.size());
+
+ List types = documentStatistics.stream().map(d -> d.getString("_id")).collect(Collectors.toList());
+ assertThat("expected three aggregation keys", types, hasItems("Restaurant", "Park", "University"));
+
+ Map dsMap = documentStatistics.stream().collect(Collectors.toMap(
+ d -> d.getString("_id"),
+ d -> d.getDouble("value"))
+ );
+
+ assertEquals(4.0d, dsMap.get("Restaurant"), 0);
+ assertEquals(1.0d, dsMap.get("Park"), 0);
+ assertEquals(7.0d, dsMap.get("University"), 0);
+ }
+
+}
diff --git a/ass1-doc/src/test/java/dst/ass1/doc/tests/Ass1_4_Suite.java b/ass1-doc/src/test/java/dst/ass1/doc/tests/Ass1_4_Suite.java
new file mode 100644
index 0000000..4c3d1a2
--- /dev/null
+++ b/ass1-doc/src/test/java/dst/ass1/doc/tests/Ass1_4_Suite.java
@@ -0,0 +1,16 @@
+package dst.ass1.doc.tests;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+@RunWith(Suite.class)
+@SuiteClasses( {
+ Ass1_4_1Test.class,
+ Ass1_4_2aTest.class,
+ Ass1_4_2bTest.class,
+ Ass1_4_3_01Test.class,
+ Ass1_4_3_02Test.class
+})
+public class Ass1_4_Suite {
+}
diff --git a/ass1-doc/src/test/resources/documents.json b/ass1-doc/src/test/resources/documents.json
new file mode 100644
index 0000000..e81c03c
--- /dev/null
+++ b/ass1-doc/src/test/resources/documents.json
@@ -0,0 +1,184 @@
+{
+ "documents": [
+ {
+ "location_id": NumberLong(1),
+ "type": "place",
+ "name": "TU Wien",
+ "geo": {
+ "type": "Point",
+ "coordinates": [
+ 16.3699,
+ 48.199
+ ]
+ },
+ "category": "University"
+ },
+ {
+ "location_id": NumberLong(2),
+ "type": "place",
+ "name": "McDonald's",
+ "geo": {
+ "type": "Point",
+ "coordinates": [
+ 16.368528,
+ 48.200939
+ ]
+ },
+ "category": "Restaurant"
+ },
+ {
+ "location_id": NumberLong(3),
+ "type": "place",
+ "name": "McDonald's",
+ "geo": {
+ "type": "Point",
+ "coordinates": [
+ 16.374138,
+ 48.201085
+ ]
+ },
+ "category": "Restaurant"
+ },
+ {
+ "location_id": NumberLong(4),
+ "type": "place",
+ "name": "McDonald's",
+ "geo": {
+ "type": "Point",
+ "coordinates": [
+ 16.371674,
+ 48.205241
+ ]
+ },
+ "category": "Restaurant"
+ },
+ {
+ "location_id": NumberLong(5),
+ "type": "place",
+ "name": "McDonald's",
+ "geo": {
+ "type": "Point",
+ "coordinates": [
+ 16.380754,
+ 48.219325
+ ]
+ },
+ "category": "Restaurant"
+ },
+ {
+ "location_id": NumberLong(6),
+ "type": "address",
+ "name": "Zirkusgasse 1",
+ "geo": {
+ "type": "Point",
+ "coordinates": [
+ 16.384230,
+ 48.216310
+ ]
+ }
+ },
+ {
+ "location_id": NumberLong(7),
+ "type": "address",
+ "name": "Handelskai 9",
+ "geo": {
+ "type": "Point",
+ "coordinates": [
+ 16.406082,
+ 48.225571
+ ]
+ }
+ },
+ {
+ "location_id": NumberLong(8),
+ "type": "place",
+ "name": "Cairngorms National Park",
+ "geo": {
+ "type": "Point",
+ "coordinates": [
+ -3.562790,
+ 57.046944
+ ]
+ },
+ "category": "Park"
+ },
+ {
+ "location_id": NumberLong(9),
+ "type": "place",
+ "name": "Distributed Systems Group",
+ "geo": {
+ "type": "Point",
+ "coordinates": [
+ 16.371425,
+ 48.197742
+ ]
+ },
+ "category": "University"
+ },
+ {
+ "location_id": NumberLong(10),
+ "type": "place",
+ "name": "Computer Vision Lab",
+ "geo": {
+ "type": "Point",
+ "coordinates": [
+ 16.369572,
+ 48.195244
+ ]
+ },
+ "category": "University"
+ },
+ {
+ "location_id": NumberLong(11),
+ "type": "place",
+ "name": "Uni Wien",
+ "geo": {
+ "type": "Point",
+ "coordinates": [
+ 16.360372,
+ 48.213069
+ ]
+ },
+ "category": "University"
+ },
+ {
+ "location_id": NumberLong(12),
+ "type": "place",
+ "name": "BOKU Wien",
+ "geo": {
+ "type": "Point",
+ "coordinates": [
+ 16.337443,
+ 48.236530
+ ]
+ },
+ "category": "University"
+ },
+ {
+ "location_id": NumberLong(13),
+ "type": "place",
+ "name": "WU Wien",
+ "geo": {
+ "type": "Point",
+ "coordinates": [
+ 16.408456,
+ 48.213561
+ ]
+ },
+ "category": "University"
+ },
+ {
+ "location_id": NumberLong(14),
+ "type": "place",
+ "name": "Audimax TU Wien",
+ "geo": {
+ "type": "Point",
+ "coordinates": [
+ 16.363949,
+ 48.200822
+ ]
+ },
+ "category": "University"
+ }
+ ]
+}
diff --git a/ass1-doc/src/test/resources/logback.xml b/ass1-doc/src/test/resources/logback.xml
new file mode 100644
index 0000000..698878a
--- /dev/null
+++ b/ass1-doc/src/test/resources/logback.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+ %d{yyyy-MM-dd HH:mm:ss.SSS} - %highlight(%5p) [%12.12thread] %cyan(%-40.40logger{39}): %m%n
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ass1-jpa/pom.xml b/ass1-jpa/pom.xml
new file mode 100644
index 0000000..66a7e17
--- /dev/null
+++ b/ass1-jpa/pom.xml
@@ -0,0 +1,35 @@
+
+
+
+ 4.0.0
+
+
+ at.ac.tuwien.infosys.dst
+ dst
+ 2021.1
+ ..
+
+
+ ass1-jpa
+
+ jar
+
+ DST :: Assignment 1 :: JPA
+
+
+
+ org.hibernate
+ hibernate-core
+
+
+ org.hibernate
+ hibernate-validator
+
+
+ com.h2database
+ h2
+
+
+
+
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/dao/GenericDAO.java b/ass1-jpa/src/main/java/dst/ass1/jpa/dao/GenericDAO.java
new file mode 100644
index 0000000..4cb4180
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/dao/GenericDAO.java
@@ -0,0 +1,9 @@
+package dst.ass1.jpa.dao;
+
+import java.util.List;
+
+public interface GenericDAO {
+ T findById(Long id);
+
+ List findAll();
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/dao/IDAOFactory.java b/ass1-jpa/src/main/java/dst/ass1/jpa/dao/IDAOFactory.java
new file mode 100644
index 0000000..4d3d233
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/dao/IDAOFactory.java
@@ -0,0 +1,23 @@
+package dst.ass1.jpa.dao;
+
+public interface IDAOFactory {
+
+ IDriverDAO createDriverDAO();
+
+ IEmploymentDAO createEmploymentDAO();
+
+ ILocationDAO createLocationDAO();
+
+ IMatchDAO createMatchDAO();
+
+ IOrganizationDAO createOrganizationDAO();
+
+ IRiderDAO createRiderDAO();
+
+ ITripDAO createTripDAO();
+
+ ITripInfoDAO createTripInfoDAO();
+
+ IVehicleDAO createVehicleDAO();
+
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/dao/IDriverDAO.java b/ass1-jpa/src/main/java/dst/ass1/jpa/dao/IDriverDAO.java
new file mode 100644
index 0000000..e4e0884
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/dao/IDriverDAO.java
@@ -0,0 +1,19 @@
+package dst.ass1.jpa.dao;
+
+import dst.ass1.jpa.model.IDriver;
+
+import java.util.Collection;
+
+public interface IDriverDAO extends GenericDAO {
+
+
+ /**
+ * Find all drivers that have active employments (active at least one month) in more than the given number
+ * of organizations
+ *
+ * @param numberOfOrganizations number of organizations
+ * @return a collection containing drivers
+ */
+ Collection findActiveInMultipleOrganizationsDrivers(Long numberOfOrganizations);
+
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/dao/IEmploymentDAO.java b/ass1-jpa/src/main/java/dst/ass1/jpa/dao/IEmploymentDAO.java
new file mode 100644
index 0000000..6ebd976
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/dao/IEmploymentDAO.java
@@ -0,0 +1,6 @@
+package dst.ass1.jpa.dao;
+
+import dst.ass1.jpa.model.IEmployment;
+
+public interface IEmploymentDAO extends GenericDAO {
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/dao/ILocationDAO.java b/ass1-jpa/src/main/java/dst/ass1/jpa/dao/ILocationDAO.java
new file mode 100644
index 0000000..ccdaf27
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/dao/ILocationDAO.java
@@ -0,0 +1,16 @@
+package dst.ass1.jpa.dao;
+
+import dst.ass1.jpa.model.ILocation;
+
+import java.util.Collection;
+
+public interface ILocationDAO extends GenericDAO {
+
+ /**
+ * Finds all distinct Location.locationId that have been reached as a Trip destination or additional
+ * stops. Only takes completed trips in account.
+ *
+ * @return a list containing Location.locationIds
+ */
+ Collection findReachedLocationIds();
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/dao/IMatchDAO.java b/ass1-jpa/src/main/java/dst/ass1/jpa/dao/IMatchDAO.java
new file mode 100644
index 0000000..bd1b113
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/dao/IMatchDAO.java
@@ -0,0 +1,6 @@
+package dst.ass1.jpa.dao;
+
+import dst.ass1.jpa.model.IMatch;
+
+public interface IMatchDAO extends GenericDAO {
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/dao/IOrganizationDAO.java b/ass1-jpa/src/main/java/dst/ass1/jpa/dao/IOrganizationDAO.java
new file mode 100644
index 0000000..06ac881
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/dao/IOrganizationDAO.java
@@ -0,0 +1,6 @@
+package dst.ass1.jpa.dao;
+
+import dst.ass1.jpa.model.IOrganization;
+
+public interface IOrganizationDAO extends GenericDAO {
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/dao/IRiderDAO.java b/ass1-jpa/src/main/java/dst/ass1/jpa/dao/IRiderDAO.java
new file mode 100644
index 0000000..54868f6
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/dao/IRiderDAO.java
@@ -0,0 +1,33 @@
+package dst.ass1.jpa.dao;
+
+import dst.ass1.jpa.model.IMoney;
+import dst.ass1.jpa.model.IRider;
+
+import java.util.Map;
+
+public interface IRiderDAO extends GenericDAO {
+
+
+ /**
+ * Returns the rider associated with the given email. Returns null if the email does not exist.
+ *
+ * @param email the email address
+ * @return the rider or null
+ */
+ IRider findByEmail(String email);
+
+ /**
+ * Gets the total travel distance of the rider with the most recently completed trip
+ *
+ * @return the total distance the rider has travelled
+ */
+ Double getTotalDistanceOfMostRecentRider();
+
+ /**
+ * Finds for each Rider, who has completed at least one trip in the past 30 days, the sum of their total
+ * spendings of the past 30 days.
+ *
+ * @return a map containing each rider, and the money grouped by their currency.
+ */
+ Map> getRecentSpending();
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/dao/ITripDAO.java b/ass1-jpa/src/main/java/dst/ass1/jpa/dao/ITripDAO.java
new file mode 100644
index 0000000..2f896b7
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/dao/ITripDAO.java
@@ -0,0 +1,18 @@
+package dst.ass1.jpa.dao;
+
+import dst.ass1.jpa.model.ITrip;
+
+import java.util.Collection;
+import java.util.Date;
+
+public interface ITripDAO extends GenericDAO {
+
+ /**
+ * Find all canceled trips that were created in the given date range.
+ *
+ * @param start start of the date range
+ * @param end end of the date range
+ * @return the cancelled trips
+ */
+ Collection findCancelledTrips(Date start, Date end);
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/dao/ITripInfoDAO.java b/ass1-jpa/src/main/java/dst/ass1/jpa/dao/ITripInfoDAO.java
new file mode 100644
index 0000000..ba7d67b
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/dao/ITripInfoDAO.java
@@ -0,0 +1,6 @@
+package dst.ass1.jpa.dao;
+
+import dst.ass1.jpa.model.ITripInfo;
+
+public interface ITripInfoDAO extends GenericDAO {
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/dao/IVehicleDAO.java b/ass1-jpa/src/main/java/dst/ass1/jpa/dao/IVehicleDAO.java
new file mode 100644
index 0000000..92b4774
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/dao/IVehicleDAO.java
@@ -0,0 +1,6 @@
+package dst.ass1.jpa.dao;
+
+import dst.ass1.jpa.model.IVehicle;
+
+public interface IVehicleDAO extends GenericDAO {
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/dao/impl/DAOFactory.java b/ass1-jpa/src/main/java/dst/ass1/jpa/dao/impl/DAOFactory.java
new file mode 100644
index 0000000..f254f94
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/dao/impl/DAOFactory.java
@@ -0,0 +1,74 @@
+package dst.ass1.jpa.dao.impl;
+
+import dst.ass1.jpa.dao.*;
+
+import javax.persistence.EntityManager;
+
+public class DAOFactory implements IDAOFactory {
+
+ /*
+ * HINT: When using the org.hibernate.Session in your DAOs you can extract it from the EntityManager reference with
+ * e.g., em.unwrap(org.hibernate.Session.class). Do not store this org.hibernate.Session in your DAOs, but unwrap it
+ * every time you actually need it.
+ */
+
+ private EntityManager em;
+
+ public DAOFactory(EntityManager em) {
+ this.em = em;
+ }
+
+ @Override
+ public IDriverDAO createDriverDAO() {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public IEmploymentDAO createEmploymentDAO() {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public ILocationDAO createLocationDAO() {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public IMatchDAO createMatchDAO() {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public IOrganizationDAO createOrganizationDAO() {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public IRiderDAO createRiderDAO() {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public ITripDAO createTripDAO() {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public ITripInfoDAO createTripInfoDAO() {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public IVehicleDAO createVehicleDAO() {
+ // TODO
+ return null;
+ }
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/interceptor/SQLInterceptor.java b/ass1-jpa/src/main/java/dst/ass1/jpa/interceptor/SQLInterceptor.java
new file mode 100644
index 0000000..4d21aa4
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/interceptor/SQLInterceptor.java
@@ -0,0 +1,33 @@
+package dst.ass1.jpa.interceptor;
+
+import org.hibernate.EmptyInterceptor;
+
+public class SQLInterceptor extends EmptyInterceptor {
+
+ private static final long serialVersionUID = -3082243834965597947L;
+
+ public static void resetCounter() {
+ // TODO
+ }
+
+ public static int getSelectCount() {
+ // TODO
+ return -1;
+ }
+
+ /**
+ * If the verbose argument is set, the interceptor prints the intercepted SQL statements to System.out.
+ *
+ * @param verbose whether or not to be verbose
+ */
+ public static void setVerbose(boolean verbose) {
+ // TODO
+ }
+
+ @Override
+ public String onPrepareStatement(String sql) {
+ // TODO
+ return sql;
+ }
+
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/listener/DefaultListener.java b/ass1-jpa/src/main/java/dst/ass1/jpa/listener/DefaultListener.java
new file mode 100644
index 0000000..bd3a7bf
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/listener/DefaultListener.java
@@ -0,0 +1,44 @@
+package dst.ass1.jpa.listener;
+
+
+public class DefaultListener {
+
+ // TODO
+
+ public static int getLoadOperations() {
+ // TODO
+ return -1;
+ }
+
+ public static int getUpdateOperations() {
+ // TODO
+ return -1;
+ }
+
+ public static int getRemoveOperations() {
+ // TODO
+ return -1;
+ }
+
+ public static int getPersistOperations() {
+ // TODO
+ return -1;
+ }
+
+ public static long getOverallTimeToPersist() {
+ // TODO
+ return -1;
+ }
+
+ public static double getAverageTimeToPersist() {
+ // TODO
+ return -1;
+ }
+
+ /**
+ * Clears the internal data structures that are used for storing the operations.
+ */
+ public static void clear() {
+ // TODO
+ }
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/model/IDriver.java b/ass1-jpa/src/main/java/dst/ass1/jpa/model/IDriver.java
new file mode 100644
index 0000000..d9d2b11
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/model/IDriver.java
@@ -0,0 +1,17 @@
+package dst.ass1.jpa.model;
+
+import java.util.Collection;
+
+public interface IDriver extends IPlatformUser {
+
+ Collection getEmployments();
+
+ void setEmployments(Collection employments);
+
+ void addEmployment(IEmployment employment);
+
+ IVehicle getVehicle();
+
+ void setVehicle(IVehicle vehicle);
+
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/model/IEmployment.java b/ass1-jpa/src/main/java/dst/ass1/jpa/model/IEmployment.java
new file mode 100644
index 0000000..1c76271
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/model/IEmployment.java
@@ -0,0 +1,18 @@
+package dst.ass1.jpa.model;
+
+import java.util.Date;
+
+public interface IEmployment {
+
+ IEmploymentKey getId();
+
+ void setId(IEmploymentKey employmentKey);
+
+ Date getSince();
+
+ void setSince(Date since);
+
+ Boolean isActive();
+
+ void setActive(Boolean active);
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/model/IEmploymentKey.java b/ass1-jpa/src/main/java/dst/ass1/jpa/model/IEmploymentKey.java
new file mode 100644
index 0000000..ac06648
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/model/IEmploymentKey.java
@@ -0,0 +1,12 @@
+package dst.ass1.jpa.model;
+
+public interface IEmploymentKey {
+
+ IDriver getDriver();
+
+ void setDriver(IDriver driver);
+
+ IOrganization getOrganization();
+
+ void setOrganization(IOrganization organization);
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/model/ILocation.java b/ass1-jpa/src/main/java/dst/ass1/jpa/model/ILocation.java
new file mode 100644
index 0000000..67deeae
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/model/ILocation.java
@@ -0,0 +1,16 @@
+package dst.ass1.jpa.model;
+
+public interface ILocation {
+
+ Long getId();
+
+ void setId(Long id);
+
+ String getName();
+
+ void setName(String name);
+
+ Long getLocationId();
+
+ void setLocationId(Long locationId);
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/model/IMatch.java b/ass1-jpa/src/main/java/dst/ass1/jpa/model/IMatch.java
new file mode 100644
index 0000000..ae4b021
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/model/IMatch.java
@@ -0,0 +1,30 @@
+package dst.ass1.jpa.model;
+
+import java.util.Date;
+
+public interface IMatch {
+
+ Long getId();
+
+ void setId(Long id);
+
+ Date getDate();
+
+ void setDate(Date date);
+
+ IMoney getFare();
+
+ void setFare(IMoney money);
+
+ ITrip getTrip();
+
+ void setTrip(ITrip trip);
+
+ IVehicle getVehicle();
+
+ void setVehicle(IVehicle vehicle);
+
+ IDriver getDriver();
+
+ void setDriver(IDriver driver);
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/model/IModelFactory.java b/ass1-jpa/src/main/java/dst/ass1/jpa/model/IModelFactory.java
new file mode 100644
index 0000000..8821d11
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/model/IModelFactory.java
@@ -0,0 +1,30 @@
+package dst.ass1.jpa.model;
+
+public interface IModelFactory {
+
+ IModelFactory createModelFactory();
+
+ IDriver createDriver();
+
+ IEmployment createEmployment();
+
+ IEmploymentKey createEmploymentKey();
+
+ ILocation createLocation();
+
+ IMatch createMatch();
+
+ IMoney createMoney();
+
+ IOrganization createOrganization();
+
+ IRider createRider();
+
+ IPreferences createPreferences();
+
+ ITrip createTrip();
+
+ ITripInfo createTripInfo();
+
+ IVehicle createVehicle();
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/model/IMoney.java b/ass1-jpa/src/main/java/dst/ass1/jpa/model/IMoney.java
new file mode 100644
index 0000000..b255e6f
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/model/IMoney.java
@@ -0,0 +1,14 @@
+package dst.ass1.jpa.model;
+
+import java.math.BigDecimal;
+
+public interface IMoney {
+
+ String getCurrency();
+
+ void setCurrency(String currency);
+
+ BigDecimal getValue();
+
+ void setValue(BigDecimal value);
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/model/IOrganization.java b/ass1-jpa/src/main/java/dst/ass1/jpa/model/IOrganization.java
new file mode 100644
index 0000000..1a43f69
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/model/IOrganization.java
@@ -0,0 +1,38 @@
+package dst.ass1.jpa.model;
+
+import java.util.Collection;
+
+public interface IOrganization {
+
+ Long getId();
+
+ void setId(Long id);
+
+ String getName();
+
+ void setName(String name);
+
+ Collection getParts();
+
+ void setParts(Collection parts);
+
+ void addPart(IOrganization part);
+
+ Collection getPartOf();
+
+ void setPartOf(Collection partOf);
+
+ void addPartOf(IOrganization partOf);
+
+ Collection getEmployments();
+
+ void setEmployments(Collection employments);
+
+ void addEmployment(IEmployment employment);
+
+ Collection getVehicles();
+
+ void setVehicles(Collection vehicles);
+
+ void addVehicle(IVehicle vehicle);
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/model/IPlatformUser.java b/ass1-jpa/src/main/java/dst/ass1/jpa/model/IPlatformUser.java
new file mode 100644
index 0000000..d60e965
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/model/IPlatformUser.java
@@ -0,0 +1,20 @@
+package dst.ass1.jpa.model;
+
+public interface IPlatformUser {
+
+ Long getId();
+
+ void setId(Long id);
+
+ String getName();
+
+ void setName(String name);
+
+ String getTel();
+
+ void setTel(String tel);
+
+ Double getAvgRating();
+
+ void setAvgRating(Double avgRating);
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/model/IPreferences.java b/ass1-jpa/src/main/java/dst/ass1/jpa/model/IPreferences.java
new file mode 100644
index 0000000..6ce91a1
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/model/IPreferences.java
@@ -0,0 +1,16 @@
+package dst.ass1.jpa.model;
+
+import java.util.Map;
+
+public interface IPreferences {
+
+ Long getId();
+
+ void setId(Long id);
+
+ Map getData();
+
+ void setData(Map data);
+
+ void putData(String key, String value);
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/model/IRider.java b/ass1-jpa/src/main/java/dst/ass1/jpa/model/IRider.java
new file mode 100644
index 0000000..be561bf
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/model/IRider.java
@@ -0,0 +1,32 @@
+package dst.ass1.jpa.model;
+
+import java.util.Collection;
+
+public interface IRider extends IPlatformUser {
+
+ String getEmail();
+
+ void setEmail(String email);
+
+ byte[] getPassword();
+
+ void setPassword(byte[] password);
+
+ String getAccountNo();
+
+ void setAccountNo(String accountNo);
+
+ String getBankCode();
+
+ void setBankCode(String bankCode);
+
+ IPreferences getPreferences();
+
+ void setPreferences(IPreferences preferences);
+
+ Collection getTrips();
+
+ void setTrips(Collection trips);
+
+ void addTrip(ITrip trip);
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/model/ITrip.java b/ass1-jpa/src/main/java/dst/ass1/jpa/model/ITrip.java
new file mode 100644
index 0000000..13792c0
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/model/ITrip.java
@@ -0,0 +1,49 @@
+package dst.ass1.jpa.model;
+
+import java.util.Collection;
+import java.util.Date;
+
+public interface ITrip {
+
+ Long getId();
+
+ void setId(Long id);
+
+ Date getCreated();
+
+ void setCreated(Date created);
+
+ Date getUpdated();
+
+ void setUpdated(Date updated);
+
+ TripState getState();
+
+ void setState(TripState state);
+
+ ILocation getPickup();
+
+ void setPickup(ILocation pickup);
+
+ ILocation getDestination();
+
+ void setDestination(ILocation destination);
+
+ Collection getStops();
+
+ void setStops(Collection stops);
+
+ void addStop(ILocation stop);
+
+ ITripInfo getTripInfo();
+
+ void setTripInfo(ITripInfo tripInfo);
+
+ IMatch getMatch();
+
+ void setMatch(IMatch match);
+
+ IRider getRider();
+
+ void setRider(IRider rider);
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/model/ITripInfo.java b/ass1-jpa/src/main/java/dst/ass1/jpa/model/ITripInfo.java
new file mode 100644
index 0000000..2d9543d
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/model/ITripInfo.java
@@ -0,0 +1,35 @@
+package dst.ass1.jpa.model;
+
+import java.util.Date;
+
+public interface ITripInfo {
+
+ Long getId();
+
+ void setId(Long id);
+
+ Date getCompleted();
+
+ void setCompleted(Date date);
+
+ Double getDistance();
+
+ void setDistance(Double distance);
+
+ IMoney getTotal();
+
+ void setTotal(IMoney money);
+
+ Integer getDriverRating();
+
+ void setDriverRating(Integer driverRating);
+
+ Integer getRiderRating();
+
+ void setRiderRating(Integer riderRating);
+
+ ITrip getTrip();
+
+ void setTrip(ITrip trip);
+
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/model/IVehicle.java b/ass1-jpa/src/main/java/dst/ass1/jpa/model/IVehicle.java
new file mode 100644
index 0000000..7517a0d
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/model/IVehicle.java
@@ -0,0 +1,20 @@
+package dst.ass1.jpa.model;
+
+public interface IVehicle {
+
+ Long getId();
+
+ void setId(Long id);
+
+ String getLicense();
+
+ void setLicense(String license);
+
+ String getColor();
+
+ void setColor(String color);
+
+ String getType();
+
+ void setType(String type);
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/model/TripState.java b/ass1-jpa/src/main/java/dst/ass1/jpa/model/TripState.java
new file mode 100644
index 0000000..6fda2fa
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/model/TripState.java
@@ -0,0 +1,5 @@
+package dst.ass1.jpa.model;
+
+public enum TripState {
+ CREATED, QUEUED, MATCHED, APPROACHING, IN_PROGRESS, CANCELLED, COMPLETED
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/model/impl/ModelFactory.java b/ass1-jpa/src/main/java/dst/ass1/jpa/model/impl/ModelFactory.java
new file mode 100644
index 0000000..4fef967
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/model/impl/ModelFactory.java
@@ -0,0 +1,87 @@
+package dst.ass1.jpa.model.impl;
+
+import dst.ass1.jpa.model.*;
+
+/**
+ * Creates new instances of your model implementations.
+ */
+public class ModelFactory implements IModelFactory {
+
+ @Override
+ public IModelFactory createModelFactory() {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public IDriver createDriver() {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public IEmployment createEmployment() {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public IEmploymentKey createEmploymentKey() {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public ILocation createLocation() {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public IMatch createMatch() {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public IMoney createMoney() {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public IOrganization createOrganization() {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public IRider createRider() {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public IPreferences createPreferences() {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public ITrip createTrip() {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public ITripInfo createTripInfo() {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public IVehicle createVehicle() {
+ // TODO
+ return null;
+ }
+}
diff --git a/ass1-jpa/src/main/java/dst/ass1/jpa/util/Constants.java b/ass1-jpa/src/main/java/dst/ass1/jpa/util/Constants.java
new file mode 100644
index 0000000..1993980
--- /dev/null
+++ b/ass1-jpa/src/main/java/dst/ass1/jpa/util/Constants.java
@@ -0,0 +1,121 @@
+package dst.ass1.jpa.util;
+
+public final class Constants {
+
+ public static final String JPA_PERSISTENCE_UNIT = "dst_pu";
+
+ /* TYPES (CLASSES) */
+ public static final String T_DRIVER = "Driver";
+ public static final String T_EMPLOYMENT = "Employment";
+ public static final String T_LOCATION = "Location";
+ public static final String T_MATCH = "Match";
+ public static final String T_ORGANIZATION = "Organization";
+ public static final String T_PREFERENCES = "Preferences";
+ public static final String T_RIDER = "Rider";
+ public static final String T_TRIP = "Trip";
+ public static final String T_TRIP_INFO = "TripInfo";
+ public static final String T_VEHICLE = "Vehicle";
+
+ /* IDs (FOREIGN KEYS) */
+ public static final String I_DRIVER = "driver_id";
+ public static final String I_EMPLOYMENT = "employment_id";
+ public static final String I_LOCATION = "location_id";
+ public static final String I_MATCH = "match_id";
+ public static final String I_ORGANIZATION = "organization_id";
+ public static final String I_ORGANIZATION_PART_OF = "partOfOrganization_id";
+ public static final String I_ORGANIZATION_PARTS = "partsOrganization_id";
+ public static final String I_PREFERENCES = "preferences_id";
+ public static final String I_RIDER = "rider_id";
+ public static final String I_TRIP = "trip_id";
+ public static final String I_TRIP_INFO = "tripInfo_id";
+ public static final String I_VEHICLE = "vehicle_id";
+ public static final String I_VEHICLES = "vehicles_id";
+
+ public static final String I_DESTINATION = "destination_id";
+ public static final String I_PICKUP = "pickup_id";
+ public static final String I_STOPS = "stops_id";
+
+
+ /* MEMBER ATTRIBUTES */
+
+
+ public static final String M_DRIVER_TEL = "tel";
+ public static final String M_DRIVER_ORGANIZATIONS = "organizations";
+ public static final String M_DRIVER_EMPLOYMENTS = "employments";
+ public static final String M_DRIVER_VEHICLE = "vehicle";
+ public static final String M_DRIVER_NAME = "name";
+ public static final String M_DRIVER_AVG_RATING = "avgRating";
+ public static final String M_EMPLOYMENT_SINCE = "since";
+ public static final String M_EMPLOYMENT_ACTIVE = "active";
+ public static final String M_LOCATION_LOCATION_ID = "locationId";
+ public static final String M_LOCATION_GEO = "geo";
+ public static final String M_LOCATION_NAME = "name";
+ public static final String M_MATCH_DATE = "date";
+ public static final String M_MATCH_FARE = "fare";
+ public static final String M_MATCH_TRIP = "trip";
+ public static final String M_MATCH_VEHICLE = "vehicle";
+ public static final String M_MATCH_DRIVER = "driver";
+ public static final String M_ORGANIZATION_NAME = "name";
+ public static final String M_ORGANIZATION_EMPLOYMENTS = "employments";
+ public static final String M_ORGANIZATION_PARTS = "parts";
+ public static final String M_ORGANIZATION_DRIVERS = "drivers";
+ public static final String M_PREFERENCES_DATA = "data";
+ public static final String M_RIDER_BANK_CODE = "bankCode";
+ public static final String M_RIDER_EMAIL = "email";
+ public static final String M_RIDER_PASSWORD = "password";
+ public static final String M_RIDER_TEL = "tel";
+ public static final String M_RIDER_ACCOUNT = "accountNo";
+ public static final String M_RIDER_PREFERENCES = "preferences";
+ public static final String M_RIDER_TRIPS = "trips";
+ public static final String M_RIDER_NAME = "name";
+ public static final String M_RIDER_AVG_RATING = "avgRating";
+ public static final String M_TRIP_STOPS = "stops";
+ public static final String M_TRIP_STATE = "state";
+ public static final String M_TRIP_CREATED = "created";
+ public static final String M_TRIP_UPDATED = "updated";
+ public static final String M_TRIP_PICKUP = "pickup";
+ public static final String M_TRIP_DESTINATION = "destination";
+ public static final String M_TRIP_TRIP_INFO = "tripInfo";
+ public static final String M_TRIP_MATCH = "match";
+ public static final String M_TRIP_RIDER = "rider";
+ public static final String M_TRIP_INFO_COMPLETED = "completed";
+ public static final String M_TRIP_INFO_TOTAL = "total";
+ public static final String M_TRIP_INFO_DISTANCE = "distance";
+ public static final String M_TRIP_INFO_TRIP = "trip";
+ public static final String M_TRIP_INFO_DRIVER_RATING = "driverRating";
+ public static final String M_TRIP_INFO_RIDER_RATING = "riderRating";
+ public static final String M_VEHICLE_LICENSE = "license";
+ public static final String M_VEHICLE_COLOR = "color";
+ public static final String M_VEHICLE_TYPE = "type";
+
+
+ /* ASSOCIATION NAMES (FOR QUERIES) */
+ public static final String A_TRIP_INFO = "tripInfo";
+ public static final String A_TRIP = "trip";
+ public static final String A_RIDER = "rider";
+
+ /* NAMED QUERIES */
+ public static final String Q_RIDER_BY_EMAIL = "riderByEmail";
+ public static final String Q_ACTIVE_IN_MULITIPLE_ORGANIZATIONS_DRIVERS = "activeInMultipleOrganizationsDrivers";
+ public static final String Q_REACHED_LOCATIONS = "reachedLocations";
+ public static final String Q_SUM_DISTANCE_MOST_RECENT_TRIP = "sumDistanceOfRiderWithMostRecentTrip";
+
+ /* JOIN TABLES */
+ public static final String J_ORGANIZATION_VEHICLE = "organization_vehicle";
+ public static final String J_ORGANIZATION_PARTS = "organization_parts";
+ public static final String J_DRIVER_ORGANIZATION = "driver_organization";
+ public static final String J_PREFERENCES_DATA = "preferences_data";
+ public static final String J_DRIVER_EMPLOYMENT = "driver_employment";
+ public static final String J_ORGANIZATION_EMPLOYMENT = "organization_employment";
+ public static final String J_TRIP_LOCATION = "trip_location";
+ public static final String J_DRIVER_VEHICLE = "driver_vehicle";
+
+ /* MONGODB */
+ public static final String MONGO_DB_NAME = "dst";
+ public static final String COLL_LOCATION_DATA = "LocationData";
+
+
+ private Constants() {
+ // final
+ }
+}
diff --git a/ass1-jpa/src/main/resources/META-INF/Trip.xml b/ass1-jpa/src/main/resources/META-INF/Trip.xml
new file mode 100644
index 0000000..1291bec
--- /dev/null
+++ b/ass1-jpa/src/main/resources/META-INF/Trip.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
diff --git a/ass1-jpa/src/main/resources/META-INF/orm.xml b/ass1-jpa/src/main/resources/META-INF/orm.xml
new file mode 100644
index 0000000..1291bec
--- /dev/null
+++ b/ass1-jpa/src/main/resources/META-INF/orm.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
diff --git a/ass1-jpa/src/main/resources/META-INF/persistence.xml b/ass1-jpa/src/main/resources/META-INF/persistence.xml
new file mode 100644
index 0000000..613080d
--- /dev/null
+++ b/ass1-jpa/src/main/resources/META-INF/persistence.xml
@@ -0,0 +1,24 @@
+
+
+
+ org.hibernate.jpa.HibernatePersistenceProvider
+ META-INF/Trip.xml
+ META-INF/orm.xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ass1-jpa/src/test/java/dst/ass1/jpa/CaseInsensitiveStringCollectionMatcher.java b/ass1-jpa/src/test/java/dst/ass1/jpa/CaseInsensitiveStringCollectionMatcher.java
new file mode 100644
index 0000000..2954c6d
--- /dev/null
+++ b/ass1-jpa/src/test/java/dst/ass1/jpa/CaseInsensitiveStringCollectionMatcher.java
@@ -0,0 +1,52 @@
+package dst.ass1.jpa;
+
+import org.hamcrest.Description;
+import org.hamcrest.Factory;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Matcher that finds items in string collections in a case-insensitive way.
+ */
+public class CaseInsensitiveStringCollectionMatcher extends TypeSafeDiagnosingMatcher> {
+
+ private final String[] items;
+
+ public CaseInsensitiveStringCollectionMatcher(String... items) {
+ this.items = items;
+ }
+
+ @Factory
+ public static CaseInsensitiveStringCollectionMatcher hasItems(String... items) {
+ return new CaseInsensitiveStringCollectionMatcher(items);
+ }
+
+ @Override
+ protected boolean matchesSafely(List collection, Description description) {
+ List missing = new ArrayList<>();
+
+ for (String item : items) {
+ if (collection.stream().noneMatch(i -> i.equalsIgnoreCase(item))) {
+ missing.add(item);
+ }
+ }
+
+ if (!missing.isEmpty()) {
+ if (missing.size() == items.length) {
+ description.appendValueList("was [", ", ", "]", collection);
+ } else {
+ description.appendValueList("missing [", ", ", "]", missing)
+ .appendValueList(" in [", ", ", "]", collection);
+ }
+ }
+
+ return missing.isEmpty();
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendValueList("collection containing ", " and ", "", items);
+ }
+}
diff --git a/ass1-jpa/src/test/java/dst/ass1/jpa/DatabaseGateway.java b/ass1-jpa/src/test/java/dst/ass1/jpa/DatabaseGateway.java
new file mode 100644
index 0000000..0c1d4df
--- /dev/null
+++ b/ass1-jpa/src/test/java/dst/ass1/jpa/DatabaseGateway.java
@@ -0,0 +1,284 @@
+package dst.ass1.jpa;
+
+import org.hibernate.Session;
+import org.hibernate.jdbc.ReturningWork;
+
+import javax.persistence.EntityManager;
+import javax.persistence.metamodel.Type;
+import java.sql.*;
+import java.util.*;
+import java.util.stream.Collectors;
+
+// DO NOT MODIFY THIS CLASS.
+
+/**
+ * Contains various methods for accessing the database underlying an EntityManager.
+ *
+ * Note that the caller is responsible for dealing with possible exceptions as well as doing the connection handling. A
+ * connection will not be closed even if a fatal error occurs. However, other SQL resources i.e.,
+ * {@link Statement Statements} and {@link ResultSet ResultSets} created within the methods, which are not returned to
+ * the caller, are closed before the method returns.
+ */
+public class DatabaseGateway {
+
+ private final EntityManager em;
+
+ public DatabaseGateway(EntityManager em) {
+ this.em = em;
+ }
+
+ /**
+ * Returns a list of all table-names for the given database/connection.
+ *
+ * @return List of table names
+ */
+ public List getTables() {
+ return getSession().doReturningWork(new CollectionWork<>("show tables", rs -> rs.getString(1)));
+ }
+
+ /**
+ * Returns a list of all column names in the given table.
+ *
+ * @param tableName the table
+ * @return a list of column names
+ */
+ public List getColumns(String tableName) {
+ return getColumnsDefinitions(tableName).stream().map(m -> m.get("COLUMN_NAME")).collect(Collectors.toList());
+ }
+
+ public List