För den här handledningen kommer vi att använda den official dummy dataset
, som innehåller många restaurangdokument från hela New York-området.
Här är ett exempel på den grundläggande dokumentstrukturen i denna samling, med hjälp av .findOne()
metod:
> 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"
}
Faktens kraft
Den viktigaste pusselbiten när du söker inom en MongoDB-samling är den enkla men flexibla db.collection.find()
metod.
Med .find()
, kan du enkelt fråga en samling dokument genom att skicka några enkla parametrar och returnera en cursor
. En cursor
är helt enkelt en resultatuppsättning och kan itereras igenom för att manipulera eller på annat sätt använda dokumenten som pekas på av cursor
.
Som ett enkelt exempel på .find()
metod kommer vi att försöka hitta alla restauranger i vår samling som servern Hamburgers
som deras 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" }
...
Resultatuppsättningen är ganska stor, så ett bättre mått för våra testexempel skulle vara att kedja .count()
metod till .find()
för att helt enkelt se hur många dokument som matchade vår fråga:
> db.restaurants.find( { cuisine: "Hamburgers" } ).count()
433
Det är många hamburgare!
Söka efter ordlikheter med regex
Nu när vi använder .find()
för att fråga efter vår samling kan vi faktiskt ändra vår syntax lite och börja söka efter matchningar baserat på ett ord eller en fras som kan vara en delvis matcha inom ett givet fält, liknande LIKE
operatör för SQL-motorer.
Tricket är att använda regular expressions
(eller regex
för kort), vilket i grunden är en textsträng som definierar ett sökmönster. Det finns ett antal regex
motorer som är skrivna i lite olika syntax, men grunderna är i princip desamma, och i det här fallet använder MongoDB Perl Regex (PCRE)
motor.
På den mest grundläggande nivån, ett regex
uttryck är en sträng (serie av tecken) omgiven på båda sidor av ett enda snedstreck (/
).
Till exempel, om vi vill använda regex
för att utföra samma fråga som ovan och ta reda på hur många restauranger som serverar Hamburgers
, vi kan ersätta vår sträng "Hamburgers"
med /Hamburgers/
istället:
> db.restaurants.find( { cuisine: /Hamburgers/ } ).count()
433
Nyfikna observatörer kanske inser att vi i praktiken inte har ändrat någonting i själva frågan vi utför – vi letar fortfarande helt enkelt upp alla dokument där cuisine
fältet är lika med strängen "Hamburgers"
.
Som sagt, genom att helt enkelt använda regex
istället för en vanlig "citerad sträng" kan vi börja leta efter partiella ord/frasmatchningar istället.
Låt oss till exempel titta på borough
för att få en bättre uppfattning om hur detta fungerar. Först kommer vi att märka att det finns sex stadsdelar totalt inom vår samling:
> db.restaurants.distinct('borough')
[
"Brooklyn",
"Bronx",
"Manhattan",
"Queens",
"Staten Island",
"Missing"
]
Låt oss nu använda regex
för att ta reda på hur många restauranger som finns i Bronx
stadsdel:
> db.restaurants.find( { borough: /Bronx/ } ).count()
2338
Men tänk dig att vi vill hitta antalet restauranger där borough
börjar med de tre första tecknen "Bro"
. Vi skulle ändra vårt regex
väldigt lite, som så:
> db.restaurants.find( { borough: /^Bro/ } ).count()
8424
Vi ser över 6 000 ytterligare dokument i denna resultatuppsättning, vilket är vettigt eftersom vi inte bara får resultat där borough
är är "Bronx"
, men också allt för "Brooklyn"
likaså.
Caret-tecknet (^
) anger platsen i vår sträng som ska vara början , så om vi hade ett dokument där de tre bokstäverna stod mitt i fältet, skulle vi inte få en matchning.
Som ett annat snabbt exempel, låt oss söka var som helst i fältet för tecknen "at"
, vilket borde ge oss resultat för både "Manhattan"
och "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
Visst har vår sista fråga kombinerat de två resultatuppsättningarna till en.
Du kanske märker att även om våra tecken "AT"
är versaler i vårt regex
sträng, men de är gemener i de faktiska dokumentposterna returnerade vi fortfarande resultat. Detta beror på att vi också har lagt till den speciella i
flagga efter vårt regex avslutande snedstreck (/
). Detta informerar regex
motor som vi vill att sökningen ska vara case insensitive
, matchande oavsett versaler eller gemener.