使用GeoJsonPoint进行NearSphere查询时,为什么会收到CodecConfigurationException?

问题描述

这个问题使我感到困惑。因此,我有一个usermodel,其中使用org.springframework.data.mongodb.core.geo.GeoJsonPoint对象存储了用户的最后已知坐标,该对象转换为

"lastKNownCoordinates": {
        "type": "Point","coordinates": [77.596503,12.966267]
    }

我有一个短途旅行集合,它映射到ExcursionsModel对象。每个游览都有一个geolocation属性,它使用GeoJsonPoint规范存储其位置,如下所示:

"geolocation": {
        "coordinates": [73.739978,15.606188],"type": "Point"
    }

现在,当用户尝试添加附近的过滤器以获取指定距离内的偏移时,我在查询对象($nearSphere)上添加org.springframework.data.mongodb.core.query.Query条条件

if (userRequest.getdistInMeters() != null) {
    query.addCriteria(Criteria.where("geolocation")
                        .nearSphere(usermodel.getLastKNownCoordinates())
                        .maxdistance(userRequest.getdistInMeters())
    );
}

从技术上讲,这应转换为以下内容

...
"geolocation" : {
  $nearSphere: {
     $geometry: {
        type : "Point",coordinates : [ 77.596503,12.966267 ]
     },$maxdistance: 10000
  }
}...

(如果distInMeters = 10000)

但是,我宁愿翻译成:

...
"geolocation" : { 
    "$nearSphere" : { 
        "$geometry" : { 
            "$java" : Point [x=-3.703790,y=40.416775] 
        } 
    } 
}...

请注意,GeoJsonPoint的转换不正确。我也收到以下异常

org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class org.springframework.data.mongodb.core.geo.GeoJsonPoint.

输出的完整记录:

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing Failed; nested exception is org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class org.springframework.data.mongodb.core.geo.GeoJsonPoint.] with root cause
org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class org.springframework.data.mongodb.core.geo.GeoJsonPoint.
    at org.bson.internal.CodecCache.getorThrow(CodecCache.java:57)
    at org.bson.internal.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:64)
    at org.bson.internal.ChildCodecRegistry.get(ChildCodecRegistry.java:52)
    at org.bson.codecs.DocumentCodec.writeValue(DocumentCodec.java:197)
    at org.bson.codecs.DocumentCodec.writeMap(DocumentCodec.java:212)
    at org.bson.codecs.DocumentCodec.writeValue(DocumentCodec.java:195)
    at org.bson.codecs.DocumentCodec.writeMap(DocumentCodec.java:212)
    at org.bson.codecs.DocumentCodec.writeValue(DocumentCodec.java:195)
    at org.bson.codecs.DocumentCodec.writeMap(DocumentCodec.java:212)
    at org.bson.codecs.DocumentCodec.encode(DocumentCodec.java:154)
    at org.bson.codecs.DocumentCodec.encode(DocumentCodec.java:45)
    at org.bson.BsonDocumentWrapper.getUnwrapped(BsonDocumentWrapper.java:195)
    at org.bson.BsonDocumentWrapper.isEmpty(BsonDocumentWrapper.java:115)
    at com.mongodb.internal.operation.DocumentHelper.putIfNotNullOrEmpty(DocumentHelper.java:43)
    at com.mongodb.internal.operation.FindOperation.getCommand(FindOperation.java:792)
    at com.mongodb.internal.operation.FindOperation.access$1600(FindOperation.java:77)
    at com.mongodb.internal.operation.FindOperation$4.create(FindOperation.java:858)
    at com.mongodb.internal.operation.CommandOperationHelper.executeCommandWithConnection(CommandOperationHelper.java:219)
    at com.mongodb.internal.operation.FindOperation$1.call(FindOperation.java:631)
    at com.mongodb.internal.operation.FindOperation$1.call(FindOperation.java:625)
    at com.mongodb.internal.operation.OperationHelper.withReadConnectionSource(OperationHelper.java:462)
    at com.mongodb.internal.operation.FindOperation.execute(FindOperation.java:625)
    at com.mongodb.internal.operation.FindOperation.execute(FindOperation.java:77)
    at com.mongodb.client.internal.MongoClientDelegate$DelegateOperationExecutor.execute(MongoClientDelegate.java:190)
    at com.mongodb.client.internal.MongoIterableImpl.execute(MongoIterableImpl.java:135)
    at com.mongodb.client.internal.MongoIterableImpl.iterator(MongoIterableImpl.java:92)
    at com.example.comercial.backend.dao.ExcursionDao.getAllExcursions(ExcursionsDao.java:140)
    at com.example.comercial.backend.service.ExcursionsService.getAllExcursions(ExcursionsService.java:70)
    at com.example.comercial.backend.controller.ExcursionsController.getAllExcursions(ExcursionsController.java:38)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.web.method.support.invocableHandlerMethod.doInvoke(invocableHandlerMethod.java:190)
    at org.springframework.web.method.support.invocableHandlerMethod.invokeForRequest(invocableHandlerMethod.java:138)
    at org.springframework.web.servlet.mvc.method.annotation.ServletinvocableHandlerMethod.invokeAndHandle(ServletinvocableHandlerMethod.java:105)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:878)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:792)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
    at org.springframework.web.servlet.dispatcherServlet.dodispatch(dispatcherServlet.java:1040)
    at org.springframework.web.servlet.dispatcherServlet.doService(dispatcherServlet.java:943)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:652)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.AbstractRequestLoggingFilter.doFilterInternal(AbstractRequestLoggingFilter.java:289)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.boot.actuate.metrics.web.servlet.WebMvcmetricsFilter.doFilterInternal(WebMvcmetricsFilter.java:93)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1589)
    at org.apache.tomcat.util.net.socketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)

ExcursionsDao类中抛出异常的代码片段:

...
FindIterable<Document> findIterable = mongoTemplate.getCollection("excursions")
                .find(query.getQueryObject())
                .skip(pageable.getPageSize() * (pageable.getPageNumber() - 1))
                .limit(pageable.getPageSize());

if (userRequest.getorderBy() != null && userRequest.getSortOrder() != null) {
    if (userRequest.getSortOrder().getSortOrder().equals(SortOrder.ASC.getSortOrder())) {
        findIterable.sort(Sorts.ascending(userRequest.getorderBy().getorderBy()));
    } else {
        findIterable.sort(Sorts.descending(userRequest.getorderBy().getorderBy()));
    }
}

ArrayList<ExcursionModel> excursionModelList = new ArrayList<>();

for (Document document : findIterable) {             // Line 140 where the exception is thrown
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNowN_PROPERTIES,false);
    excursionModelList.add(objectMapper.convertValue(document,ExcursionModel.class));
}

我正在使用mongo连接的认值,该连接实际上是通过GeoJsonCodecProvider初始化的。

来自MongoClientSettings类:

public final class MongoClientSettings {
    private static final CodecRegistry DEFAULT_CODEC_REGISTRY =
            fromProviders(asList(new ValueCodecProvider(),new BsonValueCodecProvider(),new DBRefCodecProvider(),new DBObjectCodecProvider(),new DocumentCodecProvider(new DocumentToDBRefTransformer()),new IterableCodecProvider(new DocumentToDBRefTransformer()),new MapCodecProvider(new DocumentToDBRefTransformer()),new GeoJsonCodecProvider(),new GridFSFileCodecProvider(),new jsr310CodecProvider(),new BsonCodecProvider()));

我尝试了哪些无效的替代方法

1。。我为GeoJsonPoint 对象编写了以下序列化程序(取自我忘记的SO答案)

public class GeoJsonPointSerializer extends JsonSerializer<GeoJsonPoint> {

    @Override
    public void serialize(final GeoJsonPoint value,final JsonGenerator gen,final SerializerProvider serializers)
            throws IOException {
        gen.writeStartObject();
        gen.writeStringField("type",value.getType());
        gen.writeArrayFieldStart("coordinates");
        gen.writeObject(value.getCoordinates());
        gen.writeEndarray();
        gen.writeEndobject();
    }

}

将其用于ExcursionsModel中的地理位置属性

@JsonSerialize(using = GeoJsonPointSerializer.class)
@GeoSpatialIndexed(name = "geoIndex",type = GeoSpatialIndexType.GEO_2DSPHERE)
private GeoJsonPoint geolocation;

2。 我创建了CodecRegistry的实例,并向其中添加PointCodecMongoClientSettings中的认编解码器注册表,然后使用此编解码器注册表初始化了FilterIterable类。

CodecRegistry codecRegistry = CodecRegistries.fromregistries(
                MongoClientSettings.getDefaultCodecRegistry(),CodecRegistries.fromCodecs(new PointCodec(MongoClientSettings.getDefaultCodecRegistry()))
        );

FindIterable<Document> findIterable = mongoTemplate.getCollection("excursions").withCodecRegistry(codecRegistry)
                .find(query.getQueryObject())
                .skip(pageable.getPageSize() * (pageable.getPageNumber() - 1))
                .limit(pageable.getPageSize());

在所有情况下,我都会抛出相同的异常。

注意: 我知道StackOverflow上的其他问题看起来与此类似,但它们的目标与我要实现的目标有些不同。我想使用查询对象来保持查询动态。

我正在使用的依赖项:

spring-boot-starter-data-mongodb | 2.3.3。发布

mongodb-core-driver | 4.0.5

spring-data-mongdb | 3.0.3。发布

解决方法

您正在混合春季mongo和mongo客户。对于ex模型,您使用的是Spring mongo类-对于查询,您直接使用mongo客户端。解决方法是使用其中任何一个。

从GeoJsonPoint更改为com.mongodb.client.model.geojson.Point或将查询更改为使用spring之类的

Criteria criteria = Criteria.where(key).is(value);
Query query = Query.query(criteria).skip(num).limit(num);
List<ExcursionsModel> excursions = mongoTemplate.find(query,ExcursionsModel.class);

工作示例(Spring Mongo Lib)

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.geo.Metrics;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.GeoNearOperation;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexed;
import org.springframework.data.mongodb.core.index.GeospatialIndex;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;

import java.util.ArrayList;
import java.util.List;

@SpringBootApplication
public class So63886374Application {

    public static void main(String[] args) {
        SpringApplication.run(So63886374Application.class,args);
    }

    @Autowired
    private MongoTemplate mongoTemplate;

    public void queryData()  {
        //Distance from fort worth - 45 miles - output dallas - 200 miles - austin/dallas - 300 miles - austin/dallas/houston
        Criteria criteria = Criteria.where("geolocation").nearSphere(new GeoJsonPoint(-97.3327459,32.753177)).maxDistance(200*1609.34);
        Query query = Query.query(criteria);
        List<ExcursionsModel> excursionsModels = mongoTemplate.find(query,ExcursionsModel.class);
        excursionsModels.forEach(es -> System.out.println(es.getGeolocation()));

        //Aggregation query to capture distance
        NearQuery nearQuery = NearQuery.near(-97.3327459,32.753177,Metrics.MILES).maxDistance(200);
        GeoNearOperation geoNearOperation = Aggregation.geoNear(nearQuery,"distanceFromFW");
        Aggregation aggregation = Aggregation.newAggregation(geoNearOperation);
        List<ExcursionsModel> excursionsModelsAg = mongoTemplate.aggregate(aggregation,ExcursionsModel.class,ExcursionsModel.class).getMappedResults();
        excursionsModelsAg.forEach(es -> System.out.println(es.getDistanceFromFW()));
    }

    @Document
    public class ExcursionsModel {
        @GeoSpatialIndexed(name = "geoIndex",type = GeoSpatialIndexType.GEO_2DSPHERE)
        private GeoJsonPoint geolocation;
        private long distanceFromFW;
        public void setGeolocation(GeoJsonPoint geolocation) {
            this.geolocation = geolocation;
        }

        public GeoJsonPoint getGeolocation() {
            return geolocation;
        }

        public long getDistanceFromFW() {
            return distanceFromFW;
        }

        public void setDistanceFromFW(long distanceFromFW) {
            this.distanceFromFW = distanceFromFW;
        }
    }

    public void insertData()  {
        List<ExcursionsModel> excursionsModels = new ArrayList<>();
        mongoTemplate.indexOps(ExcursionsModel.class).ensureIndex(new GeospatialIndex("geolocation").named("geoIndex").typed(GeoSpatialIndexType.GEO_2DSPHERE));
        ExcursionsModel austin = new ExcursionsModel();
        austin.setGeolocation(new GeoJsonPoint( -97.7436995,30.2711286));
        ExcursionsModel houston = new ExcursionsModel();
        houston.setGeolocation(new GeoJsonPoint(-95.3676974,29.7589382));
        ExcursionsModel dallas = new ExcursionsModel();
        dallas.setGeolocation(new GeoJsonPoint(-96.7968559,32.7762719));
        excursionsModels.add(austin);
        excursionsModels.add(houston);
        excursionsModels.add(dallas);
        mongoTemplate.insert(excursionsModels,ExcursionsModel.class);
    }

    public void dropData() {
        mongoTemplate.dropCollection(ExcursionsModel.class);
    }

    @Bean
    public ApplicationRunner runner() {
        return args -> {
            dropData();
            insertData();
            queryData();
        };
    }

}

输出

Point [x=-96.796856,y=32.776272] //Dallas
Point [x=-97.743700,y=30.271129] //Austin
31
173

示例(Mongo Client-使用Pojo编解码器)

public void queryData()  {
    //Distance from fort worth - 45 miles - output dallas - 200 miles - austin/dallas - 300 miles - austin/dallas/houston
    MongoClient mongoClient = MongoClients.create();
    MongoDatabase database = mongoClient.getDatabase("test");
    Point refPoint = new Point(new Position(-97.3327459,32.753177));
    Bson filters = Filters.nearSphere("geolocation",refPoint,200*1609.34,0.0);
    CodecRegistry pojoCodecRegistry = fromRegistries(MongoClientSettings.getDefaultCodecRegistry(),fromProviders(PojoCodecProvider.builder().register(ExcursionsModel.class).build()));
    FindIterable<ExcursionsModel> excursionsModels = database.getCollection("excursionsmodel",ExcursionsModel.class).withCodecRegistry(pojoCodecRegistry).find(filters);
        excursionsModels.forEach(es -> System.out.println(es.getGeolocation()));
    }

public static class ExcursionsModel {
    private Point geolocation;    
    public void setGeolocation(Point geolocation) {
        this.geolocation = geolocation;
    }
    public Point getGeolocation() {
        return geolocation;
    }
}

输出

Point{coordinate=Position{values=[-96.7968559,32.7762719]}}
Point{coordinate=Position{values=[-97.7436995,30.2711286]}}
,

dis您是这样创建收藏索引的吗?

db.user.createIndex({ geolocation : "2dsphere" } );

请运行此命令,然后重试。