1

In a collection, the documents may contains fields like "name", "name:en", "name:de"

{
    _id:123456
    tag:{
        name: HongKong
        name-zh: 香港
        other_tag1: value1
        other_tag2: value2
    }
}
{
    _id:123457
    tag:{
        name-en: Beijing
        other_tag1: value1
        other_tag2: value2
    }
}

What I want is to list out all fields with field name contains "name". I tried with the following codes, but it seems to be stupid, and doesn't list all possible "name"s.

find = {'$or': [{'tag.name': {'$exists': 1}}, {'tag.name:zh': {'$exists': 1}}, {'tag.name:en': {'$exists': 1}}]}
project = {'_id': 0, 'name': '$tag.name', 'name:zh': '$tag.name:zh', 'name:en': '$tag.name:en'}
names = list(db.node.aggregate([{'$match': find}, {'$project': project}]))

And the result I want is:

{
    name: HongKong
    name-zh: 香港
}
{
    name-en: Beijing
}
陈梓泉
  • 145
  • 2
  • 9
  • Can you show an expected result? – Neodan Jan 19 '18 at 09:28
  • It is not possible to have same key in a document. like `name:HongKong` and `name:zh` Is that right document ? – s7vr Jan 19 '18 at 09:46
  • @Veeram 'name:zh' is a key in dict. It's confusing. I've changed ':' to '-'. – 陈梓泉 Jan 19 '18 at 10:01
  • All you have to do is correct field names ? Something like `find = {'$or': [{'tag.name': {'$exists': 1}}, {'tag.name-zh': {'$exists': 1}}, {'tag.name-en': {'$exists': 1}}]} project = {'_id': 0, 'name': '$tag.name', 'name-zh': '$tag.name-zh', 'name-en': '$tag.name-en'} names = list(db.node.aggregate([{'$match': find}, {'$project': project}]))` – s7vr Jan 19 '18 at 10:04
  • @Veeram That's right. The query I wrote returns {tag.name, tag.name:en and tag.name:zh} if any of them exists. And I expect to get all fields with "name" in the field name. – 陈梓泉 Jan 19 '18 at 10:10
  • So you would like to return only the matched names in your output not all names correct ? What is your mongo version ? – s7vr Jan 19 '18 at 10:12
  • @Veeram MongoDB 3.4.9. I want return all fields and values with field name in a specific pattern, and in this case the pattern is "name" or start with "name:" – 陈梓泉 Jan 19 '18 at 10:20

1 Answers1

0

Its possible to search field names using $objectToArray It may not a optimal solution based on data you need to search.

$objectToArray to change the tag into array of key value pair followed by $unwind to search the keys for the input pattern and $group + $arrayToObject to return the matching key value pairs.

db.col.aggregate([
    {$match:{'$or': [{'tag.name': {'$exists': 1}}, {'tag.name-zh': {'$exists': 1}}, {'tag.name-en': {'$exists': 1}}]}}, 
    {$project:{"tagskv":{$objectToArray:"$tag"}}},
    {$unwind:"$tagskv"},
    {$match:{"tagskv.k": { $regex: /name/i }}},
    {$group:{_id:"$_id", names:{$push:"$tagskv"}}},
    {$project:{"names":{$arrayToObject:"$names"}}},
    {$replaceRoot:{newRoot:"$names"}}
])
s7vr
  • 73,656
  • 11
  • 106
  • 127