За този урок ще използваме official dummy dataset
, който съдържа множество документи за ресторанти от района на Ню Йорк.
Ето пример за основната структура на документа в тази колекция, използвайки .findOne()
метод:
> db.restaurants.findOne()
{
"_id" : ObjectId("56c651e7d84ccfde319961af"),
"address" : {
"building" : "469",
"coord" : [
-73.961704,
40.662942
],
"street" : "Flatbush Avenue",
"zipcode" : "11225"
},
"borough" : "Brooklyn",
"cuisine" : "Hamburgers",
"grades" : [
{
"date" : ISODate("2014-12-30T00:00:00Z"),
"grade" : "A",
"score" : 8
},
{
"date" : ISODate("2014-07-01T00:00:00Z"),
"grade" : "B",
"score" : 23
},
{
"date" : ISODate("2013-04-30T00:00:00Z"),
"grade" : "A",
"score" : 12
},
{
"date" : ISODate("2012-05-08T00:00:00Z"),
"grade" : "A",
"score" : 12
}
],
"name" : "Wendy'S",
"restaurant_id" : "30112340"
}
Силата на намирането
Най-важната част от пъзела при търсене в колекция MongoDB е простият, но гъвкав db.collection.find()
метод.
С .find()
, можете лесно да заявите колекция от документи, като подадете няколко прости параметъра и върнете cursor
. cursor
е просто набор от резултати и може да бъде повторен, за да манипулира или по друг начин да използва документите, към които се сочи от cursor
.
Като прост пример за .find()
метод в действие, ще се опитаме да намерим всички ресторанти в нашата колекция, които сървърът на Hamburgers
като тяхната cuisine
:
>db.restaurants.find( { cuisine: "Hamburgers" } )
{ "_id" : ObjectId("56c651e7d84ccfde319961af"), "address" : { "building" : "469", "coord" : [ -73.961704, 40.662942 ], "street" : "Flatbush Avenue", "zipcode" : "11225" }, "borough" : "Brooklyn", "cuisine" : "Hamburgers", "grades" : [ { "date" : ISODate("2014-12-30T00:00:00Z"), "grade" : "A", "score" : 8 }, { "date" : ISODate("2014-07-01T00:00:00Z"), "grade" : "B", "score" : 23 }, { "date" : ISODate("2013-04-30T00:00:00Z"), "grade" : "A", "score" : 12 }, { "date" : ISODate("2012-05-08T00:00:00Z"), "grade" : "A", "score" : 12 } ], "name" : "Wendy'S", "restaurant_id" : "30112340" }
...
Резултатът е доста голям, така че по-добро измерване за нашите тестови примери би било да се свърже .count()
метод върху .find()
за да видите просто колко документа отговарят на нашата заявка:
> db.restaurants.find( { cuisine: "Hamburgers" } ).count()
433
Това са много бургери!
Търсене на прилики на думи с помощта на Regex
Сега, когато използваме .find()
за да потърсим нашата колекция, всъщност можем да модифицираме нашия синтаксис много леко и да започнем да търсим съвпадения въз основа на дума или фраза, която може да е частична съвпадение в дадено поле, подобно на LIKE
оператор за SQL машини.
Номерът е да се използват regular expressions
(или regex
накратко), което е основно текстов низ, който дефинира модел за търсене. Има няколко regex
двигатели, които са написани в малко по-различен синтаксис, но основите са по същество еднакви и в този случай MongoDB използва Perl Regex (PCRE)
двигател.
На най-основното ниво, regex
изразът е низ (поредица от знаци), затворен от двете страни с една наклонена черта (/
).
Например, ако искаме да използваме regex
за да извършите същата заявка като по-горе и да разберете колко ресторанта сервират Hamburgers
, можем да заменим нашия низ "Hamburgers"
с /Hamburgers/
вместо това:
> db.restaurants.find( { cuisine: /Hamburgers/ } ).count()
433
Запалените наблюдатели може да разберат, че на практика не сме променили нищо относно действителната заявка, която изпълняваме – ние все още просто търсим всички документи, където cuisine
поле еравно на низът "Hamburgers"
.
Това каза, просто като използвате regex
вместо нормален „низ в кавички“, можем да започнем да търсим частични съвпадения на дума/фраза вместо това.
Например, нека разгледаме borough
поле, за да добиете по-добра представа как работи това. Първо ще забележим, че в нашата колекция има общо шест района:
> db.restaurants.distinct('borough')
[
"Brooklyn",
"Bronx",
"Manhattan",
"Queens",
"Staten Island",
"Missing"
]
Сега нека използваме regex
за да разберете колко ресторанта има в Bronx
квартал:
> db.restaurants.find( { borough: /Bronx/ } ).count()
2338
Но представете си, че искаме да намерим броя на ресторантите, където borough
започва с първите три знака "Bro"
. Бихме променили нашия regex
съвсем леко, така:
> db.restaurants.find( { borough: /^Bro/ } ).count()
8424
Виждаме над 6000 допълнителни документа в този набор от резултати, което има смисъл, защото не само получаваме резултати там, където borough
е "Bronx"
, но и всичко за "Brooklyn"
също.
Знакът на карета (^
) определя местоположението в нашия низ, което трябва да е началото , така че ако имахме документ, в който тези три букви бяха в средата на полето, нямаше да получим съвпадение.
Като друг бърз пример, нека търсим навсякъде в полето за знаците "at"
, което трябва да ни даде резултати и за двата "Manhattan"
и "Staten Island"
:
> db.restaurants.find( { borough: /Manhattan/ } ).count()
10259
> db.restaurants.find( { borough: /Staten Island/ } ).count()
969
> db.restaurants.find( { borough: /AT/i } ).count()
11228
Разбира се, нашата последна заявка е комбинирала двата резултата в един.
Може да забележите, че въпреки че нашите знаци "AT"
са с главни букви в нашия regex
низ, но те са с малки букви в действителните записи на документи, ние все още връщахме резултати. Това е така, защото добавихме и специалния i
флаг след затварящата наклонена черта на нашия редовен израз (/
). Това информира regex
машина, която искаме търсенето да е case insensitive
, съвпадащи независимо от главни или малки букви.