Den här artikeln är en del av en serie:• En enkel taggningsimplementering med Elasticsearch
• En enkel märkningsimplementering med JPA
• En avancerad taggningsimplementering med JPA
• En enkel taggningsimplementering med MongoDB (nuvarande artikel)
1. Översikt
I den här handledningen tar vi en titt på en enkel taggningsimplementering med Java och MongoDB.
För de som inte känner till konceptet är en tagg ett nyckelord som används som en "etikett" för att gruppera dokument i olika kategorier. Detta gör att användarna snabbt kan navigera genom liknande innehåll och det är särskilt användbart när de hanterar en stor mängd data.
Med detta sagt är det inte förvånande att denna teknik är mycket vanligt förekommande i bloggar. I det här scenariot har varje inlägg en eller flera taggar beroende på de ämnen som tas upp. När användaren har läst klart kan han följa en av taggarna för att se mer innehåll relaterat till det ämnet.
Låt oss se hur vi kan implementera detta scenario.
2. Beroende
För att fråga i databasen måste vi inkludera MongoDB-drivrutinsberoendet i vår pom.xml :
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.6.3</version>
</dependency>
Den aktuella versionen av detta beroende finns här.
3. Datamodell
Först och främst, låt oss börja med att planera hur ett postdokument ska se ut.
För att göra det enkelt kommer vår datamodell bara att ha en titel, som vi också kommer att använda som dokument-id, en författare och några taggar.
Vi lagrar taggarna i en array eftersom ett inlägg förmodligen kommer att ha mer än bara en:
{
"_id" : "Java 8 and MongoDB",
"author" : "Donato Rimenti",
"tags" : ["Java", "MongoDB", "Java 8", "Stream API"]
}
Vi kommer också att skapa motsvarande Java-modellklass:
public class Post {
private String title;
private String author;
private List<String> tags;
// getters and setters
}
4. Uppdatera taggar
Nu när vi har ställt in databasen och infogat ett par exempelinlägg, låt oss se hur vi kan uppdatera dem.
Vår förvarsklass kommer att innehålla två metoder för att hantera tillägg och borttagning av taggar genom att använda titeln för att hitta dem. Vi returnerar också en boolean för att indikera om frågan uppdaterade ett element eller inte:
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;
}
Vi använde addEachToSet metod istället för push för tillägget så att om taggarna redan finns där kommer vi inte att lägga till dem igen.
Lägg också märke till att addToSet operatorn skulle inte fungera heller eftersom den skulle lägga till de nya taggarna som en kapslad array vilket inte är vad vi vill ha.
Ett annat sätt vi kan utföra våra uppdateringar är genom Mongo-skalet. Låt oss till exempel uppdatera inlägget JUnit5 med Java. I synnerhet vill vi lägga till taggarna Java och JEnhet5 och ta bort taggarna Vår och VILA :
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. Frågor
Sist men inte minst, låt oss gå igenom några av de vanligaste frågorna vi kan vara intresserade av när vi arbetar med taggar. För detta ändamål kommer vi att dra fördel av tre arrayoperatorer i synnerhet:
- $in – returnerar dokumenten där ett fält innehåller något värde av den angivna arrayen
- $nin – returnerar de dokument där ett fält inte innehåller något värde av den angivna arrayen
- $all – returnerar dokumenten där ett fält innehåller alla värden av den angivna arrayen
Vi kommer att definiera tre metoder för att fråga inläggen i relation till en samling taggar som skickas som argument . De kommer att returnera de inlägg som matchar minst en tagg, alla taggarna och ingen av taggarna. Vi kommer också att skapa en mappningsmetod för att hantera konverteringen mellan ett dokument och vår modell med Java 8:s Stream 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;
}
Återigen, låt oss också ta en titt på de skalliknande frågorna . Vi kommer att hämta tre olika inläggssamlingar taggade med MongoDB eller Stream API, taggat med både Java 8 och JUnit 5 och inte taggad med Groovy inte heller 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" ] }
});