Mongoose how to find specific value - node.js

After a lot of reading, I am stuck.
the code I posted here, is the implementation of a store database I am trying to make.
in every store, we have some fields. I am interested in doing something with the items array, that contains JSON variables.
I want to filter the items through three filters, firstly by the store ID, secondly by the category ID, and the last filter will be the semi category ID.
I want to send the data from the front end, meaning I supply STOREID, the CategoryID, and the SemiCategoryID.
after receiving the data at the back end side, I am expecting to receive only the relevant items according to the data supplied by the front end.
{
"_id": {
"$oid": "5a1844b5685cb50a38adf5bb" --> **ID of the STORE**
},
"name": "ACE",
"user_id": "59e4c41105d1f6227c1771ea",
"imageURL": "none",
"rating": 5,
"items": [
{
"name": "NirCohen",
"categoryID": "5a0c2d292235680012bd12c9",
"semiCatID": "5a0c2d5a2235680012bd12ca",
"_id": {
"$oid": "5a1958181cd8a208882a80f9"
}
},
{
"name": "he",
"categoryID": "5a0c2d292235680012bd12c9",
"semiCatID": "5a0c2d5a2235680012bd12ca",
"_id": {
"$oid": "5a1973c40e561e08b8aaf2b2"
}
},
{
"name": "a",
"categoryID": "5a0c2d292235680012bd12c9",
"semiCatID": "5a0c2d5a2235680012bd12ca",
"_id": {
"$oid": "5a197439bc1310314c4c583b"
}
},
{
"name": "aaa",
"categoryID": "5a0c2d292235680012bd12c9",
"semiCatID": "5a0c2d5a2235680012bd12ca",
"_id": {
"$oid": "5a197474558a921bb043317b"
}
},
],
"__v": 9
}
and I want the Backend to return the filtered items according to the query.
The problem is, I am not managing to get the CORRECT query.
your help will be much appreciated,
thank you in advance.

If I understand you correctly, you are doing something like this:
Store.find({}).then(..);
If you only want to find the stores where categoryID is equal to the variable myCategory, you could filter them out by using:
Store.find({semiCatID: myCategory}).then(..);
Please let me know if this is not what you are after, then we can keep trying to figure this out together.
EDIT: So you are sending the variables StoreID, CategoryID and SemiCategoryID from the frontend. Receive them in the backend, and want to filter your database collection matching all three fields?
If so.. then I think all you have to do is change your current query:
store.findOne({ _id: req.body.userID }, (err, store) => { console.log(store); });
To something like:
store.findOne({
_id: req.body.userID,
storeID: req.body.StoreID,
categoryID: req.body.CategoryID,
semiCategoryID: req.body.SemiCategoryID
}, (err, store) => { console.log(store); });
This way, the objects you get back from mongo must match all four criterias given from the frontend.

As far as I Understood your question here is my answer to it you can use findById
Store.findById({//store id}).then(..);
or
Store.findOne({_id:ObjectID(storeID)}).then(..);

Related

PouchDB/CouchDB Group By Value in Array

I am using PouchDB and I have a dataset representing a social network in a graph. People are documents, and the people they follow are in an array of the _id of the person followed. Here is a sample of the data:
[
{
"_id": "mc0001",
"name": "Jill Martin",
"joined": "2020-01-15",
"follows": []
},
{
"_id": "mc0002",
"name": "Elena Markova",
"joined": "2020-01-21",
"follows": ["mc0001"]
},
{
"_id": "mc0003",
"name": "Carlos Sanchez",
"joined": "2020-01-27",
"follows": ["mc0001", "mc0002"]
},
{
"_id": "mc0004",
"name": "Ai Sato",
"joined": "2020-02-21",
"follows": ["mc0001", "mc0003"]
},
{
"_id": "mc0005",
"name": "Ming Wu",
"joined": "2020-03-21",
"follows": ["mc0002", "mc0003", "mc0004"]
}
]
What I would like to do is query for each person, and get a list of followers. I am looking for something like this:
[
{
"_id": "mc0001",
"followers": ["mc0002", "mc0003", "mc0004"]
},
{
"_id": "mc0002",
"followers": ["mc0003", "mc0005"]
},
{
"_id": "mc0003",
"followers": ["mc0004", "mc0005"]
},
{
"_id": "mc0004",
"followers": ["mc0005"]
},
{
"_id": "mc0005",
"followers": []
}
]
Is there a way to do this without changing the data structure (e.g. moving the followers array into the doc of the person being followed)?
Create a Map/Reduce view that loops through the follows array in each document and emits those; like this:
function (doc) {
for(var i =0; i<doc.follows.length; i++) {
emit(doc.follows[i], null);
}
}
You end up with an index keyed on a user and where each row has the id of a follower of that user. You can then query the index, supplying the key of the user whose followers you want to find, like this:
$URL/users/_design/users/_view/by-follower?key="mc0001"&reduce=false
You will get something like this:
{"total_rows":8,"offset":0,"rows":[
{"id":"mc0002","key":"mc0001","value":null},
{"id":"mc0003","key":"mc0001","value":null},
{"id":"mc0004","key":"mc0001","value":null}
]}
This is not exactly the format of the data you have in your question, but you can see that the id field in each object contains a follower of your desired user, so you can go from there.

Correct data structure for storing an array of objects

I want to store latitude and longitude coordinates for a user. They may have more than one so I am making it an array of objects. I just wanted to ask if this structure is okay for storing an array of objects for a user or should it be changed to something different/better?
In my User mongoose schema:
places: [
{
location: {
latitude: {
type: Number,
},
longitude: {
type: Number,
},
},
},
],
My controller:
exports.addLocation = async (req, res) => {
const { latitude, longitude, userId } = req.body;
const user = await User.findById(userId);
if (!user) {
return res.status(404).send("No user found");
}
user.places.push({ location: { longitude: longitude, latitude: latitude } });
await user.save();
res.status(201).send(user);
};
When I look in Mongo DB Atlas I can see that each object in the array has its own _id.
{
"_id": {
"$oid": "5f2d9ec5053d4a754d6790e8"
},
"name": "Bob",
"email": "bob#gmail.com",
"__v": 31,
"places": [{
"_id": {
"$oid": "5f3577d511d3f56b7082d320"
},
"location": {
"longitude": 48.137565,
"latitude": 11.575502
},
"saved_at": {
"$date": "2020-08-13T17:26:45.312Z"
}
}, {
"_id": {
"$oid": "5f3577ee11d3f56b7082d321"
},
"location": {
"longitude": 48.863129,
"latitude": 2.294873
},
"saved_at": {
"$date": "2020-08-13T17:27:10.594Z"
}
}]
}
Both options, depend on your use-case. In any NoSQL modeling, the data dictates if it better to embed repeated data in the same document or split into more documents.
If you are tracking your users, for example, and your "places" array will store destinations where a user stayed more than 10 minutes, then your array may keep growing beyond the max document size of 16MB.
Even when it is much smaller than that zise, querying your array with several hundred elements will become a nightmare.
For such a use-case, you better split those visits into separate documents. e.g. add "day" after "email" and store each day's places in a separate document, where each array element is another diestination visited on that day.
But if the "places" array is not expected to grow, then keep it embedded in a sigle user document, same as in your example.
Here are questions, that we have:
Do we need to store places in array as long as it's should be NoSQL
approach?
Isn't the point of a NoSql database to not have to normalise the database and create tables that require multiple joins etc.
With current data, normalization approach will look like this:
As for normalization approach we don't want to have any duplications and have many-to-many relation. In this case it's not suitable for NoSQL database.
Here is an other example with separating places to another collection, but more with NoSQL-suitable approach
NOTE: example for MongoDB is provided in table view just as showcase. For sure it should be JSON-format
Json Structure
Users collection:
{
"_id" : ObjectId("5f0b0540c0c721d9f87ded50"),
"name":"Bob",
"email": "bob#gmail.com"
}
Places collection:
[{
"_id": ObjectId("5f3577d511d3f56b7082d320")
"location": {
"longitude": 48.137565,
"latitude": 11.575502
},
"saved_at": "2020-08-13T17:26:45.312Z",
"user_id": ObjectId("5f0b0540c0c721d9f87ded50")
},
{
"_id": ObjectId("5f3577d5098gh56b7082d410")
"location": {
"longitude": 43.137565,
"latitude": 12.575502
},
"saved_at": "2020-08-13T19:26:45.312Z",
"user_id": ObjectId("5f0b0540c0c721d9f87ded50")
}]
As you could see here we will have data duplication and in NoSQL world we don't care of it. We choose this 2 collections separating, to increase and simplify queries to database.
Just to sum up - We don't use SQL normalization in this case. We are separating data, duplicating data, so it would be easy to query, setting several indexes(if needed), updating and deleting documents, etc.

How to get information from all collection, but get only item of array

I'm using MongoDB with NodeJS, and I keep my data in a collection called "posts", in this way
{
"_id": "5ca0ff61f247dc29b8331af9",
"tipo": "First Mongo Post",
"user": {
"_id": "5ca01d2c56a2d9165c848f4f",
"nombre": "User1",
"email": "luis#gmail.com",
"permission": "Administrador",
"__v": 0
},
"rates": [
{
"user": "5ca01d2c56a2d9165c848f4f",
"votation": 1
},
{
"user": "5ca021b03904f70cf823b6e6",
"votation": -1
}
],
"__v": 0
}
I would like to think that the way that I save my data its correctly, whatever, I want to get all the information from my collection called "posts", but the array called rates, I only want to get the object that the user its the same like the user that make the call to the get method.
For example, If I call my get method, and I send the user ID (5ca01d2c56a2d9165c848f4f), I want to return all posts collection but in the the rates array of each one I only want to have the object that has the same ID compared by the ID that I sent in the get method, for example:
{
"_id": "5ca0ff61f247dc29b8331af9",
"tipo": "First Mongo Post",
"user": {
"_id": "5ca01d2c56a2d9165c848f4f",
"nombre": "User1",
"email": "luis#gmail.com",
"permission": "Administrador",
"__v": 0
},
"rates": [
{
"user": "5ca01d2c56a2d9165c848f4f",
"votation": 1
}
],
"__v": 0
}
To be honest, I don't know how can I do it, so i hope that you can help me or explain me how can I do it, please.
Regards, I'm new using this technologies
You can store Rate and Post objects separately, so you can create a new collection called rates to keep Rates separately from Posts collection. In the Rate collection you can store the postId, userId and votation value
const mongoose = require('mongoose');
const RateSchema = new mongoose.Schema({
userId: {
type: String
},
postId: {
type: String
},
votation: {
type: Number,
}
);
module.exports = mongoose.model('Rate', RateSchema);
After that you can create a function to get the rates that is related to userId and postId like
const Rate = require('../models/Rate');
function getRatesByUserIdandPostId(userId, postId) {
return Rate.find({userId, postId});
};
This way you don't need to search in a for loop for which rate is relate to that user, function will do it for you. Also it is faster because you don't get all the rates that is related to one specific post.

Mongodb Update into three level embedded document only on field value

I am trying to update three level Embedded document only one field. i am posting my data below in which I am trying to update only one field into the document. here is my Collection in which i am trying to update View value. {
"_id": "5bbc7614b6160b29f05854c7",
"createdAt": "2018-10-09T09:34:12.604Z"
"subcategories": [
{
"status": "1",
"_id": "5bbc762fb6160b29f05854c8",
"createdAt": "2018-10-09T09:34:39.008Z",
"videos": [
{
"views": 0,
"createdAt": "2018-10-12T11:40:08.752Z",
"_id": "5bc08818ed05cf1c5e01103c",
"user_id": "5ba08df7a68f5f1e43f05983",
"description": "sdf",
"size": "30.76 MB",
"duration": "11:11",
"video": "https://www.gpnext.org"
},
{
"views": 10,
"createdAt": "2018-10-12T11:40:08.752Z",
"_id": "5bc08818ed05cf1c5e01103d",
"user_id": "5ba08df7a68f5f1e43f05984",
"description": "qwerty",
"size": "35.76 MB",
"duration": "10:10",
"video": "https://www.gpnext1.org"
}
]
},
]
}
I am trying to update Views value only but when i am executing bellow mentioned query it is deleting all video data and inserting only views field there. can someone guide me to update Views value without effecting other fields values. here is my query for updating view value.
VideoCategory.findOneAndUpdate(
{'subcategories.videos._id' : mongoose.Types.ObjectId(req.body.video_id)},
{ $set :
{
'subcategories.$.videos': {
'views' : 4
}
}
},
function(err, category){
if (err)
return res.send({ status: false, message: 'error in Increment'});
else
return res.json({ status: true, message: 'View incremented'});
}
);
Positional operator ($) works only for arrays with one-level depth. In your case you need positional filtered operator which is available in MongoDB 3.6 or higher. Try:
db.col.update(
{ _id: "5bbc7614b6160b29f05854c7" },
{ $set: { "subcategories.$[subcategory].videos.$[video].views": 4 }},
{ arrayFilters: [ { "subcategory._id": "5bbc762fb6160b29f05854c8" }, { "video._id": "5bc08818ed05cf1c5e01103c" } ] })
You set json object with only view key, thats why all other data expect "view" are deleted, in this case you need to use positional operator twice, like this
{
$set : {
"subcategories.$.videos.$.views": "4"
}
But MongoDb has limitation on positional operator.
The positional operator can be used only once in a query.
Here is the issue link https://jira.mongodb.org/browse/SERVER-831
I suggest two solution, but highly recommend to use first one
Create another Video Collection, and save video _id(pointer) into your array, than you can easily make any update query you want
Fetch your data, then do some changes, after that update whole data you fetched

MongoDB: Query model and check if document contains object or not, then mark / group result

I have a Model called Post, witch contains an property array with user-ids for users that have liked this post.
Now, i need to query the post model, and mark the returned results with likedBySelf true/false for use in by client - is this possible?
I dont have to store the likedBySelf property in the database, just modify the results to have that property.
A temporary solution i found was to do 2 queries, one that finds the posts that is liked by user x, and the ones that have not been liked by user x, and en map (setting likedBySelf true/false) and combine the 2 arrays and return the combined array. But this gives some limitations to other query functions such as limit and skip.
So now my queries looks like this:
var notLikedByQuery = Post.find({likedBy: {$ne: req.body.user._id}})
var likedByQuery = Post.find({likedBy: req.body.user._id})
(I'm using the Mongoose lib)
PS. A typical post can look like this (JSON):
{
"_id": {
"$oid": "55fc463c83b2d2501f563544"
},
"__t": "Post",
"groupId": {
"$oid": "55fc463c83b2d2501f563545"
},
"inactiveAfter": {
"$date": "2015-09-25T17:13:32.426Z"
},
"imageUrl": "https://hootappprodstorage.blob.core.windows.net/devphotos/55fc463b83b2d2501f563543.jpeg",
"createdBy": {
"$oid": "55c49e2d40b3b5b80cbe9a03"
},
"inactive": false,
"recentComments": [],
"likes": 8,
"likedBy": [
{
"$oid": "558b2ce70553f7e807f636c7"
},
{
"$oid": "559e8573ed7c830c0a677c36"
},
{
"$oid": "559e85bced7c830c0a677c43"
},
{
"$oid": "559e854bed7c830c0a677c32"
},
{
"$oid": "559e85abed7c830c0a677c40"
},
{
"$oid": "55911104be2f86e81d0fb573"
},
{
"$oid": "559e858fed7c830c0a677c3b"
},
{
"$oid": "559e8586ed7c830c0a677c3a"
}
],
"location": {
"type": "Point",
"coordinates": [
10.01941398718396,
60.96738099591897
]
},
"updatedAt": {
"$date": "2015-09-22T08:45:41.480Z"
},
"createdAt": {
"$date": "2015-09-18T17:13:32.426Z"
},
"__v": 8
}
#tskippe you can use a method like following to process whether the post is liked by the user himself and call the function anywhere you want.
var processIsLiked = function(postId, userId, doc, next){
var q = Post.find({post_id: postId});
q.lean().exec(function(err,res){
if(err) return utils.handleErr(err, res);
else {
if(_.find(doc.post.likedBy,userId)){ //if LikedBy array contains the user
doc.post.isLiked = true;
} else {
doc.post.isLiked = false;
}
});
next(doc);
}
});
}
Because you are using q.lean() you dont need to actually persist the data. You need to just process it , add isLiked field in the post and send back the response. **note that we are manuplating doc directly. Also you chan tweek it to accept doc containing array of posts and iterating it and attach an isLiked field to each post.
I found that MongoDB's aggregation with $project tequnique was my best bet. So i wrote up an aggregation like this.
Explanation:
Since i want to keep the entire document, but $project purpose is to modify the docs, thus you have to specify the properties you want to keep. A simple way of keeping all the properties is to use "$$ROOT".
So i define a $project, set all my original properties to doc: "$$ROOT", then create a new property "likedBySelf", which is marked true / false if a specified USERID is in the $likedBy set.
I think that this is more clean and simple, than querying every single model after a query to set a likedBySelf flag. It may not be faster, but its cleaner.
Model.aggregate([
{ $project: {
doc: "$$ROOT",
likedBySelf: {
$cond: {
"if": { "$setIsSubset": [
[USERID],
"$likedBy"
]},
"then": true,
"else": false
}
}
}}
]);

Resources