似乎mongo不允许插入带点(。)或美元符号($)的键,但是当我使用mongoimport工具导入包含点的JSON文件时,它工作正常。司机抱怨试图插入该元素。
这是文档在数据库中的样子:
{
"_id": {
"$oid": "..."
},
"make": "saab",
"models": {
"9.7x": [
2007,
2008,
2009,
2010
]
}
}
我这样做是错误的,不应该使用像外部数据那样的哈希映射(即模型),还是可以以某种方式逃避点?也许我在想类似Javascript。
答案 0 :(得分:56)
MongoDB不支持keys with a dot,因此您必须预先处理JSON文件以在导入之前删除/替换它们,否则您将为各种问题做好准备。< / p>
此问题没有标准的解决方法,最好的方法过于依赖于具体情况。但是如果可能的话,我会避免使用任何关键的编码器/解码器方法,因为你会继续为此永久性地付出不便,因为JSON重组可能是一次性成本。
答案 1 :(得分:18)
Mongo docs建议将$
和.
等非法字符替换为其unicode等效字符。
在这些情况下,密钥需要替换保留的$和。字符。任何字符都足够了,但考虑使用Unicode全宽等价:U + FF04(即“$”)和U + FF0E(即“。”)。
答案 2 :(得分:15)
正如其他答案所述,由于restrictions on field names,MongoDB不允许$
或.
个字符作为地图密钥。但是,如Dollar Sign Operator Escaping 中所述,此限制不会阻止您使用此类密钥插入文档,它只会阻止您更新或查询它们。
简单地将.
替换为[dot]
(如评论中所述)的问题是,当用户合法地想要存储密钥[dot]
时会发生什么?
Fantom's afMorphia driver采用的方法是使用类似于Java的unicode转义序列,但要确保首先转义任何转义字符。本质上,将进行以下字符串替换(*):
\ --> \\
$ --> \u0024
. --> \u002e
当随后从 MongoDB中读取时,会进行反向替换。
或Fantom代码:
Str encodeKey(Str key) {
return key.replace("\\", "\\\\").replace("\$", "\\u0024").replace(".", "\\u002e")
}
Str decodeKey(Str key) {
return key.replace("\\u002e", ".").replace("\\u0024", "\$").replace("\\\\", "\\")
}
用户需要了解此类转换的唯一时间是构建对此类密钥的查询。
鉴于将dotted.property.names
存储在数据库中用于配置目的,我认为这种方法比简单地禁止所有这些地图密钥更为可取。
(*)afMorphia实际上执行Unicode escape syntax in Java中提到的完整/正确的unicode转义规则,但所描述的替换序列也同样有效。
答案 3 :(得分:10)
您可以尝试在密钥中使用散列而不是值,然后将该值存储在JSON值中。
var crypto = require("crypto");
function md5(value) {
return crypto.createHash('md5').update( String(value) ).digest('hex');
}
var data = {
"_id": {
"$oid": "..."
},
"make": "saab",
"models": {}
}
var version = "9.7x";
data.models[ md5(version) ] = {
"version": version,
"years" : [
2007,
2008,
2009,
2010
]
}
然后,您可以稍后使用哈希访问模型。
var version = "9.7x";
collection.find( { _id : ...}, function(e, data ) {
var models = data.models[ md5(version) ];
}
答案 4 :(得分:9)
我刚刚实现的解决方案,我非常满意,包括将密钥名称和值拆分为两个单独的字段。这样,我可以保持角色完全相同,而不用担心任何解析噩梦。该文档看起来像:
{
...
keyName: "domain.com",
keyValue: "unregistered",
...
}
您仍然可以通过在字段 keyName 和 keyValue 上执行find
来轻松查询此内容。
所以而不是:
db.collection.find({"domain.com":"unregistered"})
实际上不会按预期工作,你会运行:
db.collection.find({keyName:"domain.com", keyValue:"unregistered"})
它将返回预期的文件。
答案 5 :(得分:9)
MongoDB的最新稳定版本(v3.6.1)现在支持键或字段名称中的点(。)。
Field names can contain dots (.) and dollar ($) characters now
答案 6 :(得分:4)
来自MongoDB docs“the'。'字符不得出现在键名称中的任何位置“。看起来你必须想出一个编码方案,或者没有。
答案 7 :(得分:3)
你需要逃脱钥匙。由于似乎大多数人不知道如何正确地逃避字符串,这里是步骤:
另外,请记住mongo也不允许键以'$'开头,所以你必须做类似的事情
以下是一些代码:
class MyTextStripper : PDFTextStripper
{
protected override void processTextPosition(TextPosition text)
{
base.processTextPosition(text);
Console.WriteLine("X: " + text.getX() +
" y: " + text.getY() +
" height: " + text.getHeight() +
" width: " + text.getWidth() +
" word: " + text.getCharacter());
}
}
class Program
{
static void Main(string[] args)
{
ExtractTextFromPdf(@"C:\Users\Desktop\mathml88.pdf");
}
private static string ExtractTextFromPdf(string path)
{
PDDocument doc = null;
try
{
doc = PDDocument.load(path);
MyTextStripper stripper = new MyTextStripper();
return stripper.getText(doc);
}
finally
{
if (doc != null)
{
doc.close();
}
}
}
}
答案 8 :(得分:3)
迟到的答案,但如果您使用Spring和Mongo,Spring可以使用Values = new ChartValues<double> { test }
为您管理转换。这是JohnnyHK的解决方案,但由Spring处理。
MappingMongoConverter
如果您存储的Json是:
@Autowired
private MappingMongoConverter converter;
@PostConstruct
public void configureMongo() {
converter.setMapKeyDotReplacement("xxx");
}
通过Spring(MongoClient),它将被读作:
{ "axxxb" : "value" }
答案 9 :(得分:1)
正如另一个用户提到的那样,编码/解码在将来可能会成为问题,因此替换所有带点的键可能更容易。这是我使用'。'替换键的递归函数。发生次数:
def mongo_jsonify(dictionary):
new_dict = {}
if type(dictionary) is dict:
for k, v in dictionary.items():
new_k = k.replace('.', '-')
if type(v) is dict:
new_dict[new_k] = mongo_jsonify(v)
elif type(v) is list:
new_dict[new_k] = [mongo_jsonify(i) for i in v]
else:
new_dict[new_k] = dictionary[k]
return new_dict
else:
return dictionary
if __name__ == '__main__':
with open('path_to_json', "r") as input_file:
d = json.load(input_file)
d = mongo_jsonify(d)
pprint(d)
您也可以修改此代码以替换'$',因为那是mongo不允许在键中使用的另一个字符。
答案 10 :(得分:1)
有一种难看的查询方式不建议在应用程序中使用,而不是用于调试目的(仅适用于嵌入式对象)
db.getCollection('mycollection').aggregate([
{$match: {mymapfield: {$type: "object" }}}, //filter objects with right field type
{$project: {mymapfield: { $objectToArray: "$mymapfield" }}}, //"unwind" map to array of {k: key, v: value} objects
{$match: {mymapfield: {k: "my.key.with.dot", v: "myvalue"}}} //query
])
答案 11 :(得分:1)
现在受支持
MongoDb 3.6 及更高版本在字段名称中同时支持点和美元。 参见下面的JIRA:https://jira.mongodb.org/browse/JAVA-2810
将Mongodb升级到3.6+听起来是最好的方法。
答案 12 :(得分:1)
并不完美,但是在大多数情况下都可以使用:用其他东西替换禁止使用的字符。由于它是密钥,因此这些新字符应该相当少。
import android.support.design.widget.BottomNavigationView;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.widget.TabHost;
import adapters.MyHorizontalAdapter;
import adapters.MyVerticalAdapter;
import models.ModelVertical;
import models.ModelsHorizontal;
public class MainActivity extends AppCompatActivity {
RecyclerView verticalRecyclerView , horizontalRecyclerView;
TabHost host;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
verticalRecyclerView = findViewById(R.id.verticalRecycler);
horizontalRecyclerView = findViewById(R.id.storiesRecycler);
BottomNavigationView bottomNavigationView= findViewById(R.id.bottomNv);
bottomNavigationView.setItemIconSize(100);
host = findViewById(R.id.tabHost);
host.setup();
TabHost.TabSpec spec = host.newTabSpec("Messages");
spec.setContent(R.id.tab1);
spec.setIndicator("Messages");
host.addTab(spec);
host.setHorizontalScrollBarEnabled(true);
host.setup();
spec = host.newTabSpec("Active");
spec.setContent(R.id.tab2);
spec.setIndicator("Active");
host.addTab(spec);
spec = host.newTabSpec("Groups");
spec.setContent(R.id.tab3);
spec.setIndicator("Groups");
host.addTab(spec);
spec = host.newTabSpec("Calls");
spec.setContent(R.id.tab4);
spec.setIndicator("Calls");
host.addTab(spec);
// ***** Assigning Vertical Recycler Data
ModelVertical[] modelVerticals = {new ModelVertical(R.drawable.ahmed ,
"Ahmed" , "Mahmoud It's Me Ahmed " , "Thursday" , R.drawable.ahmed),
new ModelVertical(R.drawable.aly , "Aly" , "Hello Mahmous It's Me Aly" ,
"Tuesday" , R.drawable.aly),
new ModelVertical(R.drawable.elaayek , "Elaayeek" , "Hello Mahmoud It's Me
Elaayek" , "Monday" , R.drawable.elaayek),
new ModelVertical(R.drawable.fathy , " Fathy" , "Hello Mahmoud It's Me
Fathy", "Sunday" , R.drawable.fathy),
new ModelVertical(R.drawable.samy , " Samy" , "Hello Mahmoud It's Me
Samy", "Saturday" , R.drawable.samy),
new ModelVertical(R.drawable.wael , " Wael" , "Hello Mahmoud It's Me
Wael", "Wednesday" , R.drawable.wael)
};
ModelsHorizontal[] modelsHorizontals = {new
ModelsHorizontal(R.drawable.ic_camera2 , " Add to your story"),
new ModelsHorizontal(R.drawable.ahmed , "Ahmed"),
new ModelsHorizontal(R.drawable.elaayek , " Elaayek "),
new ModelsHorizontal(R.drawable.ibraheem , " Ibrahim" ) ,
new ModelsHorizontal(R.drawable.elaayek , " Elaayek "),
new ModelsHorizontal(R.drawable.ibraheem , " Ibrahim" ) ,
new ModelsHorizontal(R.drawable.elaayek , " Elaayek "),
new ModelsHorizontal(R.drawable.ibraheem , " Ibrahim" ) ,
new ModelsHorizontal(R.drawable.elaayek , " Elaayek "),
new ModelsHorizontal(R.drawable.ibraheem , " Ibrahim" ) ,
new ModelsHorizontal(R.drawable.elaayek , " Elaayek "),
new ModelsHorizontal(R.drawable.ibraheem , " Ibrahim" ) ,
new ModelsHorizontal(R.drawable.elaayek , " Elaayek "),
new ModelsHorizontal(R.drawable.ibraheem , " Ibrahim" ) ,
new ModelsHorizontal(R.drawable.elaayek , " Elaayek "),
new ModelsHorizontal(R.drawable.ibraheem , " Ibrahim" ) ,
new ModelsHorizontal(R.drawable.elaayek , " Elaayek "),
new ModelsHorizontal(R.drawable.ibraheem , " Ibrahim" ) ,
new ModelsHorizontal(R.drawable.elaayek , " Elaayek "),
new ModelsHorizontal(R.drawable.ibraheem , " Ibrahim" ) ,
new ModelsHorizontal(R.drawable.elaayek , " Elaayek "),
new ModelsHorizontal(R.drawable.ibraheem , " Ibrahim" ) ,
new ModelsHorizontal(R.drawable.elaayek , " Elaayek "),
new ModelsHorizontal(R.drawable.ibraheem , " Ibrahim" ) ,
new ModelsHorizontal(R.drawable.elaayek , " Elaayek "),
new ModelsHorizontal(R.drawable.ibraheem , " Ibrahim" ) ,
new ModelsHorizontal(R.drawable.omar , "Omar")
};
verticalRecyclerView.setLayoutManager(new LinearLayoutManager(this)
MyVerticalAdapter myAdapter = new MyVerticalAdapter(modelVerticals);
verticalRecyclerView.setAdapter(myAdapter);
horizontalRecyclerView.setLayoutManager(new LinearLayoutManager(this ,
LinearLayoutManager.HORIZONTAL , false));
MyHorizontalAdapter myHorizontalAdapter = new
MyHorizontalAdapter(modelsHorizontals);
horizontalRecyclerView.setAdapter(myHorizontalAdapter);
}
}
这是一个测试:
/** This will replace \ with ⍀, ^$ with '₴' and dots with ⋅ to make the object compatible for mongoDB insert.
Caveats:
1. If you have any of ⍀, ₴ or ⋅ in your original documents, they will be converted to \$.upon decoding.
2. Recursive structures are always an issue. A cheap way to prevent a stack overflow is by limiting the number of levels. The default max level is 10.
*/
encodeMongoObj = function(o, level = 10) {
var build = {}, key, newKey, value
//if (typeof level === "undefined") level = 20 // default level if not provided
for (key in o) {
value = o[key]
if (typeof value === "object") value = (level > 0) ? encodeMongoObj(value, level - 1) : null // If this is an object, recurse if we can
newKey = key.replace(/\\/g, '⍀').replace(/^\$/, '₴').replace(/\./g, '⋅') // replace special chars prohibited in mongo keys
build[newKey] = value
}
return build
}
/** This will decode an object encoded with the above function. We assume the structure is not recursive since it should come from Mongodb */
decodeMongoObj = function(o) {
var build = {}, key, newKey, value
for (key in o) {
value = o[key]
if (typeof value === "object") value = decodeMongoObj(value) // If this is an object, recurse
newKey = key.replace(/⍀/g, '\\').replace(/^₴/, '$').replace(/⋅/g, '.') // replace special chars prohibited in mongo keys
build[newKey] = value
}
return build
}
和结果-请注意,这些值未修改:
var nastyObj = {
"sub.obj" : {"$dollar\\backslash": "$\\.end$"}
}
nastyObj["$you.must.be.kidding"] = nastyObj // make it recursive
var encoded = encodeMongoObj(nastyObj, 1)
console.log(encoded)
console.log( decodeMongoObj( encoded) )
答案 13 :(得分:1)
我在JavaScript中为每个对象键使用以下转义:
key.replace(/\\/g, '\\\\').replace(/^\$/, '\\$').replace(/\./g, '\\_')
我喜欢的是它在开头只替换$
,并且它不使用在控制台中使用的unicode字符。 _
对我来说比unicode角色更具可读性。它也不会将一组特殊字符($
,.
)替换为另一组(unicode)。但适当地逃避了传统的\
。
答案 14 :(得分:0)
给你我的提示:你可以使用JSON.stringify来保存对象/数组包含键名称有点,然后解析字符串到对象用JSON.parse处理从数据库获取数据时
另一种解决方法: 重构您的架构,如:
key : {
"keyName": "a.b"
"value": [Array]
}
答案 15 :(得分:0)
最新的MongoDB确实支持带点的键,但java MongoDB驱动程序不支持。因此,为了使它在Java中工作,我从github repo of java-mongo-driver中提取代码并在其isValid Key函数中相应地进行了更改,从中创建了新jar,现在使用它。
答案 16 :(得分:0)
将点(.
)或美元($
)替换为真实文档中永远不会使用的其他字符。并在检索文档时恢复点(.
)或美元($
)。该策略不会影响用户阅读的数据。
您可以从all characters中选择字符。
答案 17 :(得分:0)
您可以按原样存储它,然后转换为漂亮的
我在Livescript上写了这个例子。您可以使用livescript.net网站来评估它
test =
field:
field1: 1
field2: 2
field3: 5
nested:
more: 1
moresdafasdf: 23423
field3: 3
get-plain = (json, parent)->
| typeof! json is \Object => json |> obj-to-pairs |> map -> get-plain it.1, [parent,it.0].filter(-> it?).join(\.)
| _ => key: parent, value: json
test |> get-plain |> flatten |> map (-> [it.key, it.value]) |> pairs-to-obj
它会产生
{"field.field1":1,
"field.field2":2,
"field.field3":5,
"field.nested.more":1,
"field.nested.moresdafasdf":23423,
"field3":3}
答案 18 :(得分:0)
奇怪的是,使用mongojs,如果我自己设置_id,我可以创建一个带点的文档,但是生成_id时却无法创建文档:
有效吗
db.testcollection.save({"_id": "testdocument", "dot.ted.": "value"}, (err, res) => {
console.log(err, res);
});
不起作用:
db.testcollection.save({"dot.ted": "value"}, (err, res) => {
console.log(err, res);
});
我首先想到dat用点键更新文档也可以,但是它将点识别为子键!
看看mongojs如何处理点(子键),我将确保我的键不包含点。
答案 19 :(得分:0)
Lodash pairs将允许您更改
{ 'connect.sid': 's:hyeIzKRdD9aucCc5NceYw5zhHN5vpFOp.0OUaA6' }
到
[ [ 'connect.sid',
's:hyeIzKRdD9aucCc5NceYw5zhHN5vpFOp.0OUaA6' ] ]
使用
var newObj = _.pairs(oldObj);
答案 20 :(得分:0)
就像@JohnnyHK所说的那样,请删除标点符号或“。”。因为您的数据开始累积到更大的数据集中时,它将产生更大的问题。这将引起问题,尤其是当您调用诸如$ merge之类的聚合运算符时,该运算符需要访问和比较会引发错误的键。我已经学到了很难的方法,对于那些刚入门的人,请不要重复。
答案 21 :(得分:0)
对于PHP,我将HTML值替换为句点。那是"."
。
它存储在MongoDB中:
"validations" : {
"4e25adbb1b0a55400e030000" : {
"associate" : "true"
},
"4e25adb11b0a55400e010000" : {
"associate" : "true"
}
}
和PHP代码......
$entry = array('associate' => $associate);
$data = array( '$set' => array( 'validations.' . str_replace(".", `"."`, $validation) => $entry ));
$newstatus = $collection->update($key, $data, $options);
答案 22 :(得分:-1)
/home/user/anaconda3/lib/python3.6/site-packages/pymongo/collection.py
在错误消息中找到它。如果您使用anaconda
(如果没有,请查找通讯文件),只需将上述文件中的值从check_keys = True
更改为False
即可。那会有用的!