MongoDB regex string startswith and endswith [duplicate] - json

This question already has answers here:
Regex matching beginning AND end strings
(6 answers)
Regular Expression to find string starts with letter and ends with slash /
(3 answers)
Closed 4 years ago.
So i have something that looks like this
db.usuarios.insert
(
[
{
"nome" : "neymala",
"idade" : 40,
"status" : "solteira"
},
{
"nome" : "gabriel",
"idade" : 31,
"status" : "casado"
},
{
"nome" : "jose",
"idade" : 25,
"status" : "solteiro"
},
{
"nome" : "manoel",
"idade" : 25,
"status" : "solteiro",
"interesses" : [
"esporte",
"musica"
]
}
]
)
I would like to find names that starts with ma and ends with l, for example "manoel" or "manuel"
I have figured out how to do one or the other with the fallowing querys:
db.usuarios.find({nome:{$regex: /^ma/ }})
db.usuarios.find({nome:{$regex: /l$/ }})
Now i would like to combine them into a single query.

You can combine the two requirements into a single regex:
db.usuarios.find({nome: /^ma.*l$/})
In a regex, .* means to match 0 or more of any character. So this regex matches names that start with ma and end with l, ignoring whatever is between.

combine both querys with a AND opetator
db.usuarios.find({
$and:[
{nome:{$regex: /^ma/ }},
{nome:{$regex: /l$/ }}
]
})

In javascript /.../ is a regex, so no $regex needed.
db.usuarios.find({
$and:[
{nome: /^ma/ },
{nome: /l$/ }
]
})
Also note starts with can hit an index. ends with can't. This way you'd better have a selective starts with, otherwise there will be lots of object scans which may occupy extra CPU and possibly slow down your query.

May be this code help you.
you can search small letter string and capital letter string.......

Related

MongoDB, convert element data type from numeric string to number for a large collection (3kk) [duplicate]

This question already has answers here:
MongoDB CursorNotFound Error on collection.find() for a few hundred small records
(2 answers)
Closed 3 years ago.
I have a large 3kk mongodb collection for which i need to convert one element from numeric string to number.
I'm using a mongo-shell script which works for small 100k element collection, please see below the script:
db.SurName.find().forEach(function(tmp){
tmp.NUMBER = parseInt(tmp.NUMBER);
db.SurName.save(tmp);
})
But after a dozen minutes of work I got an error (the error occurs even if the collection is smaller like 1kk):
MongoDB Enterprise Test-shard-0:PRIMARY> db.SurName.find().forEach(function(tmp){
... tmp.NUMBER = parseInt(tmp.NUMBER);
... db.SurName.save(tmp);
... })
2020-01-18T16:59:21.173+0100 E QUERY [js] Error: command failed: {
"operationTime" : Timestamp(1579363161, 14),
"ok" : 0,
"errmsg" : "cursor id 4811116025485863761 not found",
"code" : 43,
"codeName" : "CursorNotFound",
"$clusterTime" : {
"clusterTime" : Timestamp(1579363161, 14),
"signature" : {
"hash" : BinData(0,"EemWWenbArSdh4dTFa0aNcfAPms="),
"keyId" : NumberLong("6748451824648323073")
}
}
} : getMore command failed: {
"operationTime" : Timestamp(1579363161, 14),
"ok" : 0,
"errmsg" : "cursor id 4811116025485863761 not found",
"code" : 43,
"codeName" : "CursorNotFound",
"$clusterTime" : {
"clusterTime" : Timestamp(1579363161, 14),
"signature" : {
"hash" : BinData(0,"EemWWenbArSdh4dTFa0aNcfAPms="),
"keyId" : NumberLong("6748451824648323073")
}
}
} :
_getErrorWithCode#src/mongo/shell/utils.js:25:13
doassert#src/mongo/shell/assert.js:18:14
_assertCommandWorked#src/mongo/shell/assert.js:583:17
assert.commandWorked#src/mongo/shell/assert.js:673:16
DBCommandCursor.prototype._runGetMoreCommand#src/mongo/shell/query.js:802:5
DBCommandCursor.prototype._hasNextUsingCommands#src/mongo/shell/query.js:832:9
DBCommandCursor.prototype.hasNext#src/mongo/shell/query.js:840:16
DBQuery.prototype.hasNext#src/mongo/shell/query.js:288:13
DBQuery.prototype.forEach#src/mongo/shell/query.js:493:12
#(shell):1:1
Is there a way to do this better/right?
EDIT:
The obj schema:
{"_id":{"$oid":"5e241b98c7cab1382c7c9d95"},
"SURNAME":"KOWALSKA",
"SEX":"KOBIETA",
"TERYT":"0201011",
"NUMBER":"51",
"COMMUNES":"BOLESŁAWIEC",
"COUNTIES":"BOLESŁAWIECKI",
"PROVINCES":"DOLNOŚLĄSKIE"
}
The best and fast solution is to use mongodb aggregation with $out operator.
Equivalent to:
insert into new_table
select * from old_table
We convert NUMBER field with $toInt (MongoDB version >= 4.0) operator and store documents in the SurName2 collection. Once we have finished, we just drop old collection and rename SurName2 collection to SurName.
db.SurName.aggregate([
{$addFields:{
NUMBER : {$toInt:"$NUMBER"}
}},
{$out: "SurName2"}
])
Once you check everything is fine, execute these sentences:
db.SurName.drop()
db.SurName2.renameCollection("SurName")
** EDIT - START **
Googling "cursor id not found code 43", yielded this answer: https://stackoverflow.com/a/51602507/2279082
** EDIT - END **
I don't have your data set so I cannot test my answer very well. That being said, you can try to Update the specific field (see about update in the docs: db.collection.update)
So your script will look like this:
db.SurName.find({}, {NUMBER: 1}).forEach(function(tmp){
db.SurName.update({_id: tmp._id}, {$set: {NUMBER: parseInt(tmp.NUMBER)}});
})
Let me know if it helps or if needs an edit

JSONPath regular expression - NOT starting with

My JSON (simplified) looks like this:
[
{"name" : "foobar",
"id" : 123
},
{"name" : "bar",
"id" : 123
},
{"name" : "foobar",
"id" : 456
}, ...
]
I'm using https://jsonpath.herokuapp.com/ to try and find the right JSONPATH syntax to filter out anything not starting with foo, and having id == 123.
Getting it to filter the ones that do start with foo is easy:
$..[?(#.name =~ /foo.*/i)]
This yields the following results:
[
{
"name" : "foobar",
"id" : 123
},
{
"name" : "foobar",
"id" : 456
}
]
I can get rid of the id 456 by adding an additional filter like so:
$..[?(#.name =~ /foo.*/i && #.id==123)]
But how do I do the opposite of getting the name starting with foo? I want all entities that do not start with foo.
I tried something like this:
$..[?(!#.name =~ /foo.*/i && #.id==123)]
Which at least parses as valid JSONPATH, and should negate the filter, but for some reason it still happily only reports the foobar entry:
[
{
"name" : "foobar",
"id" : 123
}
]
How can I achieve a NOT LIKE in JSONPATH?
Thanks!
Regex to identify data not starting with a given string foo:
^([^f]|f[^o]|fo[^o])
If your regex engine supports negative lookahead, that reduces to
^(?!foo)
Note the starting anchor (^) that limits the permissible matching location to the start of the test string.
Your attempt $..[?(!#.name =~ /foo.*/i && #.id==123)] is almost correct. Surround the regex condition with parenthesis before negating with ! like so $..[?(!(#.name =~ /foo.*/i) && #.id==123)]. Tested at https://jsonpath.herokuapp.com/
Edit: This was assuming that you were using Jayway's jsonpath (Java, https://github.com/json-path/JsonPath), but from the documentation link you provided for SmartBear, it looks like it uses the Goessner jsonpath (Javascript, https://goessner.net/articles/JsonPath/). Both, for whatever reason use slightly differing syntaxes.
Thanks to #collapsar for nudging me in the correct direction, in that the key to solving it was in the regular expression (but specifically using the JavaScript Regular Expression syntax, and merging that with the JSONPath syntax).
What actually ended up doing the trick was reading the documentation for JASONPath a bit more careful. It states:
=~
Match a JavaScript regular expression. For example, [?(#.description =~ /cat.*/i)] matches items whose description starts with cat (case-insensitive).
Note: Not supported at locations that use Ready! API 1.1.
The link to Javascript Regular Expression in turn contains the following:
[^xyz]
A negated or complemented character set. That is, it matches anything that is not enclosed in the brackets. You can specify a range of characters by using a hyphen. Everything that works in the normal character set also works here.
For example, [^abc] is the same as [^a-c]. They initially match 'r' in "brisket" and 'h' in "chop."
The resulting expression that works is:
$..[?(#.name =~ /[^foo].*/ && #.id == 123)]
Result:
[
{
"name" : "bar",
"id" : 123
}
]
(I added an additional bar with id 456 to the JSON payload I had, to double-check the filter also worked).

Query a mongodb document containing array [duplicate]

This question already has answers here:
Retrieve only the queried element in an object array in MongoDB collection
(18 answers)
Closed 4 years ago.
I have the followng json file
{
"movies": [{
"title" : "Star Wars: Episode I - The Phantom Menace",
"episode_number" : "1",
"main_characters" : ["Qui-Gon Jinn", "Obi-Wan Kenobi"],
"description" : "The evil Trade Federation, led by Nute Gunray is planning to take over the peaceful world of Naboo. ",
"poster" : "star_wars_episode_1_poster.png",
"hero_image" : "star_wars_episode_1_hero.jpg"
},
{
"title" : "Star Wars: Episode II - Attack of the Clones",
"episode_number" : "2",
"main_characters" : ["Obi-Wan Kenobi", "Anakin Skywalker"],
"description" : "Ten years after the 'Phantom Menace' threatened the planet Naboo, Padmé Amidala is now a Senator representing her homeworld.",
"poster" : "star_wars_episode_2_poster.png",
"hero_image" : "star_wars_episode_2_hero.jpg"
}
]
}
The collection name is fruits
I am trying to extract only the data for episode_number = 2. I have tried following queries
db.fruits.find({"movies": {$elemMatch:{"episode_number": "2"}}}).pretty();
which returns the entire document instead of just the data for episode_number 2.
and
db.fruits.find({"movies.episode_number": "2"}}}).pretty();
which also returns the entire document including that for episode_number 1
I am trying to figure out how to query this document so that only the data for episode_number = 2 is returned.
Please help.
find method takes two parameters: first one represents filtering condition and second one is for projection. To get one element from your array you can use $elemMatch in projection part:
db.fruits.find(
{"movies.episode_number": "2"},
{"movies": {$elemMatch:{"episode_number": "2"}}})
.pretty();
So first parameter filters out your collection and second one filters inner array
If you want all documents in the collection with movies array but it should contain only episode 2 details in it you need to filter the array.
https://docs.mongodb.com/manual/reference/operator/aggregation/filter/
or use $unwind, $match.
db.fruits.aggregate
([
{
$unwind: '$movies'
},
{
$match:{"movies.episode_number":'2'}
}
])

MongoDB queries return no results

I'm having a problem with querying a MongoDB dataset ("On Street Crime in Camden" from data.gov.uk)
The database name is Crime_Data_in_Camden and the collection name is Street_Crime_Camden. The query to find all records, db.Street_Crime_Camden.find(), works fine but anything else returns nothing at
all. Here is a portion of the metadata:
{
"id" : 509935,
"name" : "Ward Name",
"dataTypeName" : "text",
"fieldName" : "ward_name",
"position" : 13,
"renderTypeName" : "text",
"tableColumnId" : 258836,
"width" : 100,
"cachedContents" : {
"largest" : "West Hampstead",
"non_null" : 79813,
"null" : 0,
"top" : [ {
"item" : "Regent's Park",
"count" : 20
}, {
"item" : "Swiss Cottage",
"count" : 19
}, {
"item" : "Holborn and Covent Garden",
"count" : 18
}
}
}
I've tried 3 attempts at a basic query:
db.Street_Crime_Camden.find({"ward_name":"West Hampstead"});
db.Street_Crime_Camden.find({'meta.ward_name':'West Hampstead'});
db.Street_Crime_Camden.find({meta:{ward_name:"West Hampstead"} });
According to any documentation or tutorial that I've seen any of these approaches should be valid. And I know that there are hundreds of rows (or documents) that match those terms, so why are these queries returning nothing? Advice would be appreciated.
The common theme in the three aproaches you tried is some form of ward_name = West Hampstead but there is no attribute named ward_name in the document you shared with us.
Based on the document you show in your question the only way of addressing an attribute with the value West Hampstead is:
db.Street_Crime_Camden.find({"cachedContents.largest": "West Hampstead"});
For background; you address attributes in your documents by using dot notation so the document you included in your question could be found by any of the following find commands:
db.Street_Crime_Camden.find({"name": "Ward Name"});
db.Street_Crime_Camden.find({"position": 13});
db.Street_Crime_Camden.find({"cachedContents.top.item": "Swiss Cottage"});
db.Street_Crime_Camden.find({"cachedContents.top.1.count": 20});
... etc
These examples might help you to understand how to form find criteria. The MongoDB docs are also useful.

How to remove an attribute from list of json object in mongo shell? [duplicate]

This question already has answers here:
How to Update Multiple Array Elements in mongodb
(16 answers)
Remove field found in any mongodb array
(2 answers)
Closed 3 years ago.
I have below document in MongoDB(2.4.5)
{
"_id" : 235399,
"casts" : {
"crew" : [
{
"_id" : 1186343,
"withBase" : true,
"department" : "Directing",
"job" : "Director",
"name" : "Connie Rasinski"
},
{
"_id" : 86342,
"withBase" : true
}
]
},
"likes" : 0,
"rating" : 0,
"rating_count" : 0,
"release_date" : "1955-11-11"
}
I want to remove withBase filed from array elements inside casts.crew ..
I tried this
db.coll.update({_id:235399},{$unset: { "casts.crew.withBase" : 1 } },false,true)
nothing changed.
And tried this..
db.coll.update({_id:235399},{$unset: { "casts.crew" : { $elemMatch: { "withBase": 1 } } } },false,true)
it removed entire crew array from the document.
Can someone please provide me the right query?
You can use the new positional identifier to update multiple elements in array in 3.6.
Something like
db.coll.update( {_id:235399}, {$unset: {"casts.crew.$[].withBase":""}} )
$[] removes all the withBase property from the crews array. It acts as a placeholder for updating all elements in array.
Use multi true to affect multiple documents.
Sorry to disappoint you, but your answer
db.coll.update({
_id:235399,
"casts.crew.withBase": {$exists: true}
},{
$unset: {
"casts.crew.$.withBase" : true
}
},false,true)
is not correct. Actually it will remove the value, BUT only from the first occurrence of the subdocument, because of the way positional operator works:
the positional $ operator acts as a placeholder for the first element
that matches the query document
You also can not use $unset (as you tried before) because it can not work on arrays (and are you basically trying to remove a key from a document from the array). You also can not remove it with $pull, because pull removes all the array, not just a field of it.
Therefore as far as I know you can not do this with a simple operator. So the last resort is doing $find and then forEach with save. You can see how to do this in my answer here. In your case you need to have another loop in forEach function to iterate through array and to delete a key. I hope that you will be able to modify it. If no, I will try to help you.
P.S. If someone looks a way to do this - here is Sandra's function
db.coll.find({_id:235399}).forEach( function(doc) {
var arr = doc.casts.crew;
var length = arr.length;
for (var i = 0; i < length; i++) {
delete arr[i]["withBase"];
}
db.coll.save(doc);
});
I found a way to unset this lists without having to pull up the object (meaning, just doing an update), it's pretty hackish but if you have a huge database it will make the deal:
db.coll.update({},{$unset: {"casts.crew.0.withBase" : 1, "casts.crew.1.withBase" : 1} }, {multi: 1})
In other words, you have to calculate how many objects there can be in any of your documents list and add those numbers explicitly, in this case as {casts.crew.NUMBER.withBase: 1}.
Also, to count the longest array in a mongodb object, an aggregate can be done, something like this:
db.coll.aggregate( [ { $unwind : "$casts.crew" }, { $group : { _id : "$_id", len : { $sum : 1 } } }, { $sort : { len : -1 } }, { $limit : 1 } ], {allowDiskUse: true} )
Just want to emphasize that this is not a pretty solution but is way faster than fetching and saving.