文章目录
-
- 创建或切换数据库
- **1. 查询 (`find`)**
- 2. 插入 (`insert`)
- 3. 更新 (`update`)
- 4. 删除 (`delete`)
- 5. 排序 & 分页
- 6. 地理位置查询
- 其它
-
- MongoDB Shell
- linux安装MongoDB Shell
- SpringBoot集成MongoDb
- planner returned error :: caused by :: unable to find index for $geoNear query' on server 192.168.26.128:27017. The full response is {"ok": 0.0, "errmsg": "error processing query: ns=location.vehicle_locationTree: $and\n vehicleId $eq \"1871467348318228480\"\n GEONEAR field=location maxdist=0.000156786 isNearSphere=1\nSort: {}\nProj: {}\n planner returned error :: caused by :: unable to find index for $geoNear query", "code": 291, "codeName": "NoQueryExecutionPlans"}
以下是 MySQL 和 MongoDB 有差异的地方的对比表格:
概念 | MySQL | MongoDB |
---|---|---|
表 | 表(Table) | 集合(Collection) |
字段/列 | 字段/列(Column),结构固定 | 字段(Field),结构灵活,可动态扩展 |
记录/行 | 记录/行(Row),数据必须符合表结构 | 文档(Document),以BSON格式存储,支持嵌套 |
主键 | 主键(Primary Key),通常是自增的id |
_id 字段,默认为ObjectId类型,非自增 |
查询语言 | SQL(Structured Query Language) | MQL(MongoDB Query Language) |
数据模型 | 固定模式的关系型数据模型 | 灵活模式的文档型数据模型 |
事务支持 | 完全支持ACID属性 | 支持单文档原子操作;4.0版本后支持多文档事务 |
关系处理 | 使用外键实现表之间的关联 | 嵌入式(Embedding)或引用(Referencing)表示关系 |
存储格式 | 行存储格式 | BSON(类似于JSON) |
这里是详细的 MongoDB 语法总结,每个部分都有清晰的 说明 和 示例,帮助你快速掌握 MongoDB 查询、更新、插入、删除、索引、地理查询等操作。
创建或切换数据库
test> use location // 切换或创建数据库
switched to db location
1. 查询 (find
)
MongoDB 允许使用 find()
方法查询集合中的数据,并支持条件筛选、排序、分页等操作。
查询全部数据
查询 vehicle_location
集合中的所有数据:
db.vehicle_location.find({})
查询匹配字段
- 单条件查询
查询vehicleId
为"1820377198820880384"
的车辆信息:
db.vehicle_location.find({ vehicleId: "1820377198820880384" })
- 多条件查询
查询tenantId=3
,updateTime大于等于2024-07-01的,小于2025-07-02的数据
db.vehicle_location.find({
tenantId: "3",
updateTime: {
$gte: "2024-07-01",
$lt: "2025-07-02"
}
})
逻辑运算符
MongoDB 提供了一些常用的逻辑运算符:
操作符 | 作用 | 示例 |
---|---|---|
$eq |
等于 | { age: { $eq: 18 } } |
$ne |
不等于 | { age: { $ne: 18 } } |
$gt |
大于 | { age: { $gt: 18 } } |
$lt |
小于 | { age: { $lt: 18 } } |
$gte |
大于等于 | { age: { $gte: 18 } } |
$lte |
小于等于 | { age: { $lte: 18 } } |
$in |
在数组内 | { vehicleId: { $in: ["1820377198820880384", "1822090759331909632"] } } |
$nin |
不在数组内 | { vehicleId: { $nin: ["1820377198820880384"] } } |
示例:查询 18 岁及以上的所有用户
db.users.find({ age: { $gte: 18 } })
查所有集合
show collections
2. 插入 (insert
)
插入单条数据
向 vehicle_location
集合中插入一条新数据:
db.vehicle_location.insertOne({
vehicleId: "1820377198820880384",
vehicleNo: "甘A12456",
location: [103.855549, 36.050249],
updateTime: "2025-02-07T09:23:41"
})
插入多条数据
db.vehicle_location.insertMany([
{
vehicleId: "1820377198820880384",
vehicleNo: "甘A12456",
location: [103.855549, 36.050249],
updateTime: "2025-02-07T09:23:41"
},
{
vehicleId: "1822090759331909632",
vehicleNo: "甘B56789",
location: [103.857185, 36.054035],
updateTime: "2025-02-07T09:24:41"
}
])
3. 更新 (update
)
更新单条数据
将 vehicleId
为 "1820377198820880384"
的 updateTime
改为 "2025-02-07T10:00:00"
:
db.vehicle_location.updateOne(
{ vehicleId: "1820377198820880384" },
{ $set: { updateTime: "2025-02-07T10:00:00" } }
)
更新多条数据
将 updateTime
早于 "2025-02-07T09:30:00"
的车辆全部更新为 "2025-02-07T09:30:00"
:
db.vehicle_location.updateMany(
{ updateTime: { $lt: "2025-02-07T09:30:00" } },
{ $set: { updateTime: "2025-02-07T09:30:00" } }
)
4. 删除 (delete
)
删除集合
location> db.vehicleLocationBo.drop()
true
删除单条数据
删除 vehicleId
为 "1820377198820880384"
的车辆:
db.vehicle_location.deleteOne({ vehicleId: "1820377198820880384" })
删除多条数据
删除 updateTime
早于 "2025-02-07T09:00:00"
的所有车辆:
db.vehicle_location.deleteMany({ updateTime: { $lt: "2025-02-07T09:00:00" } })
5. 排序 & 分页
排序
按 updateTime
降序 排列:
db.vehicle_location.find().sort({ updateTime: -1 })
按 updateTime
升序 排列:
db.vehicle_location.find().sort({ updateTime: 1 })
分页
跳过 10 条数据,获取 5 条数据:
db.vehicle_location.find().skip(10).limit(5)
6. 地理位置查询
$near
需要 2dsphere 索引,但$geoWithin
不需要索引也能使用。
坐标格式:MongoDB GeoJSON 格式使用 [longitude, latitude](经度在前,纬度在后)。
距离单位:
maxDistance 单位为米(m)。
$geoNear 查询必须包含 spherical: true 来确保地球曲率计算。
$geoWithin
- 查询经纬度500米内的数据
db.collection.find({
location: {
$geoWithin: {
$centerSphere: [
[经度, 纬度], // 替换为目标点的经纬度
500 / 6378100 // 半径,单位是弧度(米转换为弧度)
]
}
}
});
- 查找某个区域内的点,例如查询某个矩形区域中的点
db.places.find({
location: {
$geoWithin: {
$box: [
[116.4035, 39.9140], // 左下角坐标
[116.4050, 39.9160] // 右上角坐标
]
}
}
});
- 查询某个多边形区域内的点:
db.places.find({
location: {
$geoWithin: {
$geometry: {
type: "Polygon",
coordinates: [
[
[116.4035, 39.9140],
[116.4050, 39.9140],
[116.4050, 39.9160],
[116.4035, 39.9160],
[116.4035, 39.9140] // 首尾坐标必须相同
]
]
}
}
}
});
- 判断点是否在区域内 ($geoIntersects)
db.areas.find({
region: {
$geoIntersects: {
$geometry: { type: "Point", coordinates: [116.4040, 39.9150] }
}
}
});
$near
经纬度500米内的数据
db.collection.find({
location: {
$near: {
$geometry: {
type: "Point",
coordinates: [经度, 纬度] // 替换为目标点的经纬度
},
$maxDistance: 500 // 单位是米
}
}
});
其它
MongoDB Shell
linux中用来连接mongo
linux安装MongoDB Shell
- 1.1 添加 MongoDB 官方仓库
sudo tee /etc/yum.repos.d/mongodb-org.repo <<EOF
[mongodb-org-7.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/7/mongodb-org/7.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://pgp.mongodb.com/server-7.0.asc
EOF
- 1.2 安装 mongosh
sudo yum install -y mongodb-mongosh
- 1…3 验证安装
mongosh --version
登陆
- 默认直接输入mongosh,就会登录到本机端口为27017的mongo
mongosh
- 如果端口改过,加上参数–port指定端口登陆
mongosh --port 38080
- 如果远程的monggodb,则要加上ip和端口
mongosh --host <ip> --port <port>
- 如果启用了身份验证,需要提供用户名、密码和认证数据库(通常是 admin)
mongosh --host <hostname> --port <port> -u <username> -p <password> --authenticationDatabase <authDB>
例如
mongosh --host 192.168.1.100 --port 27017 -u myuser -p mypassword --authenticationDatabase admin
- 如果已经登陆了,但是没有确认用户名密码,db.auth(用户名,密码)
kd> db.auth("zs","root")
{ ok: 1 }
SpringBoot集成MongoDb
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
java和mongodb交互
第一种,继承MongoRepository
import org.springframework.data.mongodb.repository.MongoRepository;
import java.util.List;
public interface UserRepository extends MongoRepository<User, String> {
List<User> findByName(String name);
}
第二种,使用MongoTemplate
@Autowired
private MongoTemplate mongoTemplate;
public List<User> findUsersByNameAndAge(String name, int age) {
Query query = new Query();
Criteria criteria = Criteria.where("name").is(name).and("age").is(age);
query.addCriteria(criteria);
return mongoTemplate.find(query, User.class);
}
查不到数据,需要指定集合名称
在find的第三个参数指定find(query, User.class,"")
,或者在接收的实体上面使用@Document(value = "")
指定,不然查不到。
示例
package com.dh.taxi.data.utils;
import com.dh.taxi.data.domain.bo.VehicleLocationBo;
import com.dh.taxi.data.domain.vo.VehicleLocationVo;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.domain.geo.Metrics;
import org.springframework.stereotype.Repository;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
/**
* 位置工具类
*/
@Repository
public class LocationUtils {
@Resource
private MongoTemplate mongoTemplate;
/**
* 查询车辆位置信息
* @param bo
* @return
*/
public List<VehicleLocationVo> getLocationList(VehicleLocationBo bo) {
Query query = new Query();
Criteria criteria = new Criteria();
List<Criteria> criteriaList = new ArrayList<>();
//String类型,等于该值的数据
if (bo.getVehicleId() != null) {
criteriaList.add(Criteria.where("vehicleId").is(bo.getVehicleId()));
}
if (bo.getVehicleNo() != null) {
criteriaList.add(Criteria.where("vehicleNo").is(bo.getVehicleNo()));
}
// 时间类型,查询指定时间段,mongo中的数据的格式:ISODate('2025-05-09T02:20:39.566Z')
if(bo.getIntervalTimeSeconds() != null ){
//当前时间
Instant now = Instant.now();
//默认30s,30s内
Instant tenSecondsAgo = now.minus(bo.getIntervalTimeSeconds(), java.time.temporal.ChronoUnit.SECONDS);
//当前时间30秒内的数据
criteriaList.add(Criteria.where("updateTime").gte(tenSecondsAgo).lte(now));
}
// 地理位置坐标,坐标Coordinate经纬度,半径RadiusMeter米的数据
if (!CollectionUtils.isEmpty(bo.getCoordinate())) {
List<Double> coordinate = bo.getCoordinate();
Point center = new Point(coordinate.get(0), coordinate.get(1));
Distance distance = new Distance(bo.getRadiusMeter(), Metrics.METERS); // 默认1000 米
criteriaList.add(Criteria.where("coordinate").nearSphere(center).maxDistance(distance.getNormalizedValue()));
}
// 如果有条件就拼接
if (!criteriaList.isEmpty()) {
criteria.andOperator(criteriaList.toArray(new Criteria[0]));
query.addCriteria(criteria);
}
return mongoTemplate.find(query, VehicleLocationVo.class);
}
/**
* 上传位置信息
*
* @return
*/
public VehicleLocationBo uploadLocation(VehicleLocationBo bo) {
bo.setUpdateTime(LocalDateTime.now());
return mongoTemplate.insert(bo);
}
}
删除
- 常用删除方法
方法名 | 说明 |
---|---|
remove(Query query, Class<T> entityClass) |
根据条件删除匹配的数据(可以删除多条) |
remove(Query query, String collectionName) |
指定集合删除(不绑定实体类) |
findAndRemove(Query query, Class<T> entityClass) |
查找并删除一条数据(返回删除的实体) |
- 假设你要删除
name = "张三"
的数据:
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private final MongoTemplate mongoTemplate;
public UserService(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
public void deleteUserByName(String name) {
Query query = new Query(Criteria.where("name").is(name));
mongoTemplate.remove(query, User.class); // 默认使用 User 映射的集合
}
}
- 如果你只想删除一条数据(匹配的第一条):
User removedUser = mongoTemplate.findAndRemove(query, User.class);
- 删除整个集合(清空表):
mongoTemplate.dropCollection(User.class);
// 或者指定集合名
mongoTemplate.dropCollection("user");
工具类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Optional;
@Component
public class MongoTemplateUtil {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 插入单个数据到MongoDB集合
*
* @param entity 要插入的实体对象
* @param <T> 实体类型
* @return 插入后的实体对象
*/
public <T> T insert(T entity) {
return mongoTemplate.insert(entity);
}
/**
* 批量插入数据到MongoDB集合
*
* @param entities 要插入的实体对象列表
* @param <T> 实体类型
* @return 插入后的实体对象列表
*/
public <T> List<T> insertAll(List<T> entities) {
return mongoTemplate.insertAll(entities);
}
/**
* 更新MongoDB集合中的数据
*
* @param query 查询条件
* @param entity 更新后的实体对象
* @param entityClass 实体类类型
* @param <T> 实体类型
*/
public <T> void update(Query query, T entity, Class<T> entityClass) {
mongoTemplate.findAndModify(query, entity, entityClass);
}
/**
* 根据条件查询单条数据
*
* @param query 查询条件
* @param entityClass 实体类类型
* @param <T> 实体类型
* @return 查询到的单条数据(如果存在)
*/
public <T> Optional<T> findOne(Query query, Class<T> entityClass) {
T result = mongoTemplate.findOne(query, entityClass);
return Optional.ofNullable(result);
}
/**
* 根据条件查询多条数据
*
* @param query 查询条件
* @param entityClass 实体类类型
* @param <T> 实体类型
* @return 查询到的所有数据
*/
public <T> List<T> find(Query query, Class<T> entityClass) {
return mongoTemplate.find(query, entityClass);
}
/**
* 根据ID查询数据
*
* @param id 数据的ID
* @param entityClass 实体类类型
* @param <T> 实体类型
* @return 查询到的数据(如果存在)
*/
public <T> Optional<T> findById(Object id, Class<T> entityClass) {
T result = mongoTemplate.findById(id, entityClass);
return Optional.ofNullable(result);
}
/**
* 删除数据
*
* @param query 查询条件
* @param entityClass 实体类类型
* @param <T> 实体类型
*/
public <T> void delete(Query query, Class<T> entityClass) {
mongoTemplate.remove(query, entityClass);
}
/**
* 判断集合中是否存在符合条件的数据
*
* @param query 查询条件
* @param entityClass 实体类类型
* @param <T> 实体类型
* @return 如果存在符合条件的数据,返回true;否则返回false
*/
public <T> boolean exists(Query query, Class<T> entityClass) {
return mongoTemplate.exists(query, entityClass);
}
/**
* 使用聚合查询MongoDB数据
*
* @param query 聚合查询条件
* @param entityClass 实体类类型
* @param <T> 实体类型
* @return 聚合查询结果列表
*/
public <T> List<T> aggregate(Query query, Class<T> entityClass) {
return mongoTemplate.aggregate(query, entityClass).getMappedResults();
}
/**
* 根据条件查询并返回指定字段
*
* @param query 查询条件
* @param entityClass 实体类类型
* @param fields 要返回的字段名(可变参数)
* @param <T> 实体类型
* @return 查询到的所有数据
*/
public <T> List<T> findWithFields(Query query, Class<T> entityClass, String... fields) {
if (!CollectionUtils.isEmpty(List.of(fields))) {
query.fields().include(fields);
}
return mongoTemplate.find(query, entityClass);
}
}
- 测试
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import java.util.List;
@SpringBootTest
public class MongoTemplateUtilTest {
@Autowired
private MongoTemplateUtil mongoTemplateUtil;
@Test
public void testInsert() {
User user = new User("john_doe", "John", "Doe", 30);
User insertedUser = mongoTemplateUtil.insert(user);
System.out.println("Inserted user: " + insertedUser);
}
@Test
public void testInsertAll() {
User user1 = new User("alice_smith", "Alice", "Smith", 25);
User user2 = new User("bob_jones", "Bob", "Jones", 28);
List<User> users = mongoTemplateUtil.insertAll(List.of(user1, user2));
users.forEach(user -> System.out.println("Inserted user: " + user));
}
@Test
public void testFind() {
Query query = new Query(Criteria.where("lastName").is("Doe"));
List<User> users = mongoTemplateUtil.find(query, User.class);
users.forEach(user -> System.out.println("Found user: " + user));
}
@Test
public void testDelete() {
Query query = new Query(Criteria.where("firstName").is("John"));
mongoTemplateUtil.delete(query, User.class);
System.out.println("Deleted users with first name 'John'.");
}
@Test
public void testExists() {
Query query = new Query(Criteria.where("firstName").is("Alice"));
boolean exists = mongoTemplateUtil.exists(query, User.class);
System.out.println("Does user exist? " + exists);
}
@Test
public void testFindWithFields() {
Query query = new Query(Criteria.where("lastName").is("Doe"));
List<User> users = mongoTemplateUtil.findWithFields(query, User.class, "firstName", "lastName");
users.forEach(user -> System.out.println("Found user with specified fields: " + user));
}
}
- 实体
public class User {
private String id;
private String username;
private String firstName;
private String lastName;
private int age;
// Constructors, getters and setters
public User(String username, String firstName, String lastName, int age) {
this.username = username;
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
// Getters and Setters omitted for brevity
}
planner returned error :: caused by :: unable to find index for $geoNear query’ on server 192.168.26.128:27017. The full response is {“ok”: 0.0, “errmsg”: “error processing query: ns=location.vehicle_locationTree: $and\n vehicleId $eq “1871467348318228480”\n GEONEAR field=location maxdist=0.000156786 isNearSphere=1\nSort: {}\nProj: {}\n planner returned error :: caused by :: unable to find index for $geoNear query”, “code”: 291, “codeName”: “NoQueryExecutionPlans”}
重要内容:caused by :: unable to find index for $geoNear query
,查询中使用了 $eq 和 $near(地理空间查询)组合,但是字段没有设置索引,设置索引就好了
db.集合名.createIndex({ "字段名": "2dsphere" })