Good evening.
I have this MongoDB document.
This document contains information about M. Allegri, an italian coach. The array coached_Team contains all teams that Allegri coached. In this example there is just one team, but it's just a test; one coach has a lot of team in coached_Team array.
Is there a way to update the palmarès of just one team? For example, Juventus last year won 1 Serie A and 1 Coppa Italia.
This query, clearly, does not work because in that array there will be palmarès of all teams that Allegri coached:
db.coach.update({_id:"MA_1967"}, {$set: {"coached_Team.palmarès.Serie A":33}})
So, I thought to use a normalized model instead of denormalized. But I want to know if there is a way to update the palmarés of Juventus, for example, using this denormalized pattern.
Thank you.
Yes you can using the MongoDB positional operator in your update statement. The $ positional operator has some caveats to read up on but you could write your query like this.
db.coach.update({_id:"MA_1967", 'coached_Team': "Juventus"}, {$set: {"coached_Team.$.palmarès.Serie A":33}})
in order to use the $ positional operator you have to include one and only one array attribute in your find. The $ will then match only the first element in the array that matches the document you are looking for. So your update statement reads something like 'set the first item in coached_Team that matched my array query' then you access the palmares and Serie A attribute through the $ positional operator. The $ and elemMatch operators are very powerful, but unfortunately sometimes very confusing, operators to read up on in MongoDB.
Related
I'm new in mongoDB and I'm facing an issue about performance that need your help. I have a collection with 400k records, when not create index for any field on the collection it takes 20-30s for each query then I create indexs for fields that usually using for search query, but the problem is, when using $regex to search for a string field with index on it, mongoDB does not use index on that field, mongodb still scan for all records in that collection, I've searched on internet with this keyword: "index on regex fields mongodb" and I found some answers which say that "MongoDB use prefix of RegEx to lookup indexes" which means you have to use "^" prefix for the index to work like "db.users.find({name: /^key word/})", but that is not working for me, does "index on $regex field" need MongoDB Atlas to work? because i'm using comunity version of mongoDB. Thanks!
There's a lot to unpack here. We'll split the answer into two parts, the first to try and answer some of the direct questions about index usage and the second to explore solutions to satisfy the application requirements.
Index Usage with $regex
As is true with an index in any database that captures the full string value as the key, MongoDB can use the index for a $regex operation but its efficiency in doing so greatly depends on the regex being applied. That is what the Index Use documentation from the comments and the other answers you reference are describing.
In the comments you mention that an example query might be db.users.find({name: {$regex: '.*keyword.*', $options: 'i'}}). That means that the regex is a both unanchored and case-insensitive. The aforementioned doumentation states directly:
Case insensitive regular expression queries generally cannot use indexes effectively.
Why is this? because the substring that you are searching for can be found in any string value captured by the index. So the document with matching value {name: 'a keyword'} would be located at one end of the index, {name: 'keyWord' }, may be somewhere in the middle, and {name: 'Z keyword'} may be at the end. The only way to ensure correct results is for the database to scan the index for all string values. So while it is still using the index, it may not be efficient as most of the scanned values will not be match and will be discarded.
You may always use .explain() to better understand how the database is answering the query, such as if and how it is using an index.
Solutions
So what do we do about this?
Well as #rickhg12hs suggests in the comments, it depends on exactly what you are trying to achieve. You reiterate that that you are looking for 'full regex search capability', but that is really an approach/solution rather than a goal. If what you really need, for example, is just to match an exact string in a case insensitive manner, then something as simple as a case insensitive index would likely do the trick.
However if truly do wish to perform arbitrary substring searching, then you are really looking at search engine capabilities. In that situation your best bets would probably be to emulate their indexes directly in MongoDB (e.g. have the application manually tokenize the strings to be indexed), stand up something like Solr/Elasticsearch next to MongoDB, or use MongoDB's Atlas Search offering. The $text operator mentioned in the comment has limitations when it comes to substring searching (such as just part of a word), which may or may not be relevant for your needs.
i am confuse.
here is the example:
MongoDB Enterprise > db.employee.find()
result:
{"_id":1002,"name":"Jack","address":{"previous":"Cresent Street","current":"234,Bald Hill Street","unit":"MongoDB" } }
I try this:
db.employee.find({address:{previous: "Cresent Street"}})
result: nothing returns
Next a try this:
db.employee.find({"address.previous": "Cresent Street"})
result:
{"_id":1002,"name":"Jack","address":{"previous":"Cresent Street","current":"234,Bald Hill Street","unit":"MongoDB"}}
The question is wath is wrong with this?
i use
MongoDB shell version v4.2.7 installed
cmd db.version() 4.2.6
debian 10.4
thanks for your replies.
When you Query on Embedded/Nested Documents using dotted field notation
{"address.previous": "Cresent Street"}
means find a document that containd an address field that contains a document whose previous field is equal to "Cresent Street".
When you provide a subdocument like
{address:{previous: "Cresent Street"}}
this means to find a document that contains an address field whose content is exactly the document {previous: "Cresent Street"}, with no additional fields. If you provide multiple fields in the subdocument, field order also matters.
Both of these queries are useful in specific scenarios, pick the one that does what you need in your situaion.
I need to query documents with mongoDb that contain nested arrays. I see a lot of examples using the simple $in operator. The only problem is that I strictly need to check for proper subsets.
Consider the following document.
{data: [[1,2,3], [4,5,6]]}
The query needs to be able to get documents with all of [1,2,3] where 1,2,3 can be in any order, which rules out the following query, because it will only match in the correct order.
{data:{$elemMatch:{$all:[[1,2,3]]}}}
I've also tried nested $elemMatch operators with no success, because the $in operator will return the document even if only one element matches such as the following.
{data:{$elemMatch:{$elemMatch:{$in:[1,4]}}}}
Not sure what your actual query looks like, but this should do what you need:
db.documentDto.find({"some_field":{"$elemMatch":{"$in":[1,2,3]}} })
I haven't got a complete answer (and not much time as its late here) but I would consider
Using aggregation pipeline instead of a query if your not already
Use $unwind operator to deconstruct your nested arrays
Use $sort to sort the contents of the arrays - so you can now compare
Use $match to filter out the arrays which don't fit the array subset values as you can now check based on order.
Use $group to group the result back together based on the _id value
Ref:
http://docs.mongodb.org/manual/reference/operator/aggregation-pipeline/ will give you info on each of the above.
From a quick search I came up with a similar question/example that might be helpful: Mongodb sort inner array
What I'm trying to do:
Filter a field of a collection that matches a given condition. Instead of returning every item in the field (which is an array of items), I only want to see matched items.
Similar to
select items from test where items.histPrices=[10,12]
It is also similar to what's found on the mongodb website here: http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields
Here's what I have been trying:
db.test.save({"name":"record", "items":[{"histPrices":[10,12],"name":"stuff"}]})
db.test.save({"name":"record", "items":[{"histPrices":[10,12],"name":"stuff"},
{"histPrices":[12,13],"name":"stuff"},{"histPrices":[11,14],"name":"stuff"}]})
db.test.find({},{"name":1,"items.histPrices":[10, 12]})
It will return all the objects that have a match for items.histPrices:[10,12], including ALL of the items in items[]. But I don't want the ones that don't match the condition.
From the comments left on Mongodb two years ago, the solution to get only the items with that histPrices[10,12] is to do it with javascript code, namely, loop through the result set and filter out the other items.
I wonder if there's a way to do that with just the query.
Your find query is wrong
db.test.find({},{"name":1,"items.histPrices":[10, 12]})
Your condition statement should be in the first part of the find statement.In your query {} means fetch all documents similar to this sql
select items from test (no where clause)
you have to change your mongodb find to
db.test.find({"items.histPrices":[10, 12]},{"name":1})
make it work
since your items is an array and if you wanted to return only the matching sub item, you have to use positional operator
db.test.find({"items.histPrices":[10, 12]},{"name":1,'items.$':1})
When working with arrays Embedded to the Document, the best approach is the one suggested by Chien-Wei Huang.
I would just add another aggregation, with the $group (in cases the document is very long, you may not want to retrieve all its content, only the array elements) Operator.
Now the command would look like:
db.test.aggregate({$match:{name:"record"}},
{$unwind:"$items"},
{$match {"items.histPrices":[10, 12]}},
{$group: {_id: "$_id",items: {$push: "$items"}}});)
If you are interested to return only one element from the array in each collection, then you should use projection instead
The same kind of issue solved here:
MongoDB Retrieve a subset of an array in a collection by specifying two fields which should match
db.test.aggregate({$unwind:"$items"}, {$match:{"items.histPrices":[10, 12]}})
But I don't know whether the performance would be OK. You have to verify it with your data.
The usage of $unwind
If you want add some filter condition like name="record", just add another $march at first, ex:
db.test.aggregate({$match:{name:"record"}}, {$unwind:"$items"}, {$match:{"items.histPrices":[10, 12]}})
https://jira.mongodb.org/browse/SERVER-828
Get particular element from mongoDB array
MongoDB query to retrieve one array value by a value in the array
I have a hierarchical data structure presenting answers for each question for each exam for each teacher stored in MongoDB like below:
db.foo.insert({name:"teacher1"}); //Done
db.foo.update({name:"teacher1"},{$push:{"exams":{name:"exam1"}}}); //Done
db.foo.update({"exams.name":"exam1"},{$push:{"exams.$.questions":{name:"question1"}}}); //Done
db.foo.update({"exams.questions.name":"question1"},
{$push:{"exams.$.questions.$.answers":{name:"answer1"}}});
// Error => can't append to array using string field name [$]
I appreciate your comments,
You can't use two positional operators. As per :http://www.mongodb.org/display/DOCS/Updating :
The $ operator (by itself) means "position of the matched array item in the query". Use this to find an array member and then manipulate it.
Currently the $ operator only applies to the first matched item in the query.