MongoDB: Get all nearby with filter - javascript

In MongoDB, I have models of User, Token, and Boost.
A user can have one or more tokens and one or more boosts.
Token has a 2dsphere location field.
And Boost has startTime and stopTime Date fields.
A user is said to have an active boost if Date.now() is greater than boost.startTime() and less than boost.stopTime().
What Mongo aggregation can I write to fetch me all the tokens near a particular location that belong to users with at least one active boost?

Based on your question, I have created a mock data
token collection:
{
"_id" : ObjectId("5b97541c6af22cc65216ffd8"),
"userid" : "5b9753726af22cc65216ffd6",
"location" : {
"longitude" : 80.250875,
"latitude" : 13.052519
}
},
{
"_id" : ObjectId("5b97543a6af22cc65216ffd9"),
"userid" : "5b97537e6af22cc65216ffd7",
"location" : {
"longitude" : 80.249995,
"latitude" : 13.051819
}
}
boost collection :
{
"_id" : ObjectId("5b9754796af22cc65216ffda"),
"startTime" : ISODate("2018-09-11T05:36:57.149Z"),
"stopTime" : ISODate("2018-09-11T05:36:57.149Z"),
"userid" : "5b9753726af22cc65216ffd6"
},
{
"_id" : ObjectId("5b9754b46af22cc65216ffdb"),
"startTime" : ISODate("2018-10-08T18:30:00.000Z"),
"stopTime" : ISODate("2018-10-08T18:30:00.000Z"),
"userid" : "5b97537e6af22cc65216ffd7"
}
Users collection :
{
"_id" : ObjectId("5b9753726af22cc65216ffd6"),
"userName" : "user111"
},
{
"_id" : ObjectId("5b97537e6af22cc65216ffd7"),
"userName" : "user222"
}
The aggregate query to fetch all the tokens near a particular location that belong to users with at least one active boost is:
db.token.aggregate([
{
"$geoNear": {
"near": { type: "Point", coordinates: [80.248797,13.050599] },
"distanceField": "location",
"maxDistance": 1000,
"includeLocs": "location",
"spherical": true
}
},
{"$lookup" : {"from":"boost",
"localField" : "userid",
"foreignField" : "userid",
"as" : "boostDocs"
}},
{"$unwind" : "$boostDocs"},
{"$match" : {"$and":[{"boostDocs.startTime":{"$lte":new Date("11/09/2018")}},{"boostDocs.stopTime":{"$gte":new Date("10/09/2018")}}]}}
])
Notice that query to match the location is at the top of the query as $geoNear will only work if its the first stage of the aggregation pipeline.
The Date that I've used for comparison is just to check if my query works. You can specify your date or Date.now() as per your requirement.

Related

only edit (update) fields provided by form into mongoose subdocument

i wanted to update my mongoose subdocument with whatever field i fill in the form its working fine but the problem i am facing is if any of the field is left empty and then i submit i want it to just update the subdocumnet with just the fields provided by the form and fields which are not provided should be left with what it was before. currently it is deleting the fields not provided by form inside subdocument after the update
this is the update fn i am currently using
List.updateOne(
{"questionSet._id": questId},
{
$set: {
"questionSet.$": req.body
}
},
{new: true, useFindAndModify: false},
(e)=>{
if(e){
console.log(e);
res.redirect("/admin-edit-quest")
} else {
res.redirect("/admin-edit-quest")
}
}
)
this is eg. of my mongoose model
list: { "_id" : ObjectId("60f2cc07275bbb30d8cb268e"),
"listName" : "dsa",
"aboutList" : "dsa queestions",
questionSet" : [ { "solved" : false,
"_id" : ObjectId("60f2cc12275bbb30d8cb2695"),
"topic" : "array",
"name" : "array is best",
"url" : "www.arr.com",
"listname" : "dsa",
"__v" : 0 },
{ "solved" : false,
"_id" : ObjectId("60f2cc1b275bbb30d8cb269d"),
"topic" : "linked list",
"name" : "reverse list",
"url" : "www.list.com",
"listname" : "dsa",
"__v" : 0 }
],
"__v" : 2
}
Need to delete keys with blank values from req.body:
pruned_body = {}
body = JSON.parse(reg.body)
Object.keys(body).forEach(function (key) {
if (body[key]) {
pruned_body[key] = body[key]
}})
And then use pruned_body in $set.

change only one field of entire array of embedded document in mongoose

i have a list schema and a question set schema. the quetsionSet schema is embedded inside the list schema. its working fine but how can i update anything inside the array of embedded document i.e. here i want to change the listname of all the documents inside questionSet (array of questionSet documents).
here is an example of my list document model
{ "_id" : ObjectId("60f2cc07275bbb30d8cb268e"),
"listName" : "dsa",
"aboutList" : "dsa queestions",
questionSet" : [ { "solved" : false,
"_id" : ObjectId("60f2cc12275bbb30d8cb2695"),
"topic" : "array",
"name" : "array is best",
"url" : "www.arr.com",
"listname" : "dsa",
"__v" : 0 },
{ "solved" : false,
"_id" : ObjectId("60f2cc1b275bbb30d8cb269d"),
"topic" : "linked list",
"name" : "reverse list",
"url" : "www.list.com",
"listname" : "dsa",
"__v" : 0 }
],
"__v" : 2
}
you can use the following in your case
db.<collection_name>.updateOne(
{ "_id" : ObjectId("60f2cc07275bbb30d8cb268e")},
{
$set: {
'questionSet.$[].listname': "javascript"
}
}
)

Mongo/Mongoose: Why does this query fail?

I'm trying to find documents in mongoDB that are older than a specific amount of time.
Here is an example of a document in my database that should be returned,
{
"_id" : ObjectId("5cc07ea943eeba155840b62f"),
"name" : "test3",
"value" : 3,
"created_at" : {
"$date" : 1556119209477
},
"updatedAt" : {
"$date" : 1556119209477
},
"__v" : 0
}
Here is the query I have so far....
Item.find({"created_at":{$lt:new Date(Date.now() - keys.anAmountOfMilliseconds)}}, (err, foundItems) => {
When run like this, the following error is returned: { CastError: Cast to date failed for value "Invalid Date" at path "created_at" for model "items"
When searching for documents using, createdAt or "createdAt", the query returns with nothing....

How to lazy load with angular using mongodb data

I want to implement lazy loading in angular.js, i am sending the list of data from backend to the UI using nodejs, i need to implement, on scroll 10 items, are there any examples to achieve this please share any links to do this. Please can anybody help me on this.
Lazy loading is nothing to do with DB, since it depends on the DAO layer, whereas DB is concerned about returning the data for the query submitted to it.
My approach to achieve lazy loading from UI
Using pagination we can do lazy loading
1) Find the total number of documents in your collection
2) Each time when you are loading the page with next set of data, pass on the required information such as from which document the DB needs to send the data
3) Repeat step 2 until you reach the total number of documents in your collection
An example Let us have a collection with few records
db.mycollection.find();
{ "_id" : ObjectId("58947e7e93cbb73057657d60"), "name" : "Clement" }
{ "_id" : ObjectId("58947e7e93cbb73057657d61"), "name" : "Rockin" }
{ "_id" : ObjectId("58947e7e93cbb73057657d62"), "name" : "Gowri" }
{ "_id" : ObjectId("58947e7e93cbb73057657d63"), "name" : "Inbaraj" }
{ "_id" : ObjectId("58947e7e93cbb73057657d64"), "name" : "Siva" }
{ "_id" : ObjectId("58947e7e93cbb73057657d65"), "name" : "Rani" }
{ "_id" : ObjectId("58947e7e93cbb73057657d66"), "name" : "Rose" }
{ "_id" : ObjectId("58947e7e93cbb73057657d67"), "name" : "Rekha" }
{ "_id" : ObjectId("58947e7e93cbb73057657d68"), "name" : "Dev" }
{ "_id" : ObjectId("58947f6f93cbb73057657d69"), "name" : "Joe" }
{ "_id" : ObjectId("58947f8393cbb73057657d6a"), "name" : "Beniton" }
Prerequisite for doing pagination
db.mycollection.find().count()
11
Let me have the initial load size as 5
My first query to DB would be
db.mycollection.find().sort({"_id":1}).limit(5);
{ "_id" : ObjectId("58947e7e93cbb73057657d60"), "name" : "Clement" }
{ "_id" : ObjectId("58947e7e93cbb73057657d61"), "name" : "Rockin" }
{ "_id" : ObjectId("58947e7e93cbb73057657d62"), "name" : "Gowri" }
{ "_id" : ObjectId("58947e7e93cbb73057657d63"), "name" : "Inbaraj" }
{ "_id" : ObjectId("58947e7e93cbb73057657d64"), "name" : "Siva" }
My Next query to DB
db.mycollection.find().sort({"_id":1}).skip(5).limit(5);
{ "_id" : ObjectId("58947e7e93cbb73057657d65"), "name" : "Rani" }
{ "_id" : ObjectId("58947e7e93cbb73057657d66"), "name" : "Rose" }
{ "_id" : ObjectId("58947e7e93cbb73057657d67"), "name" : "Rekha" }
{ "_id" : ObjectId("58947e7e93cbb73057657d68"), "name" : "Dev" }
{ "_id" : ObjectId("58947f6f93cbb73057657d69"), "name" : "Joe" }
final query would be
db.mycollection.find().sort({"_id":1}).skip(10).limit(5);
{ "_id" : ObjectId("58947f8393cbb73057657d6a"), "name" : "Beniton" }
In this example,
Sort on _id is used, which is based on insertion time, which helps us in ordering the documents and render it for the subsequent queries.
Hope it Helps!
References:
https://www.mongodb.com/blog/post/the-mean-stack-mongodb-expressjs-angularjs-and
Lazy Loading/More Data Scroll in Mongoose/Nodejs
http://adrichman.github.io/implementing-a-lazy-load-and-infinite-scroll-in-angularjs/

taking the difference between adjacent documents in mongoDB

How do I take the difference between adjacent records in mongoDB using javascript? For example, if I have the following three documents in a collection:
{
"_id" : ObjectId("50ed90a55502684f440001ac"),
"time" : ISODate("2013-02-13T15:45:41.148Z")
}
{
"_id" : ObjectId("50ed90a55502684f440001ad"),
"time" : ISODate("2013-02-13T15:45:42.148Z")
}
{
"_id" : ObjectId("50ed90a55502684f440001ae"),
"time" : ISODate("2013-02-13T15:45:45.148Z")
}
I want to take the difference in the "time" field between adjacent values to get:
{
"_id" : ObjectId("50ed90a55502684f440001ac"),
"time" : ISODate("2013-02-13T15:45:41.148Z"),
"time_difference" : null
}
{
"_id" : ObjectId("50ed90a55502684f440001ad"),
"time" : ISODate("2013-02-13T15:45:42.148Z"),
"time_difference" : 1
}
{
"_id" : ObjectId("50ed90a55502684f440001ae"),
"time" : ISODate("2013-02-13T15:45:45.148Z"),
"time_difference" : 3
}
Any ideas on how to do this efficiently in javascript/mongoDB? Thanks.
I don't know whether this was true when the question was asked seven years ago, but this can be solved completely within the aggregation framework. Assuming the collection name is AdjacentDocument, the following aggregation will get the results you're looking for:
db.AdjacentDocument.aggregate(
{$sort: {time: 1}},
{$group: {_id: 0, document: {$push: '$$ROOT'}}},
{$project: {documentAndPrevTime: {$zip: {inputs: ['$document', {$concatArrays: [[null], '$document.time']}]}}}},
{$unwind: {path: '$documentAndPrevTime'}},
{$replaceWith: {$mergeObjects: [{$arrayElemAt: ['$documentAndPrevTime', 0]}, {prevTime: {$arrayElemAt: ['$documentAndPrevTime', 1]}}]}},
{$set: {time_difference: {$trunc: [{$divide: [{$subtract: ['$time', '$prevTime']}, 1000]}]}}},
{$unset: 'prevTime'}
);
Aggregation pipeline walkthrough
First, the documents are sorted from oldest to newest. They are grouped into a single document with the documents stored in an ordered array field:
{$sort: {time: 1}},
{$group: {_id: 0, document: {$push: '$$ROOT'}}}
/*
{
"_id" : 0,
"document" : [
{
"_id" : ObjectId("50ed90a55502684f440001ac"),
"time" : ISODate("2013-02-13T15:45:41.148Z")
},
{
"_id" : ObjectId("50ed90a55502684f440001ad"),
"time" : ISODate("2013-02-13T15:45:42.148Z")
},
{
"_id" : ObjectId("50ed90a55502684f440001ae"),
"time" : ISODate("2013-02-13T15:45:45.148Z")
}
]
}
*/
Next, the previous times are zipped into the document array, creating an array of [document, previousTime]:
{$project: {documentAndPrevTime: {$zip: {inputs: ['$document', {$concatArrays: [[null], '$document.time']}]}}}}
/*
{
"_id" : 0,
"documentAndPrevTime" : [
[
{
"_id" : ObjectId("50ed90a55502684f440001ac"),
"time" : ISODate("2013-02-13T15:45:41.148Z")
},
null
],
[
{
"_id" : ObjectId("50ed90a55502684f440001ad"),
"time" : ISODate("2013-02-13T15:45:42.148Z")
},
ISODate("2013-02-13T15:45:41.148Z")
],
[
{
"_id" : ObjectId("50ed90a55502684f440001ae"),
"time" : ISODate("2013-02-13T15:45:45.148Z")
},
ISODate("2013-02-13T15:45:42.148Z")
]
]
}
*/
Next, the document & time array is unwound, creating a document for each of the initial documents:
{$unwind: {path: '$documentAndPrevTime'}}
/*
{
"_id" : 0,
"documentAndPrevTime" : [
{
"_id" : ObjectId("50ed90a55502684f440001ac"),
"time" : ISODate("2013-02-13T15:45:41.148Z")
},
null
]
}
{
"_id" : 0,
"documentAndPrevTime" : [
{
"_id" : ObjectId("50ed90a55502684f440001ad"),
"time" : ISODate("2013-02-13T15:45:42.148Z")
},
ISODate("2013-02-13T15:45:41.148Z")
]
}
{
"_id" : 0,
"documentAndPrevTime" : [
{
"_id" : ObjectId("50ed90a55502684f440001ae"),
"time" : ISODate("2013-02-13T15:45:45.148Z")
},
ISODate("2013-02-13T15:45:42.148Z")
]
}
*/
Next, we replace the document with the value of the document array element, merged with previous time element (using null if it's the initial index):
{$replaceWith: {$mergeObjects: [{$arrayElemAt: ['$documentAndPrevTime', 0]}, {prevTime: {$arrayElemAt: ['$documentAndPrevTime', 1]}}]}}
/*
{
"_id" : ObjectId("50ed90a55502684f440001ac"),
"time" : ISODate("2013-02-13T15:45:41.148Z"),
"prevTime" : null
}
{
"_id" : ObjectId("50ed90a55502684f440001ad"),
"time" : ISODate("2013-02-13T15:45:42.148Z"),
"prevTime" : ISODate("2013-02-13T15:45:41.148Z")
}
{
"_id" : ObjectId("50ed90a55502684f440001ae"),
"time" : ISODate("2013-02-13T15:45:45.148Z"),
"prevTime" : ISODate("2013-02-13T15:45:42.148Z")
}
*/
Finally, we update the document by setting the time_difference to the difference of the two time fields, and removing the temporary prevTime field. Since the difference between two dates is in milliseconds and your example uses seconds, we calculate the seconds by dividing by 1000 and truncating.
{$set: {time_difference: {$trunc: [{$divide: [{$subtract: ['$time', '$prevTime']}, 1000]}]}}},
{$unset: 'prevTime'}
/*
{
"_id" : ObjectId("50ed90a55502684f440001ac"),
"time" : ISODate("2013-02-13T15:45:41.148Z"),
"time_difference" : null
}
{
"_id" : ObjectId("50ed90a55502684f440001ad"),
"time" : ISODate("2013-02-13T15:45:42.148Z"),
"time_difference" : 1
}
{
"_id" : ObjectId("50ed90a55502684f440001ae"),
"time" : ISODate("2013-02-13T15:45:45.148Z"),
"time_difference" : 3
}
*/
The one thing you will want to make sure of here is that you have a sort on the query you wish to use to garnish your records. If no sort is used it will actually use find order, which is not $natural order.
Find order can differ between queries so if you run the query twice within the period of 2 minutes you might find that they don't return the same order. It does seem however that your query would be logically sorted on tiem_difference.
It should also by noted that this is not possible through normal querying. I also do not see an easy way doing this through the aggregation framework.
So already it seems the next plausible method is either using multiple queries or client side processing. Client side processing is probably the better here using a function like the one defined by #Marlon above.
One thing, I want to clear you. Unlike MYSQL, MongoDB is not give gurantee to the position. I mean, MongoDB will give you different sort at different time. So compare adjacent document may give different result, on every reading.
If you are fine with that and you want to compare then try with MongoDB's MapReduce http://docs.mongodb.org/manual/applications/map-reduce/
Assuming those 3 objects are coming through in an array, you could do something like the below:
var prevTime;
var currentTime;
for(var i = 0; i < records.length; i++)
{
currentTime = new Date(records[i].time).getTime();
records[i].time_difference = currentTime - prevTime;
prevTime = currentTime;
}
Of course you'll need to swap bits out to make it use the records from mongo.
If you need to do any more complex date calculations, I highly suggest checking out datejs (which you can get a node wrapper for if you want).

Categories