A Simple Tagging Implementation with MongoDB – 使用MongoDB的简单标签实现

最后修改: 2018年 3月 25日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll take a look at a simple tagging implementation using Java and MongoDB.

在本教程中,我们将看一下使用Java和MongoDB的简单标签实现。

For those unfamiliar with the concept, a tag is a keyword used as a “label” to group documents into different categories. This allows the users to quickly navigate through similar content and it’s especially useful when dealing with a big amount of data.

对于那些不熟悉这一概念的人来说,标签是一个用作 “标签 “的关键词,将文件归入不同的类别。这允许用户快速浏览类似的内容,在处理大量的数据时,它尤其有用。

That being said, it’s not surprising that this technique is very commonly used in blogs. In this scenario, each post has one or more tags according to the topics covered. When the user finishes reading, he can follow one of the tags to view more content related to that topic.

既然如此,这种技术在博客中非常普遍地使用也就不奇怪了。在这种情况下,每篇文章都有一个或多个标签,根据所涉及的主题。当用户完成阅读后,他可以关注其中一个标签,以查看与该主题相关的更多内容。

Let’s see how we can implement this scenario.

让我们看看如何实现这一方案。

2. Dependency

2.依赖性

In order to query the database, we’ll have to include the MongoDB driver dependency in our pom.xml:

为了查询数据库,我们必须在pom.xml中包含MongoDB驱动程序的依赖性。

<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongo-java-driver</artifactId>
    <version>3.6.3</version>
</dependency>

The current version of this dependency can be found here.

该依赖关系的当前版本可以在这里找到。

3. Data Model

3.数据模型

First of all, let’s start by planning out what a post document should look like.

首先,让我们先规划出一个职位文件应该是什么样子的。

To keep it simple, our data model will only have a title, which we’ll also use as the document id, an author, and some tags.

为了保持简单,我们的数据模型将只有一个标题,我们也将使用它作为文档的ID,一个作者,和一些标签。

We’ll store the tags inside an array since a post will probably have more than just one:

我们将在一个数组中存储标签,因为一个帖子可能不止有一个标签。

{
    "_id" : "Java 8 and MongoDB",
    "author" : "Donato Rimenti",
    "tags" : ["Java", "MongoDB", "Java 8", "Stream API"]
}

We’ll also create the corresponding Java model class:

我们还将创建相应的Java模型类。

public class Post {
    private String title;
    private String author;
    private List<String> tags;

    // getters and setters
}

4. Updating Tags

4.更新标签

Now that we have set up the database and inserted a couple of sample posts, let’s see how we can update them.

现在我们已经建立了数据库,并插入了几个样本帖子,让我们看看如何更新它们。

Our repository class will include two methods to handle the addition and removal of tags by using the title to find them. We’ll also return a boolean to indicate whether the query updated an element or not:

我们的版本库类将包括两个方法来处理标签的添加和删除,通过使用标题来找到它们。我们还将返回一个布尔值来表示查询是否更新了一个元素。

public boolean addTags(String title, List<String> tags) {
    UpdateResult result = collection.updateOne(
      new BasicDBObject(DBCollection.ID_FIELD_NAME, title), 
      Updates.addEachToSet(TAGS_FIELD, tags));
    return result.getModifiedCount() == 1;
}

public boolean removeTags(String title, List<String> tags) {
    UpdateResult result = collection.updateOne(
      new BasicDBObject(DBCollection.ID_FIELD_NAME, title), 
      Updates.pullAll(TAGS_FIELD, tags));
    return result.getModifiedCount() == 1;
}

We used the addEachToSet method instead of push for the addition so that if the tags are already there, we won’t add them again.

我们使用addEachToSet方法而不是push进行添加,这样如果标签已经在那里了,我们就不会再添加它们。

Notice also that the addToSet operator wouldn’t work either since it would add the new tags as a nested array which is not what we want.

请注意,addToSet操作符也不会起作用,因为它将把新标签作为一个嵌套的数组添加,而这不是我们想要的。

Another way we can perform our updates is through the Mongo shell. For instance, let’s update the post JUnit5 with Java. In particular, we want to add the tags Java and JUnit5 and remove the tags Spring and REST:

另一种我们可以执行更新的方式是通过Mongo shell。例如,让我们更新JUnit5 with Java.帖子。特别是,我们要添加标签Java和JUnit5,并删除标签SpringREST

db.posts.updateOne(
    { _id : "JUnit 5 with Java" }, 
    { $addToSet : 
        { "tags" : 
            { $each : ["Java", "JUnit5"] }
        }
});

db.posts.updateOne(
    {_id : "JUnit 5 with Java" },
    { $pull : 
        { "tags" : { $in : ["Spring", "REST"] }
    }
});

5. Queries

5 查询

Last but not least, let’s go through some of the most common queries we may be interested in while working with tags. For this purpose, we’ll take advantage of three array operators in particular:

最后但并非最不重要的是,让我们来看看在使用标签时我们可能感兴趣的一些最常见的查询。为此,我们将特别利用三个数组运算符。

  • $in – returns the documents where a field contains any value of the specified array
  • $nin – returns the documents where a field doesn’t contain any value of the specified array
  • $all – returns the documents where a field contains all the values of the specified array

We’ll define three methods to query the posts in relation to a collection of tags passed as arguments. They will return the posts which match at least one tag, all the tags and none of the tags. We’ll also create a mapping method to handle the conversion between a document and our model using Java 8’s Stream API:

我们将定义三个方法来查询与作为参数传递的标签集合有关的帖子。它们将返回至少与一个标签相匹配的帖子,所有的标签和没有标签的帖子。我们还将创建一个映射方法,使用Java 8的流API处理文档和我们的模型之间的转换。

public List<Post> postsWithAtLeastOneTag(String... tags) {
    FindIterable<Document> results = collection
      .find(Filters.in(TAGS_FIELD, tags));
    return StreamSupport.stream(results.spliterator(), false)
      .map(TagRepository::documentToPost)
      .collect(Collectors.toList());
}

public List<Post> postsWithAllTags(String... tags) {
    FindIterable<Document> results = collection
      .find(Filters.all(TAGS_FIELD, tags));
    return StreamSupport.stream(results.spliterator(), false)
      .map(TagRepository::documentToPost)
      .collect(Collectors.toList());
}

public List<Post> postsWithoutTags(String... tags) {
    FindIterable<Document> results = collection
      .find(Filters.nin(TAGS_FIELD, tags));
    return StreamSupport.stream(results.spliterator(), false)
      .map(TagRepository::documentToPost)
      .collect(Collectors.toList());
}

private static Post documentToPost(Document document) {
    Post post = new Post();
    post.setTitle(document.getString(DBCollection.ID_FIELD_NAME));
    post.setAuthor(document.getString("author"));
    post.setTags((List<String>) document.get(TAGS_FIELD));
    return post;
}

Again, let’s also take a look at the shell equivalent queries. We’ll fetch three different post collection respectively tagged with MongoDB or Stream API, tagged with both Java 8 and JUnit 5 and not tagged with Groovy nor Scala:

同样,我们也来看看外壳的等效查询。我们将获取三个不同的帖子集合,分别标记为MongoDBStream API,标记为Java 8JUnit 5,未标记为GroovyScala

db.posts.find({
    "tags" : { $in : ["MongoDB", "Stream API" ] } 
});

db.posts.find({
    "tags" : { $all : ["Java 8", "JUnit 5" ] } 
});

db.posts.find({
    "tags" : { $nin : ["Groovy", "Scala" ] } 
});

6. Conclusion

6.结论

In this article, we showed how to build a tagging mechanism. Of course, we can use and readapt this same methodology for other purposes apart from a blog.

在这篇文章中,我们展示了如何建立一个标签机制。当然,除了博客之外,我们还可以将这一方法用于其他目的,并重新调整。

If you are interested further in learning MongoDB, we encourage you to read this introductory article.

如果您对进一步学习MongoDB感兴趣,我们鼓励您阅读这篇介绍性文章

As always, all the code in the example is available over on the Github project.

一如既往,该示例中的所有代码都可以在Github项目中获得

« Previous

An Advanced Tagging Implementation with JPA

“/strong> Previous

An Advanced Tagging Implementation with JPA/a>