28

How to use dot in field name ?

I see error in example:

db.test2.insert({ "a.a" : "b" })

can't have . in field names [a.a]
Bdfy
  • 23,141
  • 55
  • 131
  • 179
  • `"a\.a"` doesn't seem to make any difference. The string is still evaluated as `"a.a"` – codr Jan 24 '12 at 21:10
  • There are some changes in MongoDB 5.0 mentioned in the documentation: https://www.mongodb.com/docs/manual/reference/limits/#mongodb-limit-Restrictions-on-Field-Names – user202729 May 30 '22 at 02:52

7 Answers7

49

You can replace dot symbols of your field name to Unicode equivalent of \uff0E

db.test.insert({"field\uff0ename": "test"})
db.test.find({"field\uff0ename": "test"}).forEach(printjson)
{ "_id" : ObjectId("5193c053e1cc0fd8a5ea413d"), "field.name" : "test" }

See more:

  1. http://docs.mongodb.org/manual/faq/developers/#faq-dollar-sign-escaping
  2. http://docs.mongodb.org/manual/core/document/#dot-notation
scniro
  • 16,844
  • 8
  • 62
  • 106
Fisk
  • 651
  • 5
  • 14
  • 3
    @William if you use the pure unicode char you suggest, it turns into an ASCII period anyway - it would be a fancy way to specify it. \uFFOE is a "FULLWIDTH FULL STOP" character, and as a non-ASCII character will be ignored. A \u2024 "ONE DOT LEADER" character might look better, since its centered in its space just as the normal period is. [I just verified that on Apple products, the 2024 character looks much better - just like a real period.] – David H Nov 14 '13 at 19:37
  • Great point using '\u002e' it successfully encoded the object key. however retrieving is cumbersome: doc[ unescape('field\u002ename')] does not work for me. you have to do the unescape in a separate variable then retrieve it accordingly. I would rather stringify and save the trouble is fighting against Mongo Db Driver – Matthew Chen Jan 29 '14 at 04:10
  • This is a quick and dirty C# method, but I wasted too much time on this to end up caring a whole lot. Can't seem to paste the code in the comment box so will add an answer... [link](http://stackoverflow.com/a/42185465/2109256) – darksider474 Feb 12 '17 at 08:06
  • This used to be an Mongo FAQ entry (the first link) but **it was removed** (probably mean "no longer recommended.") (see some history in https://stackoverflow.com/questions/12397118/mongodb-dot-in-key-name/25937348#25937348) – user202729 May 30 '22 at 02:58
4

You can also write a SONManipulator using the pymongo library that transforms the data going to and back out of mongodb. There are downsides; there is a performance hit (impact depends on your use case) and you have to transform your keys when you do searches using find.

Here's code with an example of how to use it in the comment for the KeyTransform class:

from pymongo.son_manipulator import SONManipulator

class KeyTransform(SONManipulator):
    """Transforms keys going to database and restores them coming out.

    This allows keys with dots in them to be used (but does break searching on
    them unless the find command also uses the transform).

    Example & test:
        # To allow `.` (dots) in keys
        import pymongo
        client = pymongo.MongoClient("mongodb://localhost")
        db = client['delete_me']
        db.add_son_manipulator(KeyTransform(".", "_dot_"))
        db['mycol'].remove()
        db['mycol'].update({'_id': 1}, {'127.0.0.1': 'localhost'}, upsert=True,
                           manipulate=True)
        print db['mycol'].find().next()
        print db['mycol'].find({'127_dot_0_dot_0_dot_1': 'localhost'}).next()

    Note: transformation could be easily extended to be more complex.
    """

    def __init__(self, replace, replacement):
        self.replace = replace
        self.replacement = replacement

    def transform_key(self, key):
        """Transform key for saving to database."""
        return key.replace(self.replace, self.replacement)

    def revert_key(self, key):
        """Restore transformed key returning from database."""
        return key.replace(self.replacement, self.replace)

    def transform_incoming(self, son, collection):
        """Recursively replace all keys that need transforming."""
        for (key, value) in son.items():
            if self.replace in key:
                if isinstance(value, dict):
                    son[self.transform_key(key)] = self.transform_incoming(
                        son.pop(key), collection)
                else:
                    son[self.transform_key(key)] = son.pop(key)
            elif isinstance(value, dict):  # recurse into sub-docs
                son[key] = self.transform_incoming(value, collection)
        return son

    def transform_outgoing(self, son, collection):
        """Recursively restore all transformed keys."""
        for (key, value) in son.items():
            if self.replacement in key:
                if isinstance(value, dict):
                    son[self.revert_key(key)] = self.transform_outgoing(
                        son.pop(key), collection)
                else:
                    son[self.revert_key(key)] = son.pop(key)
            elif isinstance(value, dict):  # recurse into sub-docs
                son[key] = self.transform_outgoing(value, collection)
        return son
Ziad Sawalha
  • 181
  • 1
  • 9
4

Actualy you may use dots in queries. See: http://www.mongodb.org/display/DOCS/Dot+Notation+%28Reaching+into+Objects%29

Because of this special dot symbol mean you cannot use it in field names. Like you cannot use dot symbol in identifiers in most of programming languages.

You may write query db.test2.find({ "a.a" : "b" }) but if you want to be able to write such a query you need to insert your object like so: db.test2.insert({"a": {"a": "b"}}). This will create document with the field named "a" with the value of embeded document containing the field named "a" (again) with the value "b".

lig
  • 3,567
  • 1
  • 24
  • 36
  • I don't get it -- this seems to be an answer to someone asking "How can I write a query in MongoDB that uses dots?" not "How do I use dots in a field name?" (or, as the question-writer clearly means, "How can I query a field that has dots in the field name?") What is the point of this answer? – Offlein May 18 '18 at 15:49
  • @Offlein the main part of the answer you're interested in is `you cannot use it in field names` – lig Aug 22 '18 at 08:07
  • Thank you, but (as I've learned since posting that comment) it is definitively possible to use it in field names since it is up to the Mongo driver to enforce a restriction on the practice, and not all drivers do this. I found that out firsthand. ...As such, I have field names with dots in them. I just found your response strange and frustrating because it primarily answers a question that that was not asked, and your assertion that field names cannot have dots in them was in opposition to my experience. – Offlein Aug 23 '18 at 14:18
1
def remove_dots(data):
    for key in data.keys():
        if type(data[key]) is dict: data[key] = remove_dots(data[key])
        if '.' in key:
            data[key.replace('.', '\uff0E')] = data[key]
            del data[key]
    return data

this recursive method replaces all dot characters from keys of a dict with \uff0E as suggested by Fisk

Community
  • 1
  • 1
tuku
  • 415
  • 5
  • 12
1

I replaced the key value using myString.replace(".","\u2024") before inserting it into the JsonObject.

appsdownload
  • 751
  • 7
  • 20
0

Initially I used a simple recursion to replace all "." characters with its unicode equivalent but figured it out that even the dots in the values was getting replaced. So I thought that we should replace the dots only from keys and made the changes accordingly in case "if isinstance(input, dict)". I thought it should be a sufficient condition to do the magic but I forgot that dict value can also be a dict or a list and then I finally added that check that if value of a dict was not string then, go inside recursively and was finally able to come up with this solution which eventually did the trick.

def remove_dots(data):
    if isinstance(data, dict):
            return {remove_dots(key): value if isinstance(value, str) else remove_dots(value) for key,value in data.iteritems()}
    elif isinstance(data, list):
            return [remove_dots(element) for element in data]
    elif isinstance(data, str):
            return data.replace('.','\u002e')
    else:                                                                                             
            return data
Community
  • 1
  • 1
  • Replace 'input' with 'data' because your variable is called data. Also, using 'input' shadows the built-in function of input() which is poor form. You should only change values as the periods are necessary for object selection within the keys. – MuffintopBikini Sep 14 '16 at 18:08
0

I've only really come across this problem when trying to serialize Dictionaries and such where the offending dot can appear as a key name. Edited to show the references.

The quick and dirty C# approach:

using MongoDB.Bson;
using Newtonsoft.Json.Linq;
using System.Text.RegularExpressions;

public static T Sanitize<T>(T obj)
{
      var str = JObject.FromObject(obj).ToJson();
      var parsed = Regex.Replace(str, @"\.(?=[^""]*"":)", "_");   //i.e. replace dot with underscore when found as a json property name { "property.name": "don't.care.what.the.value.is" }
      return JObject.Parse(parsed).ToObject<T>();
}
darksider474
  • 951
  • 8
  • 6