ฉันจะสร้างแบบสอบถามที่ไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่บน Mongodb ได้อย่างไร


93
var thename = 'Andrew';
db.collection.find({'name':thename});

ฉันจะสอบถามกรณีที่ไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่ได้อย่างไร ฉันต้องการค้นหาผลลัพธ์แม้ว่า "andrew";



หมายเหตุสำหรับทุกคนที่จะพยายามใช้คำตอบเกี่ยวกับ regexes: Regexes จำเป็นต้องได้รับการฆ่าเชื้อ
ฌอน

คำตอบ:


126

โซลูชันของ Chris Fulstow จะใช้งานได้ (+1) อย่างไรก็ตามอาจไม่มีประสิทธิภาพโดยเฉพาะอย่างยิ่งหากคอลเล็กชันของคุณมีขนาดใหญ่มาก นิพจน์ทั่วไปที่ไม่ได้รูท (ซึ่งไม่ได้ขึ้นต้นด้วย^ซึ่งยึดนิพจน์ทั่วไปไว้ที่จุดเริ่มต้นของสตริง) และนิพจน์ทั่วไปที่ใช้iแฟล็กสำหรับความไม่ไวต่อตัวพิมพ์จะไม่ใช้ดัชนีแม้ว่าจะมีอยู่ก็ตาม

ตัวเลือกทางเลือกที่คุณอาจพิจารณาคือการ denormalize ข้อมูลของคุณจะเก็บเป็นรุ่นที่ต่ำกว่ากรณีของข้อมูลเช่นเป็นname name_lowerจากนั้นคุณสามารถสืบค้นข้อมูลนั้นได้อย่างมีประสิทธิภาพ (โดยเฉพาะอย่างยิ่งหากมีการจัดทำดัชนี) สำหรับการจับคู่แบบตรงตามตัวพิมพ์เล็กและใหญ่เช่น:

db.collection.find({"name_lower": thename.toLowerCase()})

หรือด้วยการจับคู่คำนำหน้า (นิพจน์ทั่วไปที่รูท) เป็น:

db.collection.find( {"name_lower":
    { $regex: new RegExp("^" + thename.toLowerCase(), "i") } }
);

name_lowerทั้งสองคำสั่งเหล่านี้จะใช้ดัชนีใน


1
คำตอบที่ดีวิธี regex ของฉันช้าลงอย่างมากเมื่อต้องสแกนเอกสารสองสามล้านชุด
Chris Fulstow

34
นี่ไม่ใช่สิ่งที่ถูกต้องอย่างแท้จริงเพราะคุณอาจพบ "แอนดรูว์บางสิ่ง" ขณะที่กำลังมองหา "แอนดรูว์" ดังนั้นให้ปรับ regex เป็น: new RegExp('^'+ username + '$', "i")เพื่อให้ตรงทั้งหมด
Tarion

9
ตามเว็บไซต์ MongoDB regex ที่ไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่จะไม่สามารถจัดทำดัชนีได้อย่างมีประสิทธิภาพ "$ regex สามารถใช้ดัชนีได้อย่างมีประสิทธิภาพเมื่อนิพจน์ทั่วไปมีจุดยึดสำหรับจุดเริ่มต้น (เช่น ^) ของสตริงและเป็นการจับคู่แบบตรงตามตัวพิมพ์เล็กและใหญ่ "
Ryan Schumacher

2
สำหรับ Mongoose สิ่งนี้ใช้ได้กับฉัน: User.find ({'username': {$ regex: new RegExp ('^' + username.toLowerCase (), 'i')}}, function (err, res) {if (err ) โยนผิด; ถัดไป (null, res);});
ChrisRich

5
อย่าลืมหนีชื่อเมื่อทำงานกับนิพจน์ทั่วไป เราไม่ต้องการฉีดเพื่อแย่งชิงความงามของ mongodb ".*"แค่คิดที่คุณใช้รหัสนี้สำหรับหน้าเข้าสู่ระบบและชื่อผู้ใช้เป็น
Tobias

90

คุณต้องใช้นิพจน์ทั่วไปที่ไม่คำนึงถึงขนาดตัวพิมพ์สำหรับนิพจน์นี้เช่น

db.collection.find( { "name" : { $regex : /Andrew/i } } );

ในการใช้รูปแบบ regex จากthenameตัวแปรของคุณให้สร้างวัตถุRegExpใหม่:

var thename = "Andrew";
db.collection.find( { "name" : { $regex : new RegExp(thename, "i") } } );

UPDATE:สำหรับการแข่งขันที่แน่นอนคุณควรใช้ "name": /^Andrew$/iregex ขอบคุณ Yannick L.


7
คุณรู้วิธีทำโดยใช้พังพอน Node.js หรือไม่?
user847495

1
ฉันสงสัยว่าสิ่งนี้จะทำงานกับคอลเลกชันขนาดใหญ่ได้ดีเพียงใด คุณจะสูญเสียประโยชน์ของ functinon แบบเรียงลำดับ
Wilfred Springer

5
สิ่งนี้ไม่ถูกต้องเอกสารจะตรงกับเอกสารใด ๆที่มี "andrew" สำหรับnameไม่ใช่แค่เท่ากับ
Jonathan Cremin

14
@JonathanCremin เพื่อช่วยเหลือคนที่คุณควรโพสต์คำตอบที่ถูกต้อง:{ "name": /^Andrew$/i }
Yannick Loriot

@ YannickL. 1+ สำหรับการทำสิ่งสามัญสำนึก ฉันแค่เดินผ่านไปไม่ใช่สิ่งที่ฉันกำลังมองหา
Lpc_dark

38

ผมแก้แบบนี้แล้ว

 var thename = 'Andrew';
 db.collection.find({'name': {'$regex': thename,$options:'i'}});

หากคุณต้องการสอบถามเกี่ยวกับ 'การจับคู่แบบตรงตามตัวพิมพ์เล็กและใหญ่' คุณสามารถดำเนินการดังนี้

var thename =  '^Andrew$';
db.collection.find({'name': {'$regex': thename,$options:'i'}});

7

ตอนนี้ MongoDB 3.4 มีความสามารถในการสร้างดัชนีที่ไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่ซึ่งจะเพิ่มความเร็วในการค้นหาแบบไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่บนชุดข้อมูลขนาดใหญ่ สร้างขึ้นโดยการระบุการเรียงที่มีความแข็งแรงเป็น 2

อาจเป็นวิธีที่ง่ายที่สุดคือตั้งค่าการเปรียบเทียบบนฐานข้อมูล จากนั้นคำค้นหาทั้งหมดจะสืบทอดการเรียงลำดับนั้นและจะใช้:

db.createCollection("cities", { collation: { locale: 'en_US', strength: 2 } } )
db.names.createIndex( { city: 1 } ) // inherits the default collation

คุณสามารถทำได้ดังนี้:

db.myCollection.createIndex({city: 1}, {collation: {locale: "en", strength: 2}});

และใช้มันดังนี้:

db.myCollection.find({city: "new york"}).collation({locale: "en", strength: 2});

การดำเนินการนี้จะส่งคืนเมืองที่มีชื่อว่า "new york" "New York" "New york" ฯลฯ

สำหรับข้อมูลเพิ่มเติม: https://jira.mongodb.org/browse/SERVER-90


ความแข็งแรง: 1 เพียงพอสำหรับการจัดทำดัชนีที่ไม่คำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ docs.mongodb.com/manual/reference/collation
Gaurav Ragtah

7
  1. ด้วยพังพอน (และโหนด) สิ่งนี้ได้ผล:

    • User.find({ email: /^name@company.com$/i })

    • User.find({ email: new RegExp(`^ $ {emailVariable} $`, 'i')})

  2. ใน MongoDB สิ่งนี้ใช้ได้:

    • db.users.find({ email: { $regex: /^name@company.com$/i }})

ทั้งสองบรรทัดไม่คำนึงถึงขนาดตัวพิมพ์ อีเมลในฐานข้อมูลอาจเป็นได้NaMe@CompanY.Comและทั้งสองบรรทัดจะยังคงพบวัตถุในฐานข้อมูล

ในทำนองเดียวกันเราสามารถใช้/^NaMe@CompanY.Com$/iและยังคงพบอีเมล: name@company.comในฐานข้อมูล


5

หากต้องการค้นหาสตริงที่ไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่ให้ใช้สิ่งนี้

var thename = "Andrew";
db.collection.find({"name":/^thename$/i})

1
เหตุใดคุณจึงเพิ่มคำตอบที่ซ้ำกันเนื่องจากมีอยู่แล้วในstackoverflow.com/a/7101868/4273915
Shrabanee

4

ฉันเพิ่งแก้ไขปัญหานี้เมื่อไม่กี่ชั่วโมงที่ผ่านมา

var thename = 'Andrew'
db.collection.find({ $text: { $search: thename } });
  • ความไวของตัวพิมพ์และความไวในการกำกับเสียงถูกตั้งค่าเป็นเท็จตามค่าเริ่มต้นเมื่อทำการสืบค้นด้วยวิธีนี้

คุณยังสามารถขยายได้โดยเลือกฟิลด์ที่คุณต้องการจากวัตถุผู้ใช้ของ Andrew โดยทำดังนี้

db.collection.find({ $text: { $search: thename } }).select('age height weight');

อ้างอิง: https://docs.mongodb.org/manual/reference/operator/query/text/#text


1
$ text ทำการค้นหาข้อความในเนื้อหาของฟิลด์ที่จัดทำดัชนีด้วยดัชนีข้อความ
SSH

4

... กับพังพอนบน NodeJS ที่สอบถาม:

const countryName = req.params.country;

{ 'country': new RegExp(`^${countryName}$`, 'i') };

หรือ

const countryName = req.params.country;

{ 'country': { $regex: new RegExp(`^${countryName}$`), $options: 'i' } };

// ^australia$

หรือ

const countryName = req.params.country;

{ 'country': { $regex: new RegExp(`^${countryName}$`, 'i') } };

// ^turkey$

ตัวอย่างโค้ดแบบเต็มใน Javascript, NodeJS พร้อม Mongoose ORM บน MongoDB

// get all customers that given country name
app.get('/customers/country/:countryName', (req, res) => {
    //res.send(`Got a GET request at /customer/country/${req.params.countryName}`);

    const countryName = req.params.countryName;

    // using Regular Expression (case intensitive and equal): ^australia$

    // const query = { 'country': new RegExp(`^${countryName}$`, 'i') };
    // const query = { 'country': { $regex: new RegExp(`^${countryName}$`, 'i') } };
    const query = { 'country': { $regex: new RegExp(`^${countryName}$`), $options: 'i' } };

    Customer.find(query).sort({ name: 'asc' })
        .then(customers => {
            res.json(customers);
        })
        .catch(error => {
            // error..
            res.send(error.message);
        });
});

1

แบบสอบถามต่อไปนี้จะค้นหาเอกสารที่มีสตริงที่ต้องการโดยไม่คำนึงถึงความสำคัญและมีเหตุการณ์ร่วมด้วย

db.collection.find({name:{
                             $regex: new RegExp(thename, "ig")
                         }
                    },function(err, doc) {
                                         //Your code here...
                  });

1

หากต้องการค้นหาสตริงตัวอักษรที่ไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่:

ใช้ regex (แนะนำ)

db.collection.find({
    name: {
        $regex: new RegExp('^' + name.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + '$', 'i')
    }
});

ใช้ดัชนีตัวพิมพ์เล็ก (เร็วกว่า)

db.collection.find({
    name_lower: name.toLowerCase()
});

นิพจน์ทั่วไปช้ากว่าการจับคู่สตริงตามตัวอักษร อย่างไรก็ตามฟิลด์ตัวพิมพ์เล็กเพิ่มเติมจะเพิ่มความซับซ้อนของโค้ดของคุณ เมื่อมีข้อสงสัยให้ใช้นิพจน์ทั่วไป ฉันขอแนะนำให้ใช้เฉพาะฟิลด์ตัวพิมพ์เล็กที่ชัดเจนเท่านั้นหากสามารถแทนที่ฟิลด์ของคุณได้นั่นคือคุณไม่สนใจกรณีนี้ตั้งแต่แรก

โปรดทราบว่าคุณจะต้องออกจากชื่อก่อน regex หากคุณต้องการใช้สัญลักษณ์แทนที่ผู้ใช้ป้อนให้เลือกต่อท้าย.replace(/%/g, '.*')หลังจากการ Escape เพื่อให้คุณสามารถจับคู่ "a%" เพื่อค้นหาชื่อทั้งหมดที่ขึ้นต้นด้วย "a"


1

คุณสามารถใช้Case Insensitive Indexes :

ตัวอย่างต่อไปนี้สร้างคอลเลกชันโดยไม่มีการเปรียบเทียบเริ่มต้นจากนั้นเพิ่มดัชนีในฟิลด์ชื่อด้วยการเปรียบเทียบแบบไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่ International Components สำหรับ Unicode

/*
* strength: CollationStrength.Secondary
* Secondary level of comparison. Collation performs comparisons up to secondary * differences, such as diacritics. That is, collation performs comparisons of 
* base characters (primary differences) and diacritics (secondary differences). * Differences between base characters takes precedence over secondary 
* differences.
*/
db.users.createIndex( { name: 1 }, collation: { locale: 'tr', strength: 2 } } )

ในการใช้ดัชนีแบบสอบถามต้องระบุการเรียงเดียวกัน

db.users.insert( [ { name: "Oğuz" },
                            { name: "oğuz" },
                            { name: "OĞUZ" } ] )

// does not use index, finds one result
db.users.find( { name: "oğuz" } )

// uses the index, finds three results
db.users.find( { name: "oğuz" } ).collation( { locale: 'tr', strength: 2 } )

// does not use the index, finds three results (different strength)
db.users.find( { name: "oğuz" } ).collation( { locale: 'tr', strength: 1 } )

หรือคุณสามารถสร้างคอลเลกชันที่มีการเปรียบเทียบเริ่มต้น:

db.createCollection("users", { collation: { locale: 'tr', strength: 2 } } )
db.users.createIndex( { name : 1 } ) // inherits the default collation

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.