Geospatial $near в текущем значении поля документа

Возьмите этот запрос:

{ 'location' : { '$near' : [x,y], '$maxDistance' : this.field } }

Я хочу присвоить $maxDistance значение указанного поля из текущего оцениваемого документа. Это возможно?


person anat0lius    schedule 22.06.2017    source источник
comment
Нет, это невозможно.   -  person Sergio Tulentsev    schedule 22.06.2017
comment
Вы не первый с этим вопросом :) stackoverflow.com/questions/39970436/   -  person Sergio Tulentsev    schedule 22.06.2017
comment
@SergioTulentsev Тогда вы читаете не те посты. Это было возможно в течение некоторого времени. Вы просто делаете это по-другому.   -  person Neil Lunn    schedule 22.06.2017
comment
@NeilLunn: ну, эта конкретная потребность может быть удовлетворена альтернативным способом, но ответ на общий вопрос (могу ли я использовать поля документа в запросе?) по-прежнему отрицательный.   -  person Sergio Tulentsev    schedule 22.06.2017
comment
@SergioTulentsev Нет, общий ответ - использовать $redact или, что еще хуже, $where. Всегда был.   -  person Neil Lunn    schedule 22.06.2017
comment
@NeilLunn: связанный вопрос, он задает одно и то же? Можем ли мы дублировать его, теперь, когда у вас есть этот ответ?   -  person Sergio Tulentsev    schedule 22.06.2017
comment
@SergioTulentsev Я бы с удовольствием, но для этого нужно проголосовать за ответ. Это, безусловно, отвечает на другой вопрос, даже если ОП здесь думает, что это не их дело. Я отчасти думаю, что это, хотя, потому что это не редкость.   -  person Neil Lunn    schedule 22.06.2017


Ответы (1)


Да, это возможно. Вместо этого вы просто используете $geoNear. Остерегайтесь уловов и внимательно читайте.

Предполагая, что вы намерены сохранить поле, такое как "travelDistance", чтобы указать в документе, что любые такие поиски должны быть «внутри» указанного расстояния от запрашиваемой точки, чтобы быть действительными. Затем мы просто запрашиваем и оцениваем условие с помощью $redact:

db.collection.aggregate([
  { "$geoNear": {
    "near": { 
      "type": "Point",
      "coordinates": [x,y]
    },
    "spherical": true,
    "distanceField": "distance"
  }},
  { "$redact": {
    "$cond": {
      "if": { "$lte": [ "$distance", "$travelDistance" ] },
      "then": "$$KEEP",
      "else": "$$PRUNE"
    }
  }}
])

Единственная загвоздка в том, что $geoNear так же, как и $near в первую очередь вернет только определенное количество документов "near". Вы можете настроить это с помощью параметров, но в отличие от общей формы запроса, это в основном гарантирует, что возможные возвращаемые результаты будут меньше, чем указанные «ближайшие» числа.

Пока вы осознаете это, это совершенно справедливо.

На самом деле это общий способ определения того, что находится «близко» в радиусе.

Также помните о «расстоянии» в зависимости от того, как вы сохранили координаты. В качестве устаревших пар координат расстояния будут указаны в радианах, которые вам, вероятно, потребуются математические расчеты для преобразования в километры или мили.

При использовании GeoJSON расстояния всегда считаются в метрах, как стандартный формат.

Все математические заметки есть в документации.

Примечание Внимательно прочитайте документацию $geoNear. . Такие параметры, как "spherical", необходимы для индексов "2dsphere", таких как вы должны иметь для координат реального мира. Также может потребоваться применить "limit", чтобы увеличить результат документа по умолчанию, равный 100, для дальнейшей обрезки.


Поскольку в комментариях упоминается весенний монго, то для этого делается то же самое:

Aggregation aggregation = newAggregation(
    new AggregationOperation() {
      @Override
      public DBObject toDBObject(AggregationOperationContext context) {
        return new BasicDBObject("$geoNear",
          new BasicDBObject(
            "near", new BasicDBObject(
              "type","Point")
              .append("coordinates", Arrays.asList(20,30))
            )
            .append("spherical",true)
            .append("distanceField","distance")
          );
        }
    },
    new AggregationOperation() {
      @Override
      public DBObject toDBObject(AggregationOperationContext context) {
        return new BasicDBObject("$redact",
          new BasicDBObject(
            "$cond", Arrays.asList(
               new BasicDBObject("$lte", Arrays.asList("$distance", "$travelDistance")),
               "$$KEEP",
               "$$PRUNE"
             )
          )
       );
     }
    }
);
person Neil Lunn    schedule 22.06.2017
comment
Это работает, спасибо. Но теперь у меня другая проблема, так как я использую структуру Spring Data Mongodb, она по-прежнему не поддерживает $redact jira.spring.io/browse/DATAMONGO-931 :( - person anat0lius; 22.06.2017
comment
@LiLou_ У него может не быть вспомогательного метода, но вы всегда можете реализовать свой собственный этап конвейера как абстрактный, используя ввод DBObject. Несколько ответов на этом сайте об этом. Я знаю, так как я написал несколько из них. - person Neil Lunn; 22.06.2017
comment
@LiLou_ Добавил перевод Spring Data Mongo и включил тег в вопрос. Как видите, добавить пользовательские этапы конвейера и операторы довольно просто. - person Neil Lunn; 23.06.2017
comment
эта агрегация не будет работать с $near ? $geoNear нужно? - person anat0lius; 23.06.2017
comment
@LiLou_ $geoNear — это агрегированная версия $near. Вам нужна агрегация и $geoNear, чтобы вернуть расстояние для сравнения. Вы ничего не теряете. Если вы все еще не понимаете, задайте новый вопрос. - person Neil Lunn; 23.06.2017
comment
Ok. Я спрашиваю, потому что $geoNear требует индекса 2dsphere, хотя мне он не нужен, мне было бы достаточно 2d индекса ($near). Спасибо еще раз - person anat0lius; 23.06.2017
comment
@LiLou_ Пожалуйста, прочитайте документацию, а также слова в ответе. Если это просто индекс 2d, вам не нужно включать spherical, просто оставьте его вне параметров конвейера. - person Neil Lunn; 23.06.2017