diff --git a/vipra-rest/src/main/java/de/vipra/rest/CacheAdapter.java b/vipra-rest/src/main/java/de/vipra/rest/CacheAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..2e48b3341aa72d46a73e325a631ad41244473f30
--- /dev/null
+++ b/vipra-rest/src/main/java/de/vipra/rest/CacheAdapter.java
@@ -0,0 +1,138 @@
+package de.vipra.rest;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.ehcache.Cache;
+import org.ehcache.CacheManager;
+import org.ehcache.config.CacheConfiguration;
+import org.ehcache.config.CacheConfigurationBuilder;
+import org.ehcache.config.CacheRuntimeConfiguration;
+import org.ehcache.exceptions.BulkCacheLoadingException;
+import org.ehcache.exceptions.BulkCacheWritingException;
+import org.ehcache.exceptions.CacheLoadingException;
+import org.ehcache.exceptions.CacheWritingException;
+
+import de.vipra.util.AbstractCache;
+
+public class CacheAdapter<T, U> implements AbstractCache<T, U> {
+
+	public static class NullCache<T, U> implements Cache<T, U> {
+
+		@Override
+		public Iterator<org.ehcache.Cache.Entry<T, U>> iterator() {
+			return null;
+		}
+
+		@Override
+		public U get(T key) throws CacheLoadingException {
+			return null;
+		}
+
+		@Override
+		public void put(T key, U value) throws CacheWritingException {}
+
+		@Override
+		public boolean containsKey(T key) {
+			return false;
+		}
+
+		@Override
+		public void remove(T key) throws CacheWritingException {}
+
+		@Override
+		public Map<T, U> getAll(Set<? extends T> keys) throws BulkCacheLoadingException {
+			return null;
+		}
+
+		@Override
+		public void putAll(Map<? extends T, ? extends U> entries) throws BulkCacheWritingException {}
+
+		@Override
+		public void removeAll(Set<? extends T> keys) throws BulkCacheWritingException {}
+
+		@Override
+		public void clear() {}
+
+		@Override
+		public U putIfAbsent(T key, U value) throws CacheLoadingException, CacheWritingException {
+			return null;
+		}
+
+		@Override
+		public boolean remove(T key, U value) throws CacheWritingException {
+			return false;
+		}
+
+		@Override
+		public U replace(T key, U value) throws CacheLoadingException, CacheWritingException {
+			return null;
+		}
+
+		@Override
+		public boolean replace(T key, U oldValue, U newValue) throws CacheLoadingException, CacheWritingException {
+			return false;
+		}
+
+		@Override
+		public CacheRuntimeConfiguration<T, U> getRuntimeConfiguration() {
+			return null;
+		}
+
+	}
+
+	public static final Logger log = LogManager.getLogger(CacheAdapter.class);
+
+	private final Cache<T, U> cache;
+
+	public CacheAdapter(Cache<T, U> cache) {
+		this.cache = cache;
+	}
+
+	public CacheAdapter(CacheManager manager, String name, Class<T> keyClass, Class<U> valueClass) {
+		Cache<T, U> cache = manager.getCache(name, keyClass, valueClass);
+		if (cache == null) {
+			CacheConfiguration<T, U> config = CacheConfigurationBuilder.newCacheConfigurationBuilder()
+					.buildConfig(keyClass, valueClass);
+			try {
+				cache = manager.createCache(name, config);
+			} catch (IllegalArgumentException e) {
+				cache = manager.getCache(name, keyClass, valueClass);
+			}
+		}
+		if (cache == null) {
+			log.error("could not create cache, not using cache");
+			cache = new NullCache<>();
+		}
+		this.cache = cache;
+	}
+
+	@Override
+	public U get(T t) {
+		return cache.get(t);
+	}
+
+	@Override
+	public void put(T t, U u) {
+		cache.put(t, u);
+	}
+
+	@Override
+	public void remove(T t) {
+		cache.remove(t);
+	}
+
+	@Override
+	public boolean contains(T t) {
+		return cache.containsKey(t);
+	}
+
+	@Override
+	public void clear() {
+		cache.clear();
+	}
+
+}
diff --git a/vipra-rest/src/main/java/de/vipra/rest/resource/ArticleResource.java b/vipra-rest/src/main/java/de/vipra/rest/resource/ArticleResource.java
index bbea795e308fc7401c946512250caa89a9635944..8a02e335dc073e977baa5fa51f141ce69352e93c 100644
--- a/vipra-rest/src/main/java/de/vipra/rest/resource/ArticleResource.java
+++ b/vipra-rest/src/main/java/de/vipra/rest/resource/ArticleResource.java
@@ -24,10 +24,9 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 
 import org.bson.types.ObjectId;
-import org.ehcache.Cache;
 import org.ehcache.CacheManager;
-import org.ehcache.config.CacheConfigurationBuilder;
 
+import de.vipra.rest.CacheAdapter;
 import de.vipra.rest.Messages;
 import de.vipra.rest.model.APIError;
 import de.vipra.rest.model.Wrapper;
@@ -45,7 +44,6 @@ public class ArticleResource {
 	@Context
 	UriInfo uri;
 
-	final Cache<String, ArticleFull> cache;
 	final DatabaseService<ArticleFull, ObjectId> dbArticles;
 
 	public ArticleResource(@Context ServletContext servletContext) throws ConfigException, IOException {
@@ -53,11 +51,9 @@ public class ArticleResource {
 		dbArticles = DatabaseService.getDatabaseService(config, ArticleFull.class);
 
 		CacheManager manager = (CacheManager) servletContext.getAttribute("cachemanager");
-		Cache<String, ArticleFull> articleCache = manager.getCache("articlecache", String.class, ArticleFull.class);
-		if (articleCache == null)
-			articleCache = manager.createCache("articlecache", CacheConfigurationBuilder.newCacheConfigurationBuilder()
-					.buildConfig(String.class, ArticleFull.class));
-		this.cache = articleCache;
+		CacheAdapter<ObjectId, ArticleFull> cache = new CacheAdapter<>(manager, "articlecacle", ObjectId.class,
+				ArticleFull.class);
+		dbArticles.withCache(cache);
 	}
 
 	@GET
@@ -83,6 +79,7 @@ public class ArticleResource {
 
 			return res.ok(articles);
 		} catch (Exception e) {
+			e.printStackTrace();
 			res.addError(new APIError(Response.Status.BAD_REQUEST, "Error", e.getMessage()));
 			return res.badRequest();
 		}
@@ -102,8 +99,9 @@ public class ArticleResource {
 
 		ArticleFull article;
 		try {
-			article = getSingle(id, StringUtils.getFields(fields));
+			article = dbArticles.getSingle(MongoUtils.objectId(id), StringUtils.getFields(fields));
 		} catch (Exception e) {
+			e.printStackTrace();
 			res.addError(new APIError(Response.Status.BAD_REQUEST, "Error", e.getMessage()));
 			return res.badRequest();
 		}
@@ -128,6 +126,7 @@ public class ArticleResource {
 			URI newUri = new URL(uri.getAbsolutePath().toURL(), article.getId().toString()).toURI();
 			return res.created(newUri);
 		} catch (DatabaseException | MalformedURLException | URISyntaxException e) {
+			e.printStackTrace();
 			res = new Wrapper<>(new APIError(Response.Status.INTERNAL_SERVER_ERROR, "item could not be created",
 					"item could not be created due to an internal server error"));
 			return res.serverError();
@@ -142,11 +141,11 @@ public class ArticleResource {
 		try {
 			deleted = dbArticles.deleteSingle(MongoUtils.objectId(id));
 		} catch (DatabaseException e) {
+			e.printStackTrace();
 			res = new Wrapper<>(new APIError(Response.Status.INTERNAL_SERVER_ERROR, "item could not be deleted",
 					"item could not be created due to an internal server error"));
 			return res.serverError();
 		}
-		cache.remove(id);
 		int del = deleted > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) deleted;
 		switch (del) {
 			case 0:
@@ -169,26 +168,13 @@ public class ArticleResource {
 		Wrapper<ArticleFull> res = new Wrapper<>();
 		try {
 			dbArticles.updateSingle(article);
-			cache.put(id, article);
 			return res.ok(article);
 		} catch (DatabaseException e) {
+			e.printStackTrace();
 			res = new Wrapper<>(new APIError(Response.Status.INTERNAL_SERVER_ERROR, "item could not be updated",
 					"item could not be updated due to an internal server error"));
 			return res.serverError();
 		}
 	}
 
-	private ArticleFull getSingle(String id, String[] fields) {
-		if (fields == null || fields.length == 0) {
-			ArticleFull article = cache.get(id);
-			if (article == null) {
-				article = dbArticles.getSingle(MongoUtils.objectId(id));
-				if (article != null)
-					cache.put(id, article);
-			}
-			return article;
-		} else
-			return dbArticles.getSingle(MongoUtils.objectId(id), fields);
-	}
-
 }
diff --git a/vipra-rest/src/main/java/de/vipra/rest/resource/SearchResource.java b/vipra-rest/src/main/java/de/vipra/rest/resource/SearchResource.java
index 49f27b9d0f090c9fe9bc7d7703b68e46cbb750d7..e034b7a9768e4e33219de6ad54360f9637619578 100644
--- a/vipra-rest/src/main/java/de/vipra/rest/resource/SearchResource.java
+++ b/vipra-rest/src/main/java/de/vipra/rest/resource/SearchResource.java
@@ -66,6 +66,7 @@ public class SearchResource {
 			response = client.prepareSearch("articles").setQuery(QueryBuilders.multiMatchQuery(query, "_all"))
 					.setFrom(skip).setSize(limit).execute().actionGet();
 		} catch (Exception e) {
+			e.printStackTrace();
 			res.addError(new APIError(Response.Status.BAD_REQUEST, "Error", e.getMessage()));
 			return res.badRequest();
 		}
diff --git a/vipra-rest/src/main/java/de/vipra/rest/resource/TopicResource.java b/vipra-rest/src/main/java/de/vipra/rest/resource/TopicResource.java
index f158154c670a87ff1bdc6154c405d39c8b1a3436..21bf8c2c19b48f9d85c26456ef682f58670451a0 100644
--- a/vipra-rest/src/main/java/de/vipra/rest/resource/TopicResource.java
+++ b/vipra-rest/src/main/java/de/vipra/rest/resource/TopicResource.java
@@ -20,10 +20,9 @@ import javax.ws.rs.core.UriInfo;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.bson.types.ObjectId;
-import org.ehcache.Cache;
 import org.ehcache.CacheManager;
-import org.ehcache.config.CacheConfigurationBuilder;
 
+import de.vipra.rest.CacheAdapter;
 import de.vipra.rest.Messages;
 import de.vipra.rest.model.APIError;
 import de.vipra.rest.model.Wrapper;
@@ -43,7 +42,6 @@ public class TopicResource {
 	@Context
 	UriInfo uri;
 
-	final Cache<String, TopicFull> cache;
 	final DatabaseService<TopicFull, ObjectId> dbTopics;
 	final DatabaseService<ArticleFull, ObjectId> dbArticles;
 
@@ -55,11 +53,9 @@ public class TopicResource {
 		dbArticles = DatabaseService.getDatabaseService(config, ArticleFull.class);
 
 		CacheManager manager = (CacheManager) servletContext.getAttribute("cachemanager");
-		Cache<String, TopicFull> topicCache = manager.getCache("topiccache", String.class, TopicFull.class);
-		if (topicCache == null)
-			topicCache = manager.createCache("topiccache", CacheConfigurationBuilder.newCacheConfigurationBuilder()
-					.buildConfig(String.class, TopicFull.class));
-		this.cache = topicCache;
+		CacheAdapter<ObjectId, TopicFull> cache = new CacheAdapter<>(manager, "topiccache", ObjectId.class,
+				TopicFull.class);
+		dbTopics.withCache(cache);
 	}
 
 	@GET
@@ -84,6 +80,7 @@ public class TopicResource {
 
 			return res.ok(topics);
 		} catch (Exception e) {
+			e.printStackTrace();
 			res.addError(new APIError(Response.Status.BAD_REQUEST, "Error", e.getMessage()));
 			return Response.status(Response.Status.BAD_REQUEST).entity(res).build();
 		}
@@ -104,8 +101,9 @@ public class TopicResource {
 
 		TopicFull topic;
 		try {
-			topic = getSingle(id, StringUtils.getFields(fields));
+			topic = dbTopics.getSingle(MongoUtils.objectId(id), StringUtils.getFields(fields));
 		} catch (Exception e) {
+			e.printStackTrace();
 			res.addError(new APIError(Response.Status.BAD_REQUEST, "Error", e.getMessage()));
 			return res.badRequest();
 		}
@@ -131,26 +129,13 @@ public class TopicResource {
 		Wrapper<TopicFull> res = new Wrapper<>();
 		try {
 			dbTopics.updateSingle(topic);
-			cache.put(id, topic);
 			return res.ok(topic);
 		} catch (DatabaseException e) {
+			e.printStackTrace();
 			res = new Wrapper<>(new APIError(Response.Status.INTERNAL_SERVER_ERROR, "item could not be updated",
 					"item could not be updated due to an internal server error"));
 			return res.serverError();
 		}
 	}
 
-	private TopicFull getSingle(String id, String[] fields) {
-		if (fields == null || fields.length == 0) {
-			TopicFull topic = cache.get(id);
-			if (topic == null) {
-				topic = dbTopics.getSingle(MongoUtils.objectId(id));
-				if (topic != null)
-					cache.put(id, topic);
-			}
-			return topic;
-		} else
-			return dbTopics.getSingle(MongoUtils.objectId(id), fields);
-	}
-
 }
diff --git a/vipra-rest/src/main/java/de/vipra/rest/resource/WordResource.java b/vipra-rest/src/main/java/de/vipra/rest/resource/WordResource.java
index c8cb5df41e92327400966cac2deab1f838545f2c..46777bc0e03696a765ffac3697755a1b626ceb01 100644
--- a/vipra-rest/src/main/java/de/vipra/rest/resource/WordResource.java
+++ b/vipra-rest/src/main/java/de/vipra/rest/resource/WordResource.java
@@ -17,10 +17,9 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 
 import org.bson.types.ObjectId;
-import org.ehcache.Cache;
 import org.ehcache.CacheManager;
-import org.ehcache.config.CacheConfigurationBuilder;
 
+import de.vipra.rest.CacheAdapter;
 import de.vipra.rest.Messages;
 import de.vipra.rest.model.APIError;
 import de.vipra.rest.model.Wrapper;
@@ -38,7 +37,6 @@ public class WordResource {
 	@Context
 	UriInfo uri;
 
-	final Cache<String, Word> cache;
 	final DatabaseService<Word, String> dbWords;
 	final DatabaseService<TopicFull, ObjectId> dbTopics;
 
@@ -48,11 +46,8 @@ public class WordResource {
 		dbTopics = DatabaseService.getDatabaseService(config, TopicFull.class);
 
 		CacheManager manager = (CacheManager) servletContext.getAttribute("cachemanager");
-		Cache<String, Word> wordCache = manager.getCache("wordcache", String.class, Word.class);
-		if (wordCache == null)
-			wordCache = manager.createCache("wordcache",
-					CacheConfigurationBuilder.newCacheConfigurationBuilder().buildConfig(String.class, Word.class));
-		this.cache = wordCache;
+		CacheAdapter<String, Word> cache = new CacheAdapter<>(manager, "wordcache", String.class, Word.class);
+		dbWords.withCache(cache);
 	}
 
 	@GET
@@ -77,6 +72,7 @@ public class WordResource {
 
 			return res.ok(words);
 		} catch (Exception e) {
+			e.printStackTrace();
 			res.addError(new APIError(Response.Status.BAD_REQUEST, "Error", e.getMessage()));
 			return res.badRequest();
 		}
@@ -96,8 +92,9 @@ public class WordResource {
 
 		Word word;
 		try {
-			word = getSingle(id, StringUtils.getFields(fields));
+			word = dbWords.getSingle(id, StringUtils.getFields(fields));
 		} catch (Exception e) {
+			e.printStackTrace();
 			res.addError(new APIError(Response.Status.BAD_REQUEST, "Error", e.getMessage()));
 			return res.badRequest();
 		}
@@ -115,17 +112,4 @@ public class WordResource {
 		}
 	}
 
-	private Word getSingle(String id, String[] fields) {
-		if (fields == null || fields.length == 0) {
-			Word word = cache.get(id);
-			if (word == null) {
-				word = dbWords.getSingle(id);
-				if (word != null)
-					cache.put(id, word);
-			}
-			return word;
-		} else
-			return dbWords.getSingle(id, fields);
-	}
-
 }
diff --git a/vipra-ui/app/html/articles/index.html b/vipra-ui/app/html/articles/index.html
index cece9c1853b073d5c5a442c3fa5296b27fa989f8..cbb03f8f0a6eb01fd19bd9aaaf3e8c3ef9a94034 100644
--- a/vipra-ui/app/html/articles/index.html
+++ b/vipra-ui/app/html/articles/index.html
@@ -2,10 +2,10 @@
   Found <span ng-bind="articlesMeta.total"></span> articles in the database <query-time/>.
 </p>
 
-<ul class="list-unstyled">
+<ul class="dashed">
   <li ng-repeat="article in articles">
     <a ui-sref="articles.show({id: article.id})" ng-bind="::article.title"></a>
   </li>
 </ul>
 
-<pagination total="articlesMeta.total" page="page" limit="limit" change="change"/>
\ No newline at end of file
+<pagination total="articlesMeta.total" page="page" limit="limit" change="changePage"/>
\ No newline at end of file
diff --git a/vipra-ui/app/html/index.html b/vipra-ui/app/html/index.html
index 336e7d2f348a602b8c83c30c000aec72297eac34..7fb2f6a650f8505075b622fddc90a3816c046f6f 100644
--- a/vipra-ui/app/html/index.html
+++ b/vipra-ui/app/html/index.html
@@ -43,7 +43,7 @@
     <div class="text-center" ng-show="searching">
       Searching...
     </div>
-    <div class="col-md-12" ng-show="!searching && search && searchResults.length == 0">
+    <div class="col-md-12" ng-show="!searching && search && (!searchResults || searchResults.length == 0)">
       <h4>No Results <query-time/></h4>
     </div>
     <div class="col-md-12" ng-show="searchResults.length > 0">
diff --git a/vipra-ui/app/html/modal.html b/vipra-ui/app/html/modal.html
index 12a50c29e2c0b08c95d79201e6e748ba2424dd4e..e441d0bf66c3142a7db352366f576f175590b6bf 100644
--- a/vipra-ui/app/html/modal.html
+++ b/vipra-ui/app/html/modal.html
@@ -1,14 +1,10 @@
 <div class="modal-header">
     <h3 class="modal-title">I'm a modal!</h3>
 </div>
+
 <div class="modal-body">
-    <ul>
-        <li ng-repeat="item in items">
-            <a href="#" ng-click="$event.preventDefault(); selected.item = item">{{ item }}</a>
-        </li>
-    </ul>
-    Selected: <b>{{ selected.item }}</b>
 </div>
+
 <div class="modal-footer">
     <button class="btn btn-primary" type="button" ng-click="ok()">OK</button>
     <button class="btn btn-warning" type="button" ng-click="cancel()">Cancel</button>
diff --git a/vipra-ui/app/html/topics/index.html b/vipra-ui/app/html/topics/index.html
index b9b9cf85b040643802770ac17f8c657777e643de..fa32725749cf67c7a1b940a670e1e54363d7d421 100644
--- a/vipra-ui/app/html/topics/index.html
+++ b/vipra-ui/app/html/topics/index.html
@@ -2,10 +2,10 @@
   Found <span ng-bind="topicsMeta.total"></span> topics in the database <query-time/>.
 </p>
 
-<ul class="list-unstyled">
+<ul class="dashed">
   <li ng-repeat="topic in topics">
     <a ui-sref="topics.show({id: topic.id})">{{topic.name}}</a>
   </li>
 </ul>
 
-<pagination total="topicsMeta.total" page="page" limit="limit" change="change"/>
\ No newline at end of file
+<pagination total="topicsMeta.total" page="page" limit="limit" change="changePage"/>
\ No newline at end of file
diff --git a/vipra-ui/app/html/topics/show.html b/vipra-ui/app/html/topics/show.html
index a8923889203414a81279a0669695e9db7c83d055..566856a5f93a3ecdc9b2356dfc324763d94270be 100644
--- a/vipra-ui/app/html/topics/show.html
+++ b/vipra-ui/app/html/topics/show.html
@@ -29,7 +29,7 @@
 
 <h3>Articles <hide-link target="#articles"/></h3>
 
-<ul class="list-unstyled" id="articles">
+<ul class="dashed">
   <li ng-repeat="article in ::topic.articles">
     <article-link article="article"/>
   </li>
diff --git a/vipra-ui/app/html/words/index.html b/vipra-ui/app/html/words/index.html
index 90832f5d8e7446f1eacd1103714260208e7fce87..631b2dc2c7ebea7d5a6cda3b4e076d768126113d 100644
--- a/vipra-ui/app/html/words/index.html
+++ b/vipra-ui/app/html/words/index.html
@@ -26,4 +26,4 @@
   </div>
 </div>
 
-<pagination total="wordsMeta.total" page="page" limit="limit" change="change"/>
\ No newline at end of file
+<pagination total="wordsMeta.total" page="page" limit="limit" change="changePage"/>
\ No newline at end of file
diff --git a/vipra-ui/app/index.html b/vipra-ui/app/index.html
index 7b7f84988ffe4e9068f3dc85caafe79d6dbe4015..fe6a3c37912a5303abd7a5866aee5118d0fb2787 100644
--- a/vipra-ui/app/index.html
+++ b/vipra-ui/app/index.html
@@ -59,6 +59,8 @@
       </div><!-- /.container-fluid -->
     </nav>
 
+    <div ui-view="subNavBar"></div>
+
     <div class="container" ui-view></div>
 
     <footer class="footer navbar-default">
diff --git a/vipra-ui/app/js/controllers.js b/vipra-ui/app/js/controllers.js
index 18dad55a42fcf73e50ae80215609e232f3b95a1e..a12294d9e9bed738a43e859eea7c71d35e533852 100644
--- a/vipra-ui/app/js/controllers.js
+++ b/vipra-ui/app/js/controllers.js
@@ -73,16 +73,18 @@
    * Article Controllers
    */
 
-  app.controller('ArticlesIndexController', ['$scope', '$stateParams', 'ArticleFactory',
-    function($scope, $stateParams, ArticleFactory, testService) {
+  app.controller('ArticlesIndexController', ['$scope', '$state', '$stateParams', 'ArticleFactory', 'Store',
+    function($scope, $state, $stateParams, ArticleFactory, Store) {
 
     $scope.page = Math.max($stateParams.page || 1, 1);
     $scope.limit = pageSize;
+    $scope.sort = Store('sortarticles-' + $state.current.name) || 'date';
 
-    $scope.change = function(page) {
+    $scope.reload = function() {
       ArticleFactory.query({
-        skip: (page-1)*$scope.limit,
-        limit: $scope.limit
+        skip: ($scope.page-1)*$scope.limit,
+        limit: $scope.limit,
+        sort: $scope.sort
       }, function(response) {
         $scope.articles = response.data;
         $scope.articlesMeta = response.meta;
@@ -90,7 +92,12 @@
       });
     };
 
-    $scope.change($scope.page);
+    $scope.changePage = function(page) {
+      $scope.page = page;
+      $scope.reload();
+    };
+
+    $scope.reload();
 
   }]);
 
@@ -132,6 +139,7 @@
       };
 
       $scope.topicShare = topicShare;
+
     });
 
   }]);
@@ -145,10 +153,11 @@
 
     $scope.page = Math.max($stateParams.page || 1, 1);
     $scope.limit = pageSize;
+    $scope.sort = Store('sorttopics-' + $state.current.name) || 'name';
 
-    $scope.change = function(page) {
+    $scope.reload = function() {
       TopicFactory.query({
-        skip: (page-1)*$scope.limit,
+        skip: ($scope.page-1)*$scope.limit,
         limit: $scope.limit
       }, function(response) {
         $scope.topics = response.data;
@@ -157,7 +166,12 @@
       });
     };
 
-    $scope.change($scope.page);
+    $scope.changePage = function(page) {
+      $scope.page = page;
+      $scope.reload();
+    };
+
+    $scope.reload();
 
   }]);
 
@@ -178,16 +192,18 @@
    * Word Controllers
    */
 
-  app.controller('WordsIndexController', ['$scope', '$stateParams', 'WordFactory',
-    function($scope, $stateParams, WordFactory) {
+  app.controller('WordsIndexController', ['$scope', '$state', '$stateParams', 'WordFactory',
+    function($scope, $state, $stateParams, WordFactory) {
 
     $scope.page = Math.max($stateParams.page || 1, 1);
     $scope.limit = 300;
+    $scope.sort = Store('sortwords-' + $state.current.name) || 'word';
 
-    $scope.change = function(page) {
+    $scope.reload = function() {
       WordFactory.query({
-        skip: (page-1)*$scope.limit,
-        limit: $scope.limit
+        skip: ($scope.page-1)*$scope.limit,
+        limit: $scope.limit,
+        sort: $scope.sort
       }, function(response) {
         $scope.words = response.data;
         $scope.wordsMeta = response.meta;
@@ -195,7 +211,12 @@
       });
     };
 
-    $scope.change($scope.page);
+    $scope.changePage = function(page) {
+      $scope.page = page;
+      $scope.reload();
+    };
+
+    $scope.reload();
 
   }]);
 
@@ -215,7 +236,7 @@
    * Directive Controllers
    */
 
-   app.controller('PaginationController', ['$scope',
+  app.controller('PaginationController', ['$scope',
     function($scope) {
 
       if(!$scope.page)
@@ -246,6 +267,7 @@
       $scope.changePage = function(page) {
         $scope.page = page;
         change(page);
+        window.scrollTo(0,0);
       };
    }]);
 
diff --git a/vipra-ui/app/js/directives.js b/vipra-ui/app/js/directives.js
index 4b0c64c862b840683e9ba829ff3f78f6719ea5c1..4335de7bdf1eac433ef4d37ae9a37f1d5316c8f7 100644
--- a/vipra-ui/app/js/directives.js
+++ b/vipra-ui/app/js/directives.js
@@ -57,7 +57,7 @@
     };
   });
 
-  app.directive('visGraph', ['$state', '$timeout', 'ArticleFactory', 'TopicFactory',
+  app.directive('visNetwork', ['$state', '$timeout', 'ArticleFactory', 'TopicFactory',
     function($state, $timeout, ArticleFactory, TopicFactory) {
 
     return {
diff --git a/vipra-ui/app/less/app.less b/vipra-ui/app/less/app.less
index 0919aec6625c909b61eb6df05b7055b5de5c6ba6..348c59eef3a204ec6399e7fef0c19f1b933b5254 100644
--- a/vipra-ui/app/less/app.less
+++ b/vipra-ui/app/less/app.less
@@ -13,6 +13,16 @@ a:hover {
   cursor:pointer;
 }
 
+ul.dashed {
+  list-style: none;
+  padding: 0;
+
+  li:before {
+    content: "–";
+    padding-right: 5px;
+  }
+}
+
 .heading {
   .noselect;
   background: transparent url(/img/logo.svg) no-repeat 50% 50%;
@@ -25,7 +35,9 @@ a:hover {
   padding: 15px;
 
   .search-result {
-    margin-bottom: 20px;
+    &:not(:last-child) {
+      margin-bottom: 20px;
+    }
 
     a {
       font-size: 1.5rem;
diff --git a/vipra-util/src/main/java/de/vipra/util/AbstractCache.java b/vipra-util/src/main/java/de/vipra/util/AbstractCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..f80100acf8276c3fe27ac71e27ba1ec995c4aeb1
--- /dev/null
+++ b/vipra-util/src/main/java/de/vipra/util/AbstractCache.java
@@ -0,0 +1,15 @@
+package de.vipra.util;
+
+public interface AbstractCache<T, U> {
+
+	U get(T t);
+
+	void put(T t, U u);
+
+	void remove(T t);
+	
+	boolean contains(T t);
+	
+	void clear();
+
+}
diff --git a/vipra-util/src/main/java/de/vipra/util/service/DatabaseService.java b/vipra-util/src/main/java/de/vipra/util/service/DatabaseService.java
index 692dc93efe156cde80e707079e73bf05a4614aab..fdac1542edca05c723d234099cebec77ecce2dc8 100644
--- a/vipra-util/src/main/java/de/vipra/util/service/DatabaseService.java
+++ b/vipra-util/src/main/java/de/vipra/util/service/DatabaseService.java
@@ -10,6 +10,7 @@ import java.util.Set;
 import org.mongodb.morphia.Datastore;
 import org.mongodb.morphia.query.Query;
 
+import de.vipra.util.AbstractCache;
 import de.vipra.util.Config;
 import de.vipra.util.ListUtils;
 import de.vipra.util.Mongo;
@@ -25,6 +26,7 @@ public class DatabaseService<Type extends Model<IdType>, IdType> implements Serv
 	private final Class<Type> clazz;
 	private final String[] ignoredFieldsSingle;
 	private final String[] ignoredFieldsMulti;
+	private AbstractCache<IdType, Type> cache;
 
 	public DatabaseService(Mongo mongo, Class<Type> clazz) {
 		this.datastore = mongo.getDatastore();
@@ -48,12 +50,20 @@ public class DatabaseService<Type extends Model<IdType>, IdType> implements Serv
 
 	@Override
 	public Type getSingle(IdType id, String... fields) {
-		Query<Type> q = datastore.createQuery(clazz).field("_id").equal(id);
-		if (fields != null && fields.length > 0)
-			q.retrievedFields(true, setMinus(fields, ignoredFieldsSingle));
-		else if (ignoredFieldsSingle.length > 0)
-			q.retrievedFields(false, ignoredFieldsSingle);
-		return q.get();
+		Type t = null;
+		if (cache == null || (fields != null && fields.length > 0) || !cache.contains(id)) {
+			Query<Type> q = datastore.createQuery(clazz).field("_id").equal(id);
+			if (fields != null && fields.length > 0)
+				q.retrievedFields(true, setMinus(fields, ignoredFieldsSingle));
+			else if (ignoredFieldsSingle.length > 0)
+				q.retrievedFields(false, ignoredFieldsSingle);
+			t = q.get();
+			if (cache != null && (fields == null || fields.length == 0))
+				cache.put(id, t);
+		} else {
+			t = cache.get(id);
+		}
+		return t;
 	}
 
 	@Override
@@ -113,6 +123,8 @@ public class DatabaseService<Type extends Model<IdType>, IdType> implements Serv
 	@Override
 	public Type createSingle(Type t) throws DatabaseException {
 		datastore.save(t);
+		if (cache != null)
+			cache.put(t.getId(), t);
 		return t;
 	}
 
@@ -125,17 +137,24 @@ public class DatabaseService<Type extends Model<IdType>, IdType> implements Serv
 
 	@Override
 	public long deleteSingle(IdType id) throws DatabaseException {
-		return datastore.delete(id).getN();
+		int deleted = datastore.delete(id).getN();
+		if (cache != null)
+			cache.remove(id);
+		return deleted;
 	}
 
 	@Override
 	public void updateSingle(Type t) throws DatabaseException {
 		datastore.save(t);
+		if (cache != null)
+			cache.put(t.getId(), t);
 	}
 
 	@Override
 	public void drop() {
 		datastore.getCollection(clazz).drop();
+		if (cache != null)
+			cache.clear();
 	}
 
 	@Override
@@ -143,6 +162,11 @@ public class DatabaseService<Type extends Model<IdType>, IdType> implements Serv
 		return datastore.getCount(clazz);
 	}
 
+	@Override
+	public void withCache(AbstractCache<IdType, Type> cache) {
+		this.cache = cache;
+	}
+
 	public static <Type extends Model<IdType>, IdType> DatabaseService<Type, IdType> getDatabaseService(Config config,
 			Class<Type> clazz) throws ConfigException {
 		Mongo mongo = config.getMongo();
diff --git a/vipra-util/src/main/java/de/vipra/util/service/Service.java b/vipra-util/src/main/java/de/vipra/util/service/Service.java
index 1062f540967f23e14bb14c17d596faf4e705b85c..16b40b15cc119cac7ffb9a2197c1638665492bfa 100644
--- a/vipra-util/src/main/java/de/vipra/util/service/Service.java
+++ b/vipra-util/src/main/java/de/vipra/util/service/Service.java
@@ -3,6 +3,7 @@ package de.vipra.util.service;
 import java.util.ArrayList;
 import java.util.List;
 
+import de.vipra.util.AbstractCache;
 import de.vipra.util.Pair;
 import de.vipra.util.model.Model;
 
@@ -34,6 +35,8 @@ public interface Service<Type extends Model<IdType>, IdType, E extends Exception
 
 	long count() throws E;
 
+	void withCache(AbstractCache<IdType, Type> cache);
+
 	public static class QueryBuilder {
 
 		private Integer skip;
@@ -51,22 +54,27 @@ public interface Service<Type extends Model<IdType>, IdType, E extends Exception
 		}
 
 		public QueryBuilder skip(Integer skip) {
-			this.skip = skip;
+			if (skip == null || skip >= 0)
+				this.skip = skip;
 			return this;
 		}
 
 		public QueryBuilder limit(Integer limit) {
-			this.limit = limit;
+			if (limit == null || limit >= 0)
+				this.limit = limit;
 			return this;
 		}
 
 		public QueryBuilder sortBy(String sortBy) {
-			this.sortBy = sortBy;
+			if (sortBy == null || !sortBy.isEmpty())
+				this.sortBy = sortBy;
 			return this;
 		}
 
 		public QueryBuilder criteria(String field, Object value) {
-			return criteria(Pair.pair(field, value));
+			if (field != null && value != null && !field.isEmpty())
+				criteria(Pair.pair(field, value));
+			return this;
 		}
 
 		public QueryBuilder criteria(Pair<String, Object> pair) {