2011-12-08 15 views
24

Come utilizzare il punto nel nome del campo?Come utilizzare il punto nel nome del campo?

vedo l'errore nell'esempio:

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

can't have . in field names [a.a] 
+0

Che dire ' "a \ .a"'? –

+0

'" a \ .a "' non sembra fare alcuna differenza. La stringa è ancora valutata come '" a.a "' – codr

risposta

3

actualy è possibile utilizzare i punti nelle query. Vedi: http://www.mongodb.org/display/DOCS/Dot+Notation+%28Reaching+into+Objects%29

Questo simbolo di punto speciale indica che non è possibile utilizzarlo nei nomi dei campi. Come non è possibile utilizzare il simbolo del punto negli identificatori nella maggior parte dei linguaggi di programmazione.

È possibile scrivere la query db.test2.find({ "a.a" : "b" }) ma se si desidera essere in grado di scrivere una query di questo tipo è necessario inserire il proprio oggetto in questo modo: db.test2.insert({"a": {"a": "b"}}). Questo creerà il documento con il campo "a" con il valore del documento embeded contenente il campo "a" (di nuovo) con il valore "b".

44

È possibile sostituire i simboli di punti del vostro nome del campo a Unicode equivalente di \uff0E

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

guarda anche:

  1. http://docs.mongodb.org/manual/faq/developers/#faq-dollar-sign-escaping
  2. http://docs.mongodb.org/manual/core/document/#dot-notation
+2

Ho pensato che unicode per periodo era "\ u002e". – William

+3

@William se usi il carattere unicode puro che suggerisci, si trasforma comunque in un periodo ASCII - sarebbe un modo elegante per specificarlo. \ uFFOE è un carattere "FULLWIDTH FULL STOP" e in quanto carattere non ASCII verrà ignorato. Un carattere "ONE DOT LEADER" potrebbe sembrare migliore, poiché è centrato nel suo spazio proprio come il periodo normale. [Ho appena verificato che sui prodotti Apple, il 2024 sembra molto meglio, proprio come in un vero periodo.] –

+0

Ottimo punto usando '\ u002e' ha codificato correttamente la chiave dell'oggetto. tuttavia il recupero è ingombrante: doc [unescape ('field \ u002ename')] non funziona per me. devi fare il unescape in una variabile separata e recuperarlo di conseguenza. Vorrei piuttosto stringificare e salvare il problema sta combattendo contro Mongo Db Driver –

3

È possibile anche scrivere una SONManipulator u canta la libreria pymongo che trasforma i dati in entrata e in uscita da mongodb. Ci sono aspetti negativi; c'è un calo di prestazioni (l'impatto dipende dal tuo caso d'uso) e devi trasformare le tue chiavi quando fai ricerche usando find.

Ecco il codice con un esempio di come usarlo nel commento per la classe KeyTransform:

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 
0
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 

questo metodo ricorsivo sostituisce tutti i caratteri punti da tasti di un dict con \ uff0E come suggerito da Fisk

+0

Dovresti usare: if isinstance (date [key] , dict) per il ciclo –

+0

per il ciclo utilizza già l'elenco delle chiavi disponibili, non penso sia necessario inserirne un altro se il controllo è – tuku

0

Inizialmente ho usato una semplice ricorsione per sostituire tutto "." personaggi con il suo equivalente in Unicode ma ha capito che anche i punti nei valori venivano rimpiazzati. Quindi ho pensato che dovremmo sostituire i punti solo con le chiavi e apportare le modifiche di conseguenza nel caso "if isinstance (input, dict)". Ho pensato che dovrebbe essere una condizione sufficiente per fare la magia, ma ho dimenticato che il valore di dict può anche essere un dict o una lista e poi ho finalmente aggiunto che controlla se il valore di un dict non è una stringa, quindi vai in modo ricorsivo ed è stato finalmente in grado di venire con questa soluzione che alla fine ha fatto il trucco.

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 
+0

Sostituire "input" con "data" perché la variabile è denominata dati. Inoltre, l'uso di 'input' ombreggia la funzione built-in di input() che è una forma scadente. È necessario modificare solo i valori poiché i periodi sono necessari per la selezione degli oggetti all'interno delle chiavi. –

0

ho sostituito il valore della chiave utilizzando myString.replace (". "" \ U2024") prima di inserirla nella JsonObject.

0

Mi sono davvero imbattuto in questo problema durante il tentativo di serializzare i dizionari e in cui il punto incriminato può apparire come un nome di chiave. Modificato per mostrare i riferimenti.

Il rapido e sporco approccio C#:

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>(); 
} 
Problemi correlati