Простая реализация тегов с MongoDB

Простая реализация тегов с MongoDB

1. обзор

В этом руководстве мы рассмотрим простую реализацию тегов с использованием Java и MongoDB.

Для тех, кто не знаком с концепцией,a tag is a keyword used as a “label” to group documents into different categories. Это позволяет пользователям быстро перемещаться по аналогичному контенту и особенно полезно при работе с большим объемом данных.

При этом неудивительно, что этот метод очень часто используется в блогах. В этом сценарии каждый пост имеет один или несколько тегов в соответствии с темами. Когда пользователь заканчивает чтение, он может следовать одному из тегов, чтобы просмотреть больше контента, связанного с этой темой.

Давайте посмотрим, как мы можем реализовать этот сценарий.

2. зависимость

Чтобы запросить базу данных, нам нужно включить зависимость драйвера MongoDB в нашpom.xml:


    org.mongodb
    mongo-java-driver
    3.6.3

Текущая версия этой зависимости находится вhere.

3. Модель данных

Прежде всего, давайте начнем с планирования того, как должен выглядеть почтовый документ.

Для простоты наша модель данных будет иметь только заголовок, который мы также будем использовать в качестве идентификатора документа, автора и некоторых тегов.

Мы будем хранить теги внутри массива, так как в сообщении, вероятно, будет больше одного:

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

Мы также создадим соответствующий класс модели Java:

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

    // getters and setters
}

4. Обновление тегов

Теперь, когда мы настроили базу данных и вставили несколько примеров сообщений, давайте посмотрим, как мы можем их обновить.

Our repository class will include two methods to handle the addition and removal of tags, используя заголовок, чтобы найти их. Мы также вернем логическое значение, чтобы указать, обновил ли запрос элемент или нет:

public boolean addTags(String title, List 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 tags) {
    UpdateResult result = collection.updateOne(
      new BasicDBObject(DBCollection.ID_FIELD_NAME, title),
      Updates.pullAll(TAGS_FIELD, tags));
    return result.getModifiedCount() == 1;
}

Мы использовали методaddEachToSet вместоpush для добавления, чтобы, если теги уже есть, мы не добавляли их снова.

Также обратите внимание, что операторaddToSet тоже не будет работать, поскольку он добавит новые теги в виде вложенного массива, чего мы не хотим.

Another way we can perform our updates is through the Mongo shell. Например, давайте обновим сообщениеJUnit5 with Java.. В частности, мы хотим добавить тегиJava и JUnit5 и удалить тегиSpring иREST:

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. Запросы

И последнее, но не менее важное: давайте рассмотрим некоторые из наиболее частых запросов, которые могут быть интересны при работе с тегами. Для этого мы, в частности, воспользуемся преимуществами трех операторов массива:

  • $in – возвращает документы, в которыхa field contains any value указанного массива

  • $nin – возвращает документы, в которыхa field doesn’t contain any value указанного массива

  • $all – возвращает документы, в которыхa field contains all the values указанного массива

We’ll define three methods to query the posts in relation to a collection of tags passed as arguments. Они будут возвращать сообщения, которые соответствуют хотя бы одному тегу, всем тегам и ни одному из тегов. Мы также создадим метод сопоставления для обработки преобразования между документом и нашей моделью с помощью Java 8 Stream API:

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

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

public List postsWithoutTags(String... tags) {
    FindIterable 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) document.get(TAGS_FIELD));
    return post;
}

Опятьlet’s also take a look at the shell equivalent queries. Мы получим три разных коллекции сообщений, соответственно с тегамиMongoDB илиStream API, с тегамиJava 8 иJUnit 5 и без теговGroovy илиScala:

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. Заключение

В этой статье мы показали, как создать механизм тегирования. Конечно, мы можем использовать и перенастроить эту же методологию для других целей, кроме блога.

Если вас интересует дальнейшее изучение MongoDB,we encourage you to read this introductory article.

Как всегда, доступен весь код в примереover on the Github project.

Related