Mongodb запрос с полями в тех же документах - PullRequest
30 голосов
/ 08 декабря 2011

У меня следующий json.

{
  "a1": {"a": "b"},
  "a2": {"a": "c"}
}

Я хочу запросить все документы, где a1 не равно a2 в том же документе.

Как я могу это сделать?

Ответы [ 5 ]

75 голосов
/ 08 декабря 2011

Вы можете использовать $where:

db.myCollection.find( { $where: "this.a1.a != this.a2.a" } )

Однако имейте в виду, что это не будет очень быстрым, потому что ему придется раскрутить движок java-скриптов иитерируйте каждый документ и проверяйте условие для каждого.

Если вам нужно выполнить этот запрос для больших коллекций или очень часто, лучше всего ввести денормализованный флаг, например areEqual.Тем не менее, такие поля низкой селективности не дают хорошую производительность индекса, потому что набор кандидатов по-прежнему большой.

21 голосов
/ 07 февраля 2017

Чтобы избежать JavaScript, используйте структуру агрегации:

db.myCollection.aggregate([
  {"$match":{"a1":{"$exists":true},"a2":{"$exists":true}}},
  {"$project": {
      "a1":1,
      "a2":1,
      "aCmp": {"$cmp":["$a1.a","$a2.a"]}
    }
  },
  {"$match":{"aCmp":0}}
])

На нашем сервере разработки эквивалентный запрос JavaScript выполняется в 7 раз дольше.

Обновление (10 мая 2017 г.)

Я только что понял, что мой ответ не отвечает на вопрос, в котором нужны значения, которые не равны (иногда я действительно медленный).Это будет работать для этого:

db.myCollection.aggregate([
  {"$match":{"a1":{"$exists":true},"a2":{"$exists":true}}},
  {"$project": {
      "a1":1,
      "a2":1,
      "aEq": {"$eq":["$a1.a","$a2.a"]}
    }
  },
  {"$match":{"aEq": false}}
])

$ne может использоваться вместо $eq, если условие соответствия было изменено на true, но я считаю, что использование $eq с false должно бытьболее интуитивно понятный.

8 голосов
/ 26 февраля 2018

обновление

используя новый оператор $ expr , доступный начиная с версии 3.6, вы можете использовать агрегатные выражения в поисковом запросе, например так:

  db.myCollection.find({$expr: {$ne: ["$a1.a", "$a2.a"] } });

Хотя этот комментарий решает проблему, я думаю, что лучшим вариантом для этого варианта использования было бы использование оператора $ addFields , доступного в версии 3.4 вместо $ project.

db.myCollection.aggregate([
     {"$match":{"a1":{"$exists":true},"a2":{"$exists":true}}},
     {"$addFields": {
           "aEq": {"$eq":["$a1.a","$a2.a"]}
         }
     },
     {"$match":{"aEq": false}} 
  ]);
4 голосов
/ 08 декабря 2011

MongoDB использует Javascript в фоновом режиме, поэтому

{"a": "b"} == {"a": "b"}

будет false.

Таким образом, чтобы сравнить каждого из них с

Для этого в MongoDB вы должны использовать оператор $ where

db.myCollection.find({$where: "this.a1.a != this.a2.a"});

Это предполагает, что каждый внедренный документ будет иметь свойство "a". Если это не так, все становится сложнее.

0 голосов
/ 30 ноября 2018

Спасибо всем за решение моей проблемы - что касается ответов, использующих aggregate (), меня сначала смутило то, что $ eq (или $ in, или множество других операторов) имеет различное значение в зависимости от того, где он находитсяиспользуемый.На этапе агрегации find () или $ match $ eq принимает одно значение и выбирает подходящие документы:

db.items.aggregate([{$match: {_id: {$eq: ObjectId("5be5feb45da16064c88e23d4")}}}])

Однако на этапе агрегации $ project $ eq принимает массивиз 2 выражений и создает новое поле со значением true или false:

db.items.aggregate([{$project: {new_field: {$eq: ["$_id", "$foreignID"]}}}])

Попутно, вот запрос, который я использовал в своем проекте, чтобы найти все элементы, чей список связанных элементов (из-за ошибки)связаны между собой:

db.items.aggregate([{$project: {idIn: {$in: ["$_id","$header.links"]}, "header.links": 1}}, {$match: {idIn: true}}])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...