I am building a mongoose aggregate for search mongodb documents - mongodb

I built an aggregate for searching according to a filter. The things that the user can search for are optional, So is there any way to make the match optional - for example the user can select a date, If he didn't select, I want the aggregate function not to use match date
db.articles.aggregate(
[ {
$match : { date : userSelectedDate }, { else : elseSelection}
} ]
);
If no date is selected => cancel the date matching and match anothers

I would try to build the query document dynamically.
var query = []
if (userSelectedDate){
query.push({ $match : { date : userSelectedDate }})
db.articles.aggregate(query)

The solution was in mongoose documentation using: Aggregate#append(ops)
Appends new operators to this aggregate pipeline
after having the aggregate
aggregate.append({ $project: { field: 1 }}, { $limit: 2 });
so now I can use if(condition) before appending
mongoose documentation for append

Related

Filter dates stored as int on MongoDB

I have several documents on MongoDB collection that were created with an invalid date value and have it currently as:
"UpdateDate " : Date(-62135596800000)
I want to filter those documents, but my query is not returning any data:
db.MyCollection.find({UpdateDate : Date(-62135596800000)})
How can I filter my collection to retrieve those documents?
The problem with your query is that you need to build a proper Date object. This may be different depending on what languaje or drivers are you using to query, but in JS just using new Date() should work.
db.MyCollection.find({UpdateDate : new Date(-62135596800000)})
If you cant reproduce what the value need to be , luckily the date when you have inserted the document for the first time you can extract and update from the document _id as follow:
db.getCollection('MyCollection').update({ UpdateDate : new Date(-62135596800000)
},
[
{ "$set": { "UpdateDate": { $toLong: { $toDate: "$_id" }}}}
],
{ "multi" : true}
)
This is with the assumption that you havent customized the default _id

MongoDB Atlas search filter range with an OR criteria

I would like to do a range search on a date using MongoDB Atlas Search, but I also need to include search results where there is no date at all. This is how I would do this in a $match stage of an aggregation pipeline:
$match: {
$or: [
{
publish_start_date: {
$lte: todayDateTime,
},
},
{
publish_start_date: undefined,
},
],
}
Here is the equivalent of the first part in an atlas $search
$search: {
index: "iapp-component-search",
compound: {
filter: [
{
range: {
path: "publish_start_date",
lte: todayDateTime,
},
},
]
}
}
I know I can simply add the $match stage to the aggregation pipeline after the $search, but the Atlas docs say this is not good for performance, so I would like to include it in the filter of the $search.
How would I add an "OR" clause to also include documents with a publish_start_date of undefined in the $search stage?
For all documents that don't have a start date, write a placeholder value into the date e.g. 0000-00-00. If the missing date means something for your application, duplicate the data + placeholder into a new field.

How to rename fields in MongoDB output?

I am new to MongoDB, my native language is Spanish. So I'm not sure how to find what I need.
I will try to make myself understandable.
I do this query and the result is an array.
For example :
Users.find({passport:123, (err, result) => {
//output of result
[
{"nombre":"pedro","apellido":"jose"},
{"nombre":"pablo","apellido":"jacinto"},
{"nombre":"jose","apellido":"berta"},
]
I want to know if there is a more effective way to do something so that using some function from mongodb I can customize the output to avoid doing something like this:
Required Output :
[
{"name":"pedro","lastname":"jose"},
{"name":"pablo","lastname":"jacinto"},
{"name":"jose","lastname":"berta"},
]
is there any way to process the output information directly from mongoDB?
You need to use MongoDB's Aggregation to do that :
db.collection.aggregate([
/** Filter docs based on criteria */
{
$match: {
passport: 123
}
},
/** Transform fields into required form */
{
$project: {
name: "$nombre",
lastname: "$apellido"
}
}
])
Test : MongoDB-Playground
Ref : $match , $project

mongodb: how to make selection

I Have a mongodb with objects like this:
{
"_id" : "7XXXXXXXXXX",
"apps" : [
{
"id" : "e0d538e0df9a345e",
"os" : "android",
"token" : "f1zp-VSi7Ec:APA91bEAbfH8nMeVidkIjPrJ28WHRFDy-BhvKCQdDSdCYsylUzET9GjbPHjofCrr1NMQurinMCI4fuiF7VWNPXne-80op_h0MloK217kc1zKptdo9FTgAH5R932uDphcyB1xQ27-AFdR",
"version" : "3.2.1",
"build" : "8115680e",
"timestamp" : NumberLong(1571740696818)
}
]
}
How i can select objects older certain date using timestampin my case, for example older 3 month?
You can use $toDate operator in aggregation to do the desired operation,
I hope you are using mongo version 4.0+
$toDate is supported in mongo version 4.0 and on
let selectedDate = new Date();
selectedDate.setDate(d.getDate()-30); //subtracting 30 days from today's date
db.collection("your_collection_name").aggregate({$unwind:{ path: "$apps"}},
{$addFields: { dateValue: {$toDate: "$apps.timestamp" }}},
{$match: { dateValue: {$lte: selectedDate }}},
(err, result) => {
//perform your desired operations
});
Explanation:
basically, I am first unwinding apps array, which will result in having a separate document of each entry in apps.
Then operate on the timestamp field in each document, and convert it into a proper date with $toDate.
Then in the next stage apply your filter with $match.
UPDATE (from comments):
as you are using mongo version 3.2 the above solution will not work.
then I think, you can opt for another approach here:
Query all your documents in this particular collection, find the proper date from the timestamp field.
Update each document with a new field which will now have the value of computed date from the above step, and save it.
I suggest you write a migration script for the above two steps.
Make sure when inserting a new document, you already add this newly computed date field.
Then you can have simple query like:
db.collection("your_collection_name").find({"app.newDateField": {$lte: {selectedDate }}},
{ "apps.$": 1},
(err, result)=>{
})

Include all existing fields and add new fields to document

I would like to define a $project aggregation stage where I can instruct it to add a new field and include all existing fields, without having to list all the existing fields.
My document looks like this, with many fields:
{
obj: {
obj_field1: "hi",
obj_field2: "hi2"
},
field1: "a",
field2: "b",
...
field26: "z"
}
I want to make an aggregation operation like this:
[
{
$project: {
custom_field: "$obj.obj_field1",
//the next part is that I don't want to do
field1: 1,
field2: 1,
...
field26: 1
}
},
... //group, match, and whatever...
]
Is there something like an "include all fields" keyword that I can use in this case, or some other way to avoid having to list every field separately?
In 4.2+, you can use the $set aggregation pipeline operator which is nothing other than an alias to $addFieldsadded in 3.4
The $addFields stage is equivalent to a $project stage that explicitly specifies all existing fields in the input documents and adds the new fields.
db.collection.aggregate([
{ "$addFields": { "custom_field": "$obj.obj_field1" } }
])
You can use $$ROOT to references the root document. Keep all fields of this document in a field and try to get it after that (depending on your client system: Java, C++, ...)
[
{
$project: {
custom_field: "$obj.obj_field1",
document: "$$ROOT"
}
},
... //group, match, and whatever...
]
>>> There's something like "include all fields" keyword that I can use in this case or some another solution?
Unfortunaly, there is no operator to "include all fields" in aggregation operation. The only reason, why, because aggregation is mostly created to group/calculate data from collection fields (sum, avg, etc.) and return all the collection's fields is not direct purpose.
To add new fields to your document you can use $addFields
from docs
and to all the fields in your document, you can use $$ROOT
db.collection.aggregate([
{ "$addFields": { "custom_field": "$obj.obj_field1" } },
{ "$group": {
_id : "$field1",
data: { $push : "$$ROOT" }
}}
])
As of version 2.6.4, Mongo DB does not have such a feature for the $project aggregation pipeline. From the docs for $project:
Passes along the documents with only the specified fields to the next stage in the pipeline. The specified fields can be existing fields from the input documents or newly computed fields.
and
The _id field is, by default, included in the output documents. To include the other fields from the input documents in the output documents, you must explicitly specify the inclusion in $project.
according to #Deka reply, for c# mongodb driver 2.5 you can get the grouped document with all keys like below;
var group = new BsonDocument
{
{ "_id", "$groupField" },
{ "_document", new BsonDocument { { "$first", "$$ROOT" } } }
};
ProjectionDefinition<BsonDocument> projection = new BsonDocument{{ "document", "$_document"}};
var result = await col.Aggregate().Group(group).Project(projection).ToListAsync();
// For demo first record
var fistItemAsT = BsonSerializer.Deserialize<T>(result.ToArray()[0]["document"].AsBsonDocument);