Подстановочный знак MongoDB в ключе запроса

#mongodb #mongodb-query #wildcard

#mongodb #mongodb-запрос #подстановочный знак

Вопрос:

Возможно ли подстановочный знак ключа в запросе? Например, учитывая следующую запись, я хотел бы сделать .find({'a.*': 4})

Это обсуждалось здесь https://jira.mongodb.org/browse/SERVER-267 но, похоже, проблема не решена.

 {
  'a': {
    'b': [1, 2],
    'c': [3, 4]
  }
}
  

Комментарии:

1. у меня такая же проблема с mongo. у меня есть много документов, подобных этому: { one: { two: { more:data }, также: { more:data } } } как только вы углубляетесь на один уровень в дерево документов, каждый узел на втором уровне является контейнером для документов с согласованной структурой. итак, я хотел бы иметь возможность выполнять поиск следующим образом: find({‘one.*.some.data’:5})

Ответ №1:

Как и было предложено, это невозможно. Проблема с сервером, на которую вы ссылались, все еще находится в разделе «проблемы, в которых мы не уверены».

MongoDB обладает некоторым интеллектом, связанным с использованием массивов, и я думаю, что это часть сложности, связанной с такой функцией.

Выполните следующий запрос db.foo.find({ 'a.b' : 4 } ) . Этот запрос будет соответствовать следующим документам.

 { a: { b: 4 } }
{ a: [ { b: 4 } ] }
  

Итак, что здесь делает «подстановочный знак»? db.foo.find( { a.* : 4 } ) Соответствует ли он первому документу? Как насчет второго?

Более того, что это означает семантически? Как вы описали, запрос фактически является «найти документы, где любое поле в этом документе имеет значение 4». Это немного необычно.

Есть ли конкретная семантика, которой вы пытаетесь достичь? Возможно, изменение структуры документа даст вам нужный запрос.

Комментарии:

1. спасибо, это проясняет. Более конкретно, то, что я хочу сделать, — это подстановочный знак только для конкретного узла в ветке, то есть для любого соответствующего подполя a . Я не совсем понимаю, как a.* говорит «найти, где любое поле». Разве это не «найти документы, в которых есть поле верхнего уровня ‘a’ с подполем, соответствующим 4»?

2. Я думаю, что путаница здесь связана с «подполем». Когда я пишу {a:{b:4,c:2}} , я говорю, что значение a является объектом JSON. Этот объект JSON имеет два ключа b и c . Значения этих ключей равны 4 и 2 соответственно. Когда вы запрашиваете a.* , вы фактически запрашиваете синтаксис, который перебирает все ключи в этом объекте JSON. Вы не просите «перебирать массив» , вы просите «перебирать свойства объекта» . Это немного необычно, поэтому я прошу здесь указать конкретный вариант использования.

3. @gates: У меня есть вариант использования. myDocInMongo = {‘someUnknownKey’:{propToCheck: true}, ‘someKnownKey’: true}; Теперь я хочу найти этот документ, используя селектор {someKnownKey:{$exists: true}} но я также хочу убедиться, что ни у одного из других ключей нет объекта со свойством propToCheck. Итак, как показано ниже: {‘*.propToCheck’:{$exists:false}, {someKnownKey:{$exists:true}}}

Ответ №2:

Я столкнулся с этим вопросом, потому что столкнулся с той же проблемой. Принятый поставщик ответов здесь объясняет, почему это не поддерживается, но на самом деле не решает саму проблему.

В итоге я нашел решение, которое делает использование подстановочного знака, указанное здесь, избыточным, и делюсь им здесь на случай, если кто-нибудь когда-нибудь найдет этот пост

Почему я хотел использовать подстановочные знаки в своих запросах MongoDB?В моем случае мне нужна была эта «функция», чтобы иметь возможность находить совпадения внутри словаря (точно так же, как демонстрирует код вопроса).

Какие есть альтернативы?Используйте перевернутую карту (очень похожую на то, как работает DNS) и просто используйте ее. Итак, в нашем случае мы можем использовать что-то похожее на это:

 {
  "a": {
    "map": {
      "b": [1, 2, 3],
      "c": [3, 4]
    },
    "reverse-map": {
      "1": [ "b" ],
      "2": [ "b" ],
      "3": [ "b", "c" ],
      "4": [ "c" ]
    }
  }
}
  

Я знаю, это занимает больше памяти, и операции вставки / обновления должны проверять, что этот набор всегда симметричен, и все же — это решает проблему. Теперь, вместо того, чтобы создавать воображаемый запрос, подобный

 db.foo.find( { a.map.* : 4 } )
  

Я могу сделать реальный запрос

 db.foo.find( { a.reverse-map.4 : {$exists: true} } )
  

Который вернет все элементы, имеющие определенное значение (в нашем примере 4 )

Я знаю — этот подход требует больше памяти, и вам нужно правильно управлять индексами, если вы хотите получить хорошую производительность (читайте документы), и все же — это хорошо для моего варианта использования. Надеюсь, это когда-нибудь поможет и кому-нибудь другому

Ответ №3:

Начиная с MongoDB версии v3.4 , вы можете использовать его $objectToArray для преобразования a в массив k-v кортежей для выполнения запросов.

 db.collection.aggregate([
  {
    "$addFields": {
      "a": {
        "$objectToArray": "$a"
      }
    }
  },
  {
    $match: {
      "a.v": 4
    }
  },
  {
    "$addFields": {
      // cosmetics to revert back to original structure
      "a": {
        "$arrayToObject": "$a"
      }
    }
  }
])
  

Вот игровая площадка Mongo для вашей справки.