retrieve elements by a superset of their field with mongoose - node.js

My mongoose schema for Recipe looks like this:
const mongoose = require("mongoose");
const { Schema } = mongoose;
const RecipeSchema = new Schema({
recipeName: {
type: String,
required: [true, "Please add a recipeName"],
maxlength: [99, "max 50 chars are allowed for the recipeName"],
},
image: {
type: String,
},
ingredients: [
{
quantity: {
type: Number,
required: true,
},
ingredient: {
type: Schema.ObjectId,
ref: "Ingredient",
},
},
],
description: [
{
type: String,
required: [true, "Please add a description"],
maxlength: [5000, "max 50 chars are allowed for the description"],
},
],
});
module.exports = mongoose.model("Recipe", RecipeSchema);
Given a list of ingredients' ids, I want to be able to find all the recipes whose ingredients list is a subset of the list I am searching by.
I tried a couple of things to no avail, like for instance given
const ingredients = [
ObjectId('614af0b845d87d639b1d337a'),
ObjectId('614b12bebfbe29d357515c63'),
]
trying to retrieve by
Recipe.find({ ingredients: {$not: { $elemMatch: { $nin: ingredients } } }})
This doesn't work because I am not taking into account the right field ingredient, but I do not know where that should be.

Given ingredients is an array with ingredients ids, which needs to be a superset of the ingredients array in the recipe, this query works:
Recipe.find({ ingredients: { $not: { $elemMatch: { ingredient: { $nin: ingredients } } } } })

Related

Pushing items to array in mongoose

i am trying to create items to specific id of collection using nodejs and mongoose but i am getting CastError which i shared below picture. So goal is, I binded items id in collection schema because collection has many items and when i create items to the specific id of collection i want to push them to items array in the collection schema.
ERROR
ITEMS ROUTE
itemRouter.post("/:collectionId", JWTAuthMiddleware, async (req, res, next) => {
const {name} = req.body
if (req.params.collectionId.length !== 24)
return next(createHttpError(400, "Invalid ID"));
const collection = await CollectionModal.findByIdAndUpdate(
req.params.collectionId,
{
$push : { items: { ...req.body, owner: req.user._id, id: uuidv4() } },
},
{ new: true }
);
if (!collection)
return next(
createHttpError(
400,
`The id ${req.params.collectionId} does not match any collections`
)
);
res.send(collection);
});
Collection schema
import mongoose from "mongoose";
const { Schema, model } = mongoose;
const collectionSchema = new Schema(
{
name: { type: String },
description: { type: String },
topic: { type: String },
image: { type: String },
additionalCustomFields: {
fieldNumber: { type: Number },
fieldName: { type: String },
fieldType: { type: String },
fieldChecked: { type: Boolean },
fieldDate: { type: Date },
},
owner: { type: Schema.Types.ObjectId, ref: "User" },
items: [{ type: Schema.Types.ObjectId, ref: "Item" }],
},
{ timestamps: true }
);
// collectionSchema.index({ "$**": "text" });
export default model("Collection", collectionSchema);
ITEM schema
import mongoose from "mongoose";
const { Schema, model } = mongoose;
const itemSchema = new Schema(
{
name: { type: String },
description: { type: String },
topic: { type: String },
image: { type: String },
comments: [
{
user: { type: Schema.Types.ObjectId, ref: "User", required: true },
text: { type: String },
},
],
tags: { type: String },
owner: { type: Schema.Types.ObjectId, ref: "User" },
likes: [{ type: Schema.Types.ObjectId, ref: "User" }],
collections: { type: Schema.Types.ObjectId, ref: "Collection" },
},
{ timestamps: true }
);
itemSchema.index({ "$**": "text" });
export default model("Item", itemSchema);
Are you sure that using referenced documents is the correct way to go here? Mongoose is complaining because you are trying to push whole objects to the "items" array, instead of ObjectIds of referenced documents.
You have 2 choices here:
1. use embedded documents
This way you can easily store objects directly to the items array, which does not allow you to store the Items in a seperate collection
2. first create Item documents, then push the reference to items array
In this case you have to first create the items in Item collection. Afterwards you can map the result to only ObjectIds and push these to items array of your collection document.

Query MongoDB w/Mongoose on an Array of objects

I am having a rough time querying my collection of data, Im building a simple E-Comm platform and in that platform i want to be able to have categories for the products. the idea is simple, each category should be its own object in the database that has a collection of products, however each product can have multiple categories, this is for products that might fit into one or more category definitions.
here is the model for the categories
import mongoose from "mongoose";
const categorySchema = mongoose.Schema(
{
cat_name: {
type: String,
required: true,
unique: true,
},
cat_items: [
{
product: {
type: mongoose.Schema.Types.ObjectId,
ref: "Product",
},
},
],
// Adds a relationship between a User admin and a product
// this is useful to see which admin, if multiple created a category
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "User",
},
},
{ timestamps: true }
);
const Category = mongoose.model("Category", categorySchema);
export default Category;
Here is the Product model
const productSchema = mongoose.Schema(
{
// Adds a relationship between a User admin and a product
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "User",
},
name: {
type: String,
required: true,
},
image: {
type: String,
required: true,
},
brand: {
type: String,
required: true,
},
category: {
type: String,
required: true,
},
categories: [
{
category: {
type: mongoose.Schema.Types.ObjectId,
ref: "Category",
},
},
],
description: {
type: String,
required: true,
},
reviews: [reviewSchema],
rating: {
type: Number,
required: true,
default: 0,
},
numReviews: {
type: Number,
required: true,
default: 0,
},
price: {
type: Number,
required: true,
default: 0,
},
countInStock: {
type: Number,
required: true,
default: 0,
},
},
{
timestamps: true,
}
);
const Product = mongoose.model("Product", productSchema);
export default Product;
I believe the overall problem is the way i have the models setup, because any query i run on the categories object to return the products associated with it in cat_items, does not return any products it simply returns an empty array.
here is the simple function to return data
const products = await category.map((product)=> { return Product.findById(product._id)})
here im pulling out the array of category items, and mapping over them looking for the product in the database by the _id which is what product is example: 620e626203a59f0004e5a8c6 this in theory if you had 3 items, would return a new array of the product objects that i can send to the client, however every attempt only returns a [] and im pretty sure this is all do in part to the way i have the models setup.
for reference this is what returning a category looks like in postman:
{
"category": {
"_id": "62102f5c2b990d0e7c7d9bf8",
"cat_name": "Pendants",
"user": "620af3311fe4c247904b84d9",
"cat_items": [
{
"_id": "620e626203a59f0004e5a8c6"
},
{
"_id": "620e626203a59f0004e5a8c6"
}
],
"createdAt": "2022-02-18T23:44:28.697Z",
"updatedAt": "2022-02-18T23:54:23.103Z",
"__v": 2
},
"products": [
{},
{}
]
}
where im trying to fill the products array with the actual products stashed in the database

Extract data from a mongoose object

I have a Shop Model
const Shop = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
shop_name: { type: String },
products: {_id: mongoose.Schema.Types.ObjectId,type:Array},
});
and a product schema
const Product = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
title: { type: String },
description: { type: String },
shop: { type: mongoose.Schema.Types.ObjectId, ref: "Shop" },
});
I'm trying to access a product within the products array of the Shop model, so that I can update it.
I've looked online a lot but couldn't quite find what I'm looking for. I need to access a very specific product within the products array with the given parameters, which are the id of the shop and the id of the product.
This is what I tried to do
const item = await Product.findOne({_id} , 'products').find({"products._id" : productId})
But what this does is it gives a mongoose object if the second find method hits a match
[
{
products: [ [Object] ],
_id: 617f1bca39a5a43c1a981060,
butik: 'scsbutik',
butik_slug: 'egzbutikcom-1000010',
butik_image: 'https://webizade.com/bm/img/butik-10.jpg',
butik_points: '9.8',
butik_order_count: 45,
butik_success_order_count: 42,
butik_refund_count: 3,
is_butik_refund: true,
__v: 0,
login: []
}
]
I need to access the object INSIDE the products array and update that product.
Appreciate any help in advance.
don't use by default _id format I suggest declare a different value and then using populate method you can do it
const Product = mongoose.Schema({
shopId: mongoose.Schema.Types.ObjectId,
title: { type: String },
description: { type: String },
shop: { type: mongoose.Schema.Types.ObjectId, ref: 'Shop' },
});
THEN ROUTING
const item = await Product.find(_id).populate('shopId')

find and update data of a certain user's nested data in MongoDB Mongoose

I have a database of users and their info of the places they checked. Each time an user clicks on a particular place, I want to check in the data base if he hasn't yet clicked on that place. User's schema:
const userSchema = new mongoose.Schema({
email: {
type: String,
required: [true, 'Įveskite el. paštą'],
unique: true,
lowercase : true,
validate: [isEmail, 'Įveskite tinkamą el. paštą']
},
password: {
type: String,
required: [true, 'Įveskite slaptažodį'],
minlength: [6, 'Minimalus simbolių skaičius – 6']
},
placeinfo: [
{
title: String,
score: String
}
]
});
const User = mongoose.model('user', userSchema);
If I search for a place title, it searches in all database for all users, but I want it to search for a particular user, for example testuser#test.com and if he has "Kalnaberžės piliakalnis" in his placeInfo array.
{
_id: 60d2eecea704c60a705ef3fb,
email: 'testuser#test.com',
password: '$2a$10$PN3krKgwsdtQV1Y/Z.wQ7eeJbQ9GIrOEjHcpGlYCQR33Oxub5bJ4a',
placeinfo: [
{ _id: 60d2eefba704c60a705ef3fe, title: 'Kalnaberžės piliakalnis' },
{ _id: 60d2ef10ea1c3437a4e47c31, title: 'Pakalniškių piliakalnis' },
],
__v: 0
}
You can use this aggregation:
db.collection.aggregate([
{
"$match": {
"email": "testuser#test.com",
"placeinfo.title": "Kalnaberžės piliakalnis"
}
}
])
Or basicly find:
db.collection.find({
"email": "testuser#test.com",
"placeinfo.title": "Kalnaberžės piliakalnis"
})
Playground

Mongoose find recursive nested object by id

I'm building a note taking app in which there can be as many nested notes inside one another as the user wants, Similar to folder and file structure expect here everything is a note, I have a note model inside it there is a sub_notes array, I want to be able to find a note whether it is a note at the top of the document or nested inside the sub_notes array, I'm passing the id of the note to which I want to add the next note, I have tried graphlookup but when I use the query it is returning an empty array
This is my Note model
const noteSchema = new Schema({
icon: {
type: String,
},
banner: {
type: String,
},
name: {
type: String,
required: true,
},
user_id: {
type: String,
required: true,
},
date: {
type: Date,
required: true,
default: Date.now,
},
sub_notes: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "notes",
},
],
content: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "blocks",
},
],
});
module.exports = Note = mongoose.model("notes", noteSchema);
This is the query I'm using to find the nested note, The path is a dot seperated string with ids of the notes & the note is the actual note object with the name and other data.
So for ex:
The note
{
id: "123123123",
sub_notes: [{
id: "xyz"
}]
}
So the path here will be 123123123.xyz
var {
note,
path
} = req.body;
var newPath = path.split(".");
const parentNote = Note.findById(newPath[0]);
newPath.splice(0, 1);
console.log(newPath[newPath.length - 1]);
Note.aggregate([{
$match: {
$id: newPath[newPath.length - 1]
}
},
{
$graphLookup: {
from: "notes",
startWith: "$id",
connectFromField: "sub_notes",
connectToField: "id",
depthField: "level",
as: "sub_notes",
},
}
]).exec((err, notes) => {
console.log(notes);
});

Resources