MongoDB text search string equality? - mongodb

I'm new to MongoDB and was looking through the docs a few nights ago and saw something that I haven't been able to locate since...
There was an option, I believe it was related to $text search, to treat an array of strings as if they were the same word. The syntax looked something like this:
["cheez", "cheese"],
["donut", "doughnut"],
["chips", "fries", "crisps"],
So a search for "chips" would return all documents indexed with "fries" or "crisps" even if they did not also have "chips".
Please tell me I wasn't dreaming!

YOU ARE NOT DREAMING
mongodb fuzzy text search
The following query searches the title field for the phrase naw yark. It uses the fuzzy default options where:
maxEdits allows up to two character variation of each term in the
given phrase to match the query to a document.
maxExpansions considers up to fifty similar terms for each term in
naw yark to find matches.
prefixLength is disabled.
The query also includes a $limit stage to limit the output to 10 results and a $project stage to:
Exclude all fields except title
Add a field named score
db.movies.aggregate([
{
$search: {
"text": {
"path": "title",
"query": "naw yark",
"fuzzy": {}
}
}
},
{
$limit: 10
},
{
$project: {
"_id": 0,
"title": 1,
score: { $meta: "searchScore" }
}
}
])
The above query returns the following results:
{ "title" : "New York, New York", "score" : 4.392756462097168 }
{ "title" : "New York", "score" : 4.050914287567139 }
{ "title" : "New York Stories", "score" : 3.4838104248046875 }
{ "title" : "New York Minute", "score" : 3.4838104248046875 }
{ "title" : "Synecdoche, New York", "score" : 3.4838104248046875 }
{ "title" : "New York Doll", "score" : 3.4838104248046875 }
{ "title" : "Little New York", "score" : 3.4838104248046875 }
{ "title" : "Escape from New York", "score" : 3.0559897422790527 }
{ "title" : "King of New York", "score" : 3.0559897422790527 }
{ "title" : "Naked in New York", "score" : 3.0559897422790527 }
also synonyms:
mongodb synonyms text search

Related

Updating matched array by identifier with multiple names [duplicate]

I have a large DB with various inconsistencies. One of the items I would like to clear up is changing the country status based on the population.
A Sample of the data is:
{ "_id" : "D", "name" : "Deutschland", "pop" : 70000000, "country" : "Large Western" }
{ "_id" : "E", "name" : "Eire", "pop" : 4500000, "country" : "Small Western" }
{ "_id" : "G", "name" : "Greenland", "pop" : 30000, "country" : "Dependency" }
{ "_id" : "M", "name" : "Mauritius", "pop" : 1200000, "country" : "Small island"}
{ "_id" : "L", "name" : "Luxembourg", "pop" : 500000, "country" : "Small Principality" }
Obviously I would like to change the country field go something more uniform, based on population size.
I've tried this approach, but obviously missing some way of tying into an update of the country field.
db.country.updateMany( { case : { $lt : ["$pop" : 20000000] }, then : "Small country" }, { case : { $gte : ["$pop" : 20000000] }, then : "Large country" }
Edit: Posted before I was finished writing.
I was thinking to use $cond functionality, to basically return if true, do X, if false, do y, while using the updateMany.
Is this possible, or is there a workaround?
You really want want bulkWrite() using two "updateMany" statements within it instead. Aggregation expressions cannot be used to do "alternate selection" in any form of update statement.
db.country.bulkWrite([
{ "updateMany": {
"filter": { "pop": { "$lt": 20000000 } },
"update": { "$set": { "country": "Small Country" } }
}},
{ "updateMany": {
"filter": { "pop": { "$gt": 20000000 } },
"update": { "$set": { "country": "Large Country" } }
}}
])
There is still an outstanding "feature request" on SERVER-6566 for "conditional syntax", but this is not yet resolved. The "bulk" API was actually introduced after this request was raised, and really can be adapted as shown to do more or less the same thing.
Also using $out in an aggregation statement as was otherwise suggested is not an option to "update" and can only write to a "new collection" at present. The slated change from MongoDB 4.2 onwards would allow $out to actually "update" an existing collection, however this would only be where the collection to be updated is different from any other collection used within the gathering of data from the aggregation pipeline. So it is not possible to use an aggregation pipeline to update the same collection as what you are reading from.
In short, use bulkWrite().

Perform a search on main collection field and array of objects simultaneously

I have my document structure as below:
{
"codeId" : 8.7628945723895E13, // long numeric value stored in scientific notation by Mongodb
"problemName" : "Hardware Problem",
"problemErrorCode" : "97695686856",
"status" : "active",
"problemDescription" : "ghdsojgnhsdjgh sdojghsdjoghdghd i0dhgjodshgddsgsdsdfghsdfg",
"subProblems" : [
{
"codeId" : 8.76289457238896E14,
"problemName" : "Some problem",
"problemErrorCode" : "57790389503490249640",
"problemDescription" : "This is edited",
"status" : "active",
"_id" : ObjectId("589476eeae39b20b1c15535b")
},
...
]
}
I have a search field which should search by codeId which basically serves as parentCodeID in search fields as shown below
Now, along with parentIdCode I want to search for codeId, problemCode, problemName and problemDescription as well.
How do I query the submodules with a regex search and at same time tag some parent field with "$or" clause etc. to achieve this ?
You can try something like this.
query = {
'$or': [{
"codeId":somevalue
}, {
"subProblems.codeId": {
"$regex": searchValue,
"$options": "i"
}
}, {
//rest of sub modules fields
}]
};

Select LIKE string in MongoDB with Array

I have the following document with this array:
"r" : [{
"id" : "890",
"ca" : "Other CPF Schemes and Priorities",
"su" : "National Day Rally 2015"
}, {
"id" : "1031-52347",
"ca" : "Current Events",
"su" : "Lee Kuan Yew"
}]
and I would like to list all documents where the id has got a dash so document with "id" : "1031-52347" will be returned.
I tried this:
{
r: { id: { $in: [/^-/] } }
}
but not able to get anything.
What would be the correct syntax?
I used this regex:
^[0-9]+-[0-9]+$
Debuggex Demo
You should try this database query:
"r":
{
{ "id": {"$regex" : new RegExp("^[0-9]+-[0-9]+$") } }
}
UPDATE
Working database queries by Blakes Seven
db.mydb.find({ "r.id": { "$regex": "^[0-9]+-[0-9]+$" }})
or
db.mydb.find({ "r.id": /^[0-9]+-[0-9]+$/ })

How to query and update nested arrays

I am building a course system. Each course has multiple sections, each section has multiple steps. My datastructure is as follows:
{
"_id" : "Mtz4DMTwMMKWTWbzE",
"slug" : "how-to-be-awesome",
"title" : "How to be awesome",
"description" : "In 4 easy lessons.",
"createdAt" : ISODate("2014-08-25T13:33:24.675Z"),
"sections" : [
{
"title" : "Be cool",
"description" : "Title says it all really",
"steps" : [
{
"title" : "Wear sunglasses",
"description" : "Always works."
},
{
"title" : "Be funny",
"description" : "Make an occasional joke. But no lame ones."
}
]
}
]
}
This worked while adding steps;
Course._collection.update( { _id: course._id, sections: section }, {
"$push": {
"sections.$.steps": step
}
})
But I can't figure out how to update a step. I tried to give the steps an ID and do it like that, but it's not working, apparently because it's two arrays deep, and you can't have two positionals ($) in a query. I tried something like this:
Course._collection.update( { _id: course._id, 'sections.steps._id': step._id }, {
"$set": {
"sections.steps.$.title": "test updated title"
}
})
But this gave the following error:
can't append to array using string field name: steps
Is there a way to do this? Or is my schema design off?
Thanks!

Modify a document inside an array in MongoDB

Past answers (from mid 2013 and before) don't seem to work and links to the documentation are all out of date.
Example user object:
{
"name": "Joe Bloggs",
"email": "joebloggs#example.com",
"workstations" : [
{ "number" : "10001",
"nickname" : "home" },
{ "number" : "10002",
"nickname" : "work" },
{ "number" : "10003",
"nickname" : "vacation" }
]
}
How can I modify the nickname of a workstation?
I tried using $set, workstations.$ and workstations.nickname but none gave the desired results.
Short answer, you have to use array index. For example, you want to update the nickname of 10002: {$set:{"workstations.1.nickname":"newnickname"}}
Here is the complete example:
> db.test.update({"_id" : ObjectId("5332b7cf4761549fb7e1e72f")},{$set:{"workstations.1.nickname":"newnickname"}})
> db.test.findOne()
{
"_id" : ObjectId("5332b7cf4761549fb7e1e72f"),
"email" : "joebloggs#example.com",
"name" : "Joe Bloggs",
"workstations" : [
{
"number" : "10001",
"nickname" : "home"
},
{
"nickname" : "newnickname",
"number" : "10002"
},
{
"number" : "10003",
"nickname" : "vacation"
}
]
}
>
If you don't know the index (position of the workstations), you can update the doc using $elemMatch:
>db.test.update(
{
"email": "joebloggs#example.com",
"workstations": { "$elemMatch" { "number" : "10002" } }
},
{
"$set": { "workstations.$.nickname": "newnickname2" }
}
)
>
#naimdjon's answer would work. To generalize, you could use the $elemMatch operator in combination with the $ positional operator to update one element in the array using below query:
db.test.update({
// Find the document where name="Joe Bloggs" and the element in the workstations array where number = "10002"
"name": "Joe Bloggs",
"workstations":{$elemMatch:{"number":"10002"}}
},
{
// Update the nickname in the element matched
$set:{"workstations.$.nickname":"newnickname"}
})
Note: $elemMatch is only required if you need to match more than one component in the array. If you are going to match on just the number, you could use "workstations.number":"10002"
As long as you know "which" entry you wish to update then the positional $ operator can be of help. But you need to update your query form:
db.collection.update(
{
"email": "joebloggs#example.com",
"workstations": { "$elemMatch" { "nickname" : "work" } }
},
{
"$set": { "workstations.$.nickname": "new name" }
}
)
So that is the general form. What you need to do here is "match" something in the array in order to get a "position" to use for the update.
Alternately, where you know the position, then you can just "specify" the position with "dot notation":
db.collection.update(
{
"email": "joebloggs#example.com",
},
{
"$set": { "workstations.1.nickname": "new name" }
}
)
Which updates the second element in the array, and does not need the "matching" part in the query.