杰瑞科技汇

MongoDB Java如何操作数组?

我们将使用 MongoDB Java Driver (版本 4.x 及以上),这是目前官方推荐的方式。

准备工作

确保你的项目中已经添加了 MongoDB Java Driver 的依赖。

Maven (pom.xml):

<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongodb-driver-sync</artifactId>
    <version>4.11.1</version> <!-- 请使用最新版本 -->
</dependency>

Gradle (build.gradle):

implementation 'org.mongodb:mongodb-driver-sync:4.11.1' // 请使用最新版本

数据模型

假设我们有一个 users 集合,每个用户文档包含一个 tags 数组字段,用于存储用户的兴趣标签。

// users 集合中的示例文档
{
  "_id": ObjectId("63f8b8e1e4b0f3a1b2c3d4e5"),
  "name": "Alice",
  "age": 30,
  "tags": ["java", "mongodb", "programming"]
}
{
  "_id": ObjectId("63f8b8f2e4b0f3a1b2c3d4e6"),
  "name": "Bob",
  "age": 25,
  "tags": ["python", "data-science", "mongodb"]
}
{
  "_id": ObjectId("63f8b900e4b0f3a1b2c3d4e7"),
  "name": "Charlie",
  "age": 35,
  "tags": ["java", "spring-boot", "react"]
}

在 Java 中,我们可以用一个简单的 POJO (Plain Old Java Object) 来表示这个文档结构。

import org.bson.types.ObjectId;
public class User {
    private ObjectId id;
    private String name;
    private int age;
    private List<String> tags; // 关键:用 List 表示数组
    // Getters and Setters
    public ObjectId getId() { return id; }
    public void setId(ObjectId id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
    public List<String> getTags() { return tags; }
    public void setTags(List<String> tags) { this.tags = tags; }
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", tags=" + tags +
                '}';
    }
}

连接到 MongoDB

在执行操作前,我们需要一个 MongoClient 来连接数据库。

import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.MongoCollection;
public class MongoConnection {
    public static MongoCollection<User> getUserCollection() {
        // 替换为你的 MongoDB 连接字符串
        String uri = "mongodb://localhost:27017";
        try (MongoClient mongoClient = MongoClients.create(uri)) {
            MongoDatabase database = mongoClient.getDatabase("testdb"); // 数据库名
            // 如果需要,可以配置 CodecRegistry 来自动处理 POJO
            // 这里为了简单,我们后续会手动处理 Bson 转换
            return database.getCollection("users", User.class);
        }
    }
}

注意: MongoClients.create(uri) 返回一个实现了 AutoCloseableMongoClient,将它放在 try-with-resources 块中是一个好习惯,可以确保连接被正确关闭。

插入包含数组的文档

插入数据非常直接,因为 Java Driver 会自动将 List 映射为 MongoDB 的数组。

import com.mongodb.client.MongoCollection;
import java.util.Arrays;
import java.util.List;
public class InsertExample {
    public static void main(String[] args) {
        MongoCollection<User> collection = MongoConnection.getUserCollection();
        User user1 = new User();
        user1.setName("Alice");
        user1.setAge(30);
        user1.setTags(Arrays.asList("java", "mongodb", "programming"));
        User user2 = new User();
        user2.setName("Bob");
        user2.setAge(25);
        user2.setTags(Arrays.asList("python", "data-science", "mongodb"));
        // 插入单个文档
        collection.insertOne(user1);
        System.out.println("Inserted Alice");
        // 插入多个文档
        collection.insertMany(Arrays.asList(user1, user2)); // 注意:实际中可能重复插入,这里仅为示例
        System.out.println("Inserted multiple users");
    }
}

查询数组

查询是操作数组最核心的部分,MongoDB 提供了非常丰富的操作符来查询数组。

1 精确匹配整个数组

使用 Filters.all() 来查找数组字段包含所有指定元素的文档。

import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.Filters;
import org.bson.Document;
import java.util.Arrays;
public class QueryExactArray {
    public static void main(String[] args) {
        MongoCollection<User> collection = MongoConnection.getUserCollection();
        // 查找 tags 数组同时包含 "java" 和 "mongodb" 的用户
        // 注意:顺序不重要,但元素必须全部存在
        collection.find(Filters.all("tags", "java", "mongodb"))
                  .forEach(doc -> System.out.println(doc));
    }
}

2 匹配数组中的任意一个元素

使用 Filters.in() 来查找数组字段包含至少一个指定元素的文档。

import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.Filters;
public class QueryAnyElement {
    public static void main(String[] args) {
        MongoCollection<User> collection = MongoConnection.getUserCollection();
        // 查找 tags 数组中包含 "mongodb" 的所有用户
        collection.find(Filters.in("tags", "mongodb"))
                  .forEach(doc -> System.out.println(doc));
    }
}

3 查询数组长度

使用 Filters.size() 来查找数组长度为指定值的文档。

import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.Filters;
public class QueryArraySize {
    public static void main(String[] args) {
        MongoCollection<User> collection = MongoConnection.getUserCollection();
        // 查找 tags 数组长度恰好为 3 的用户
        collection.find(Filters.size("tags", 3))
                  .forEach(doc -> System.out.println(doc));
    }
}

4 查询数组元素的位置

  • Filters.elemMatch(): 当数组元素是文档时非常有用,可以匹配文档内嵌的特定条件。
  • Filters.where(): 使用 JavaScript 表达式进行更复杂的查询。

更新数组

更新数组同样有多种方式,使用 Updates 类来构建更新操作。

1 向数组末尾添加元素

使用 Updates.push()

import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.Updates;
import org.bson.Document;
public class UpdatePush {
    public static void main(String[] args) {
        MongoCollection<User> collection = MongoConnection.getUserCollection();
        // 给 Alice 的 tags 数组末尾添加一个新标签 "docker"
        collection.updateOne(
            Filters.eq("name", "Alice"),
            Updates.push("tags", "docker")
        );
        // 查看更新结果
        collection.find(Filters.eq("name", "Alice")).forEach(doc -> System.out.println(doc));
    }
}

更新后,Alice 的 tags 将变为 ["java", "mongodb", "programming", "docker"]

2 向数组开头添加元素

使用 Updates.pushFirst()

// 给 Alice 的 tags 数组开头添加一个新标签 "kubernetes"
collection.updateOne(
    Filters.eq("name", "Alice"),
    Updates.pushFirst("tags", "kubernetes")
);

更新后,Alice 的 tags 将变为 ["kubernetes", "java", "mongodb", "programming", "docker"]

3 添加元素(如果不存在)

使用 Updates.addToSet(),如果元素已存在,则不会重复添加。

// 尝试给 Alice 的 tags 数组添加 "java",如果已存在则忽略
collection.updateOne(
    Filters.eq("name", "Alice"),
    Updates.addToSet("tags", "java")
);

4 删除数组元素

  • 删除所有匹配的元素: Updates.pull("field", value)
  • 删除第一个匹配的元素: Updates.pullFirst("field", value)
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Updates;
public class UpdatePull {
    public static void main(String[] args) {
        MongoCollection<User> collection = MongoConnection.getUserCollection();
        // 删除 Alice 的 tags 数组中所有的 "mongodb" 标签
        collection.updateOne(
            Filters.eq("name", "Alice"),
            Updates.pull("tags", "mongodb")
        );
        // 查看结果
        collection.find(Filters.eq("name", "Alice")).forEach(doc -> System.out.println(doc));
    }
}

5 按位置更新数组元素

使用 Updates.set() 配合数组索引(从 0 开始)。

// 将 Alice 的 tags 数组中索引为 1 的元素(第二个)更新为 "golang"
collection.updateOne(
    Filters.eq("name", "Alice"),
    Updates.set("tags.1", "golang")
);

高级用法:数组作为嵌入式文档

数组中的元素不一定是简单的字符串或数字,也可以是复杂的文档对象。

1 数据模型和插入

假设我们想记录用户的地址历史。

import java.util.List;
public class UserWithAddress {
    private ObjectId id;
    private String name;
    private List<Address> addresses; // 地址列表
    // ... getters and setters
}
class Address {
    private String street;
    private String city;
    private int zipCode;
    // ... getters and setters
}
// 插入示例
UserWithAddress user = new UserWithAddress();
user.setName("David");
user.setAddresses(Arrays.asList(
    new Address("123 Main St", "New York", 10001),
    new Address("456 Oak Ave", "Boston", 02108)
));
collection.insertOne(user);

2 查询嵌入式文档数组

这是 Filters.elemMatch() 大显身手的地方,我们想查找住在 "New York" 的用户。

import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Projections;
// 查找 addresses 数组中至少有一个元素的 city 是 "New York" 的用户
collection.find(Filters.elemMatch("addresses", 
                  Filters.eq("city", "New York")))
          .forEach(doc -> System.out.println(doc));
// 如果只想返回匹配的地址,而不是整个用户文档
collection.find(Filters.elemMatch("addresses", Filters.eq("city", "New York")))
          .projection(Projections.include("name", "addresses.$")) // .$ 返回第一个匹配的嵌入式文档
          .forEach(doc -> System.out.println(doc));
操作 Java 方法/类 MongoDB Shell (mongosh) 等价示例
插入 collection.insertOne(user) db.users.insertOne({...})
精确匹配数组 Filters.all("tags", "a", "b") db.users.find({tags: {$all: ["a", "b"]}})
匹配任意元素 Filters.in("tags", "a") db.users.find({tags: {$in: ["a"]}})
匹配数组长度 Filters.size("tags", 3) db.users.find({tags: {$size: 3}})
向数组末尾添加 Updates.push("tags", "c") db.users.updateOne({}, {$push: {tags: "c"}})
添加不重复元素 Updates.addToSet("tags", "a") db.users.updateOne({}, {$addToSet: {tags: "a"}})
删除所有匹配元素 Updates.pull("tags", "a") db.users.updateOne({}, {$pull: {tags: "a"}})
按索引更新 Updates.set("tags.1", "new") db.users.updateOne({}, {$set: {"tags.1": "new"}})
查询嵌入式文档 Filters.elemMatch("arr", Filters.eq("k", "v")) db.users.find({"arr": {$elemMatch: {k: "v}}})

掌握这些基本操作后,你就可以在 Java 应用中灵活地处理 MongoDB 的数组数据了,核心是熟练运用 FiltersUpdates 这两个工具类。

分享:
扫描分享到社交APP
上一篇
下一篇