在不区分大小写的情况下有效地查询MongoDB(通过pymongo)

joh*_*eth 21 python case-insensitive mongodb pymongo

我目前正在创建一个python(金字塔)网站,要求用户注册并登录.系统允许用户选择一个用户名,可以是大写字母,小写字母和数字的混合.

确保两个用户不会意外地共享相同的用户名时出现问题,即在我的系统中,"randomUser"应该与"RandomUser"或"randomuser"相同.

不幸的是(在这种情况下)因为Mongo将字符串存储为区分大小写,因此可能会有许多用户使用"相同"的用户名.

我知道查询mongo的不区分大小写的字符串的方法:

db.stuff.find_one({"foo": /bar/i});
Run Code Online (Sandbox Code Playgroud)

但是,这在使用pymongo的查询方法中似乎不起作用:

username = '/' + str(username) + '/i'
response = request.db['user'].find_one({"username":username},{"username":1})
Run Code Online (Sandbox Code Playgroud)

这是构造pymongo查询的正确方法(我假设没有)?

无论何时创建或登录用户帐户,都将使用此查询(因为它必须检查系统中是否存在用户名).我知道这不是最有效的查询,所以它是否仅用于登录或帐户创建是否重要?是否更愿意做一些事情,比如强迫用户只选择小写的用户名(完全不需要不区分大小写的查询)?

dcr*_*sta 41

PyMongo使用本机python正则表达式,就像mongo shell使用本机javascript正则表达式一样.要编写您在上面的shell中编写的内容的等效查询,您将使用:

db.stuff.find_one({'name': re.compile(username, re.IGNORECASE)})
Run Code Online (Sandbox Code Playgroud)

请注意,这将避免使用name字段上可能存在的任何索引.不区分大小写的搜索或排序的常见模式是在文档中包含第二个字段,例如name_lower,每次name更改时始终设置该字段(name在本例中为较低版本的字段).然后,您将查询如下文档:

db.stuff.find_one({'name_lower': username.lower()})
Run Code Online (Sandbox Code Playgroud)

  • 更安全的查询是:`db.stuff.find_one({'name':re.compile('^'+ re.escape(username)+'$',re.IGNORECASE)})` (10认同)
  • 如果"username"变量包含正则表达式元字符,那不是问题吗? (2认同)

Mik*_*kko 6

接受的答案是危险的,它将匹配任何包含用户名的字符串!安全选项是匹配确切的字符串:

import re
db.stuff.find_one({'name': re.compile('^' + username + '$', re.IGNORECASE)})
Run Code Online (Sandbox Code Playgroud)

更安全的是,转义任何可能影响正则表达式匹配的特殊字符的变量:

import re
db.stuff.find_one({'name': re.compile('^' + re.escape(username) + '$', re.IGNORECASE)}) 
Run Code Online (Sandbox Code Playgroud)

  • 如果 db 中的所有名称值都是小写并且“username”变量具有不同的大小写,则“username.lower()”将起作用。如果 db 值大小写不同,则需要正则表达式 (3认同)