MongoDB Dates in Date and String format for the same column - mongodb

I have a collection where the same column has Date and String format dates for different records, with the same ISO pattern like 2021-04-22T14:10:48.751779Z.
And I need to build a unified query for search, with such =, >, < options for search.
The second problem is an input search query has a different pattern ("2021-04-22")
So the question is:
can I write some kind of transformation of existing table data before the search, for example from Date to String, or in case I have a string value this transformation would not cause any exception.
In this case, I would be able to perform a search by $eq, $lt, $gt, and $regex query options.
I`m not considering casting strings to dates even if this is possible because my input did not have "14:10:48.751779Z" this part of the date and query would not find anything because of dates would not match. For this case, $regex looks like the only solution.
Or your any other suggestions would be considered.
Current query which not satisfies case when DB column is String type:
{
"aggregate":"collection_name",
"pipeline":[
{
"$match":{
"$and":[
{
"some_column":{
"$eq":"some_value"
}
},
{
"date_column_with_string_or_date_type":{
"$gt":{
"$date":"1980-01-01T00:00:00Z"
}
}
}
]
}
},
{
"$project":{
"_id":1
}
}
]
}

Solved by using $expr and $toString
This works with Date And String types of "date_column"
$match: {
$expr: {
$gt : [
{ $toString:"$date_column" },
"2021-04-22"
]
}
}
operator:

Related

How do you query a Mongo db for rows containing a column with a specific type?

I have a collection that has ended up with a column containing an updated timestamp, sometimes as a text field, sometimes as a date field. I am looking for the syntax to execute a query to find all rows that contain that field as a date type.
You can use $type with $not
Checking the not scenario. If type is not of string, then consider that doc.
playground
db.collection.find({
"key": {
$not: {
$type: "string"
}
}
})

MongoDB: concatinate multiple number values to string

I have a document (inside aggregation, after $group stage) which have an object (but I could form array, if I needed it to) with number values.
MongoPlayground example with data and my aggregate query available here.
And I want to make a new _id field during next $project stage, consisted of this three number values, like:
item_id | unix time | pointer
_id: 453435-41464556645#1829
The problem is, that when I am trying to use $concat, the query returns me an error like:
$concat only supports strings, not int
So here is my question: is it possible to achieve such results? I have seen the relevant question MongoDB concatenate strings from two fields into a third field, but it didn't cover my case.
The $concat only concatenate strings, these fields $_id.item_id contains int value and $_id.last_modified double value,
The $toString converts a value to a string,
_id: {
$concat: [
{
$toString: "$_id.item_id"
},
" - ",
{
$toString: "$_id.last_modified"
}
]
}
Playground: https://mongoplayground.net/p/SSlXW4gIs_X

Mongodb shell aggregate query comparing dates not returning results

I cant spot what the issue here is. This returns nothing
db.mycoll.aggregate([
{$project:{CreatedAt:"$CreatedAt",Now:"$$NOW",DateFloor:{$add:["$$NOW",-24*60*60000]}}},
{$match:{CreatedAt:{$gte:"$DateFloor"}}}
])
But this returns results - substituting DateFloor with actual value
db.mycoll.aggregate([
{$project:{CreatedAt:"$CreatedAt",Now:"$$NOW",DateFloor:{$add:["$$NOW",-24*60*60000]}}},
{$match:{CreatedAt:{$gte: ISODate("2020-04-28T23:17:56.547Z")}}}
])
Issue with your query is when you're doing :
{$match:{CreatedAt:{$gte:"$DateFloor"}}}
You're actually checking for documents where CreatedAt field's value to be greater than input string value "$DateFloor", So $match is not considering "$DateFloor" as another field in the same document rather it's considering it as a string value. So to compare two fields from same document you need to use $gte with $expr (which will let you use aggregation expressions within the query language).
{
$match: {
{
$expr: {
$gte: ["$CreatedAt", "$DateFloor"];
}
}
}
}
So you might get confused when I say aggregation expressions & why $gte needs to be wrapped inside $expr - In your actual query $gte refers to comparison operator in MongoDB but in this above query $gte refers to aggregation pipeline operator where both technically does the same but which is what needed to compare two fields from same document.

MongoDB: Search in array

I have a field in a MongoDB Collection like:
{
place = ['London','Paris','New York']
}
I need a query that will return only that particular entry of the array, where a specific character occurs. For Example, I want to search for the terms having the letter 'o'(case-insensitive) in them. It should just return 'London' and 'New York'.
I tried db.cities.find({"place":/o/i}), but it returns the whole array.
You'll need to $unwind using an aggregate query, then match.
db.cities.aggregate([ { $unwind:'$place' }, { $match: { place : {$regex: /o/i } } } ])
In simple you find with $regex below query will work without aggregation
db.collectionName.find({ place : {$regex: /o/i } })

Checking if a field contains a string

I'm looking for an operator, which allows me to check, if the value of a field contains a certain string.
Something like:
db.users.findOne({$contains:{"username":"son"}})
Is that possible?
You can do it with the following code.
db.users.findOne({"username" : {$regex : "son"}});
As Mongo shell support regex, that's completely possible.
db.users.findOne({"username" : /.*son.*/});
If we want the query to be case-insensitive, we can use "i" option, like shown below:
db.users.findOne({"username" : /.*son.*/i});
See: http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-RegularExpressions
https://docs.mongodb.com/manual/reference/sql-comparison/
http://php.net/manual/en/mongo.sqltomongo.php
MySQL
SELECT * FROM users WHERE username LIKE "%Son%"
MongoDB
db.users.find({username:/Son/})
As of version 2.4, you can create a text index on the field(s) to search and use the $text operator for querying.
First, create the index:
db.users.createIndex( { "username": "text" } )
Then, to search:
db.users.find( { $text: { $search: "son" } } )
Benchmarks (~150K documents):
Regex (other answers) => 5.6-6.9 seconds
Text Search => .164-.201 seconds
Notes:
A collection can have only one text index. You can use a wildcard text index if you want to search any string field, like this: db.collection.createIndex( { "$**": "text" } ).
A text index can be large. It contains one index entry for each unique post-stemmed word in each indexed field for each document inserted.
A text index will take longer to build than a normal index.
A text index does not store phrases or information about the proximity of words in the documents. As a result, phrase queries will run much more effectively when the entire collection fits in RAM.
As this is one of the first hits in the search engines, and none of the above seems to work for MongoDB 3.x, here is one regex search that does work:
db.users.find( { 'name' : { '$regex' : yourvalue, '$options' : 'i' } } )
No need to create and extra index or alike.
Here's what you have to do if you are connecting MongoDB through Python
db.users.find({"username": {'$regex' : '.*' + 'Son' + '.*'}})
you may also use a variable name instead of 'Son' and therefore the string concatenation.
Simplest way to accomplish this task
If you want the query to be case-sensitive
db.getCollection("users").find({'username':/Son/})
If you want the query to be case-insensitive
db.getCollection("users").find({'username':/Son/i})
ideal answer its use index
i option for case-insensitive
db.users.findOne({"username" : new RegExp(search_value, 'i') });
This should do the work
db.users.find({ username: { $in: [ /son/i ] } });
The i is just there to prevent restrictions of matching single cases of letters.
You can check the $regex documentation on MongoDB documentation.
Here's a link: https://docs.mongodb.com/manual/reference/operator/query/regex/
I use this code and it work for search substring
db.users.find({key: { $regex: new RegExp(value, 'i')}})
If you need to do the search for more than one attribute you can use the $or. For example
Symbol.find(
{
$or: [
{ 'symbol': { '$regex': input, '$options': 'i' } },
{ 'name': { '$regex': input, '$options': 'i' } }
]
}
).then((data) => {
console.log(data)
}).catch((err) => {
console.log(err)
})
Here you are basing your search on if the input is contained in the symbol attribute or the name attribute.
If the regex is not working in your Aggregate solution and you have nested object. Try this aggregation pipeline: (If your object structure is simple then, just remove the other conditions from below query):
db.user.aggregate({$match:
{$and:[
{"UserObject.Personal.Status":"ACTV"},
{"UserObject.Personal.Address.Home.Type":"HME"},
{"UserObject.Personal.Address.Home.Value": /.*son.*/ }
]}}
)
One other way would be to directly query like this:
db.user.findOne({"UserObject.Personal.Address.Home.Value": /.*son.*/ });
If your regex includes a variable, make sure to escape it.
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
This can be used like this
new RegExp(escapeRegExp(searchString), 'i')
Or in a mongoDb query like this
{ '$regex': escapeRegExp(searchString) }
Posted same comment here
For aggregation framework
Field search
('$options': 'i' for case insensitive search)
db.users.aggregate([
{
$match: {
'email': { '$regex': '#gmail.com', '$options': 'i' }
}
}
]);
Full document search
(only works on fields indexed with a text index
db.articles.aggregate([
{
$match: { $text: { $search: 'brave new world' } }
}
])
How to ignore HTML tags in a RegExp match:
var text = '<p>The <b>tiger</b> (<i>Panthera tigris</i>) is the largest cat species, most recognizable for its pattern of dark vertical stripes on reddish-orange fur with a lighter underside. The species is classified in the genus <i>Panthera</i> with the lion, leopard, jaguar, and snow leopard. It is an apex predator, primarily preying on ungulates such as deer and bovids.</p>';
var searchString = 'largest cat species';
var rx = '';
searchString.split(' ').forEach(e => {
rx += '('+e+')((?:\\s*(?:<\/?\\w[^<>]*>)?\\s*)*)';
});
rx = new RegExp(rx, 'igm');
console.log(text.match(rx));
This is probably very easy to turn into a MongoDB aggregation filter.