I am making a POST request to the Shopify API to send the product object to '/cart/add.js' but I keep getting a 400 response. All of the values I am capturing from the form are correct. So it is the structure of my object that is wrong. Here is what the object looks like:
const items = {
products: [
{
quantity: quantity,
id: variantId,
properties: {
"I am a": document.getElementById('i-am-a').value,
"Company Name": document.getElementById('company-name').value,
"Email": document.getElementById('email-capture').value,
},
}
]
};
Had to find this article on the Cart API
https://shopify.dev/api/ajax/reference/cart
const data = {
items: [
{
quantity: quantity,
id: variantId,
properties: {
"I am a": document.getElementById('i-am-a').value,
"Company Name": document.getElementById('company-name').value,
"Email": document.getElementById('email-capture').value,
},
}
]
};
the products array should be named items
Related
Below is my code to display review array data which is part of the restaurant collection object:
async get(reviewId) {
const restaurantsCollection = await restaurants();
reviewId = ObjectId(reviewId)
const r = await restaurantsCollection.findOne(
{ reviews: { $elemMatch: { _id : reviewId } } },
{"projection" : { "reviews.$": true }}
)
return r
}
My object looks like:
{
_id: '6176e58679a981181d94dfaf',
name: 'The Blue Hotel',
location: 'Noon city, New York',
phoneNumber: '122-536-7890',
website: 'http://www.bluehotel.com',
priceRange: '$$$',
cuisines: [ 'Mexican', 'Italian' ],
overallRating: 0,
serviceOptions: { dineIn: true, takeOut: true, delivery: true },
reviews: []
}
My output looks like:
{
"_id": "6174cfb953edbe9dc5054f99", // restaurant Id
"reviews": [
{
"_id": "6176df77d4639898b0c155f0", // review Id
"title": "This place was great!",
"reviewer": "scaredycat",
"rating": 5,
"dateOfReview": "10/13/2021",
"review": "This place was great! the staff is top notch and the food was delicious! They really know how to treat their customers"
}
]
}
What I want as output:
{
"_id": "6176df77d4639898b0c155f0",
"title": "This place was great!",
"reviewer": "scaredycat",
"rating": 5,
"dateOfReview": "10/13/2021",
"review": "This place was great! the staff is top notch and the food was delicious! They really know how to treat their customers"
}
How can I get the output as only the review without getting the restaurant ID or the whole object?
So the query operators, find and findOne do not allow "advanced" restructure of data.
So you have 2 alternatives:
The more common approach will be to do this in code, usually people either use some thing mongoose post trigger or have some kind of "shared" function that handles all of these transformations, this is how you avoid code duplication.
Use the aggregation framework, like so:
const r = await restaurantsCollection.aggregate([
{
$match: { reviews: { $elemMatch: { _id : reviewId } } },
},
{
$replaceRoot: {
newRoot: {
$arrayElemAt: [
{
$filter: {
input: "$reviews",
as: "review",
cond: {$eq: ["$$review._id", reviewId]}
}
},
0
]
}
}
}
])
return r[0]
thank you in advance for any help.
My problem is essentially to add data to a specific sub document.
I have the following models in my NodeJS server:
MODELS
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const dataSchema = new Schema({
time: Date,
value: String
});
const nodeSchema = new Schema({
name: String,
description: String,
number: Number,
status: String,
lastSeen: Date,
data: [dataSchema]
});
const siteSchema = new Schema({
code: String,
name: String,
description: String,
totalNodes: Number,
nodes: [nodeSchema]
});
const Site = mongoose.model('site',siteSchema);
module.exports = Site;
Which basically looks like this:
Example
{
"_id": "5fa169473a394829bc485069",
"code": "xfx3090",
"name": "Name of this site",
"description": "Some description",
"totalNodes": 2,
"__v": 0,
"nodes": [
{
"_id": "5fa1af361e085b516066d7e2",
"name": "device name",
"description": "device description",
"number": 1,
"status": "Offline",
"lastSeen": "2020-11-03T19:27:50.062Z",
"data": [
{
"Date": "2019-01-01T00:00:00.000Z",
"value": "12"
},
{
"Date": "2019-01-01T00:00:00.000Z",
"Value": "146"
}
]
},
{
"_id": "5fa1b10f4f24051520f85a58",
"name": "device name",
"description": "device description",
"number": 2,
"status": "Offline",
"lastSeen": "2020-11-03T19:35:43.409Z",
"data": [
{
"Date": "2019-01-01T00:00:00.000Z",
"Value": "555"
}
]
}
]
}
]
As you can see I have created two dummy nodes with some random data.
My question now is, say I want to add some data to Node 1. How will this code look?
I've tried many variations and attempted many different things without any luck. I know this would be easier by using the Object Id's, but I was hoping there is a way around this.
My Best result so far was with this code, but unfortunately it doesn't add any data.
addNodeData: async (req,res,next) => {
const {siteCode} = xfx3090; //req.params
const { nodeNumber } = 1; //req. params - just to show example
const nodeData = await Site.findOneAndUpdate({'code': siteCode, 'node.number': nodeNumber}, {$push: {'data':{'time': Date.now(), 'value':1223}}});
res.status(200).json({message:'success'});
}
Thank you in advance!
You need the positional operator $.
The query you want is something like this:
db.collection.update({
"_id": "5fa169473a394829bc485069",
"nodes._id": "5fa1af361e085b516066d7e2"
},
{
"$push": {
"nodes.$.data": {
"Date": "newDate",
"value": "newValue"
}
}
})
The first part is to find the document. I'm assuming nodes._id is not unique so I match _id too.
Then, with the pointer in the document you want to add the new data, you use $push into nodes.$.data. So, in the filed data there will be a new object.
A mongo plauground example is here
I am currently using the code below in node.js to find and return data on various nesting levels from a mongo database. I'd like to add another layer of nesting (as mentioned in #3).
Collection:
[
{
"title": "Category A",
"link": "a",
"items": [
{
"title": "Item C",
"link": "a-c",
"series": [
{
"title": "Item C X",
"link": "a-c-x"
},
{
"title": "Item C Y",
"link": "a-c-y"
},
]
},
{
"title": "Item D",
"link": "a-d"
}
]
},
{
"title": "Category B",
"link": "b"
}
]
The query:
const doc = await ... .findOne(
{
$or: [
{ link: id },
{ "items.link": id },
{ "items.series.link": id }
],
},
{
projection: {
_id: 0,
title: 1,
link: 1,
items: { $elemMatch: { link: id } },
},
}
);
Intended results:
(works) if link of the document is matched,
(works) there should only be an object with the title and link returned
e.g.
value of id variable: "a"
expected query result: { title: "Category A", link: "a"}
(works) if items.link of subdocument is matched,
(works) it should be the same as above + an additional element in the items array returned.
e.g.
value of id variable: "a-c"
expected query result: { title: "Category A", link: "a", items: [{ title: "Item C", link: "a-c" }]}
(works) if items.series.link of sub-subdocument is matched
(struggling with this) it should return the same as in 2. + an additional element inside the matched items.series
e.g.
value of id variable: "a-c-y"
expected query result: { title: "Category A", link: "a", items: [{ title: "Item C", link: "a-c", series: [{ title: "Item C Y", link: "a-c-y" }]}]}
current query result: The whole Category A document with all sub-documents
Questions:
a.) How do I modify the projection to return the expected output in #3 as well?
b.) Is the approach above sound in terms of reading speed from a denormalized structure? I figured there'd probably need to be indexes on link, items.link and items.series.link as they are all completely unique in the document, but maybe there is a way to achieve the above goal with a completely different approach?
Ended up with going half-way via mongodb and get the full item for both - when the item link is matched and the series link is matched:
projection: {
_id: 0,
title: 1,
link: 1,
items: { $elemMatch: { $or: [
{ link: id },
{"series.link": id }
]}},
}
After that javascript filters the series array to see if the series is matched:
doc?.items?.[0]?.series?.find(item => item.link === id)
if the js is truthy (returns an object) we matched a series, if there is a doc, but the js is falsy we matched an item result.
Although not a full mongodb solution and there is definitely room for improvement the above seems to achieve the end goal to be able to distinguish between category, item and series results.
I am using Express + Apollo Server + GraphQL + Mongoose + MongoDB to "perform" several CRUD operations on a database.
One of the operations I am trying to make is to get the sites from the database and expand its users with their information for each record like this:
query {
getSites {
id
name
owner {
name
email
}
origins
}
}
Instead, I am getting these results:
{
"data": {
"getSites": [{
"id": "5cae36182ab9b94e94ba9af5",
"name": "Test site 1",
"owner": [{
"name": null,
"email": null
}
],
"origins": [
"test1",
"test2"
]
}, {
"id": "5cae3a3798c302247c036544",
"name": "Test site 2",
"owner": [{
"name": null,
"email": null
}
],
"origins": [
"test1",
"test2"
]
}
]
}
}
This is my typeDef code for Site:
import { gql } from 'apollo-server-express';
const site = gql `
extend type Site {
id: ID!
name: String!
origins: [String]
owner: [User]
createdOn: String
updatedOn: String
}
extend type Query {
getSites: [Site]
getSite(id: ID!): Site
}
extend type Mutation {
addSite(name: String!, owner: [String!], origins: [String]): Site
}
`;
export default site;
If I console.log(sites) I see owner is an array of Strings.
Edit:
If I change addSite(name: String!, owner: [User], origins: [String]): Site then I get when compiling:
Error: The type of Mutation.addSite(owner:) must be Input Type but got: [User]
My resolver looks like this:
getSites: async () => await Site.find().exec()
What's the proper way to define relationships today? Thanks.
I just edited my resolver to this:
getSites: async () => {
let sites = await Site.find().exec();
let ownedSites = await User.populate(sites, { path: 'owner' });
return ownedSites;
}
And that solved the errors.
I am trying to send a list of total paid and unpaid client with count along with data from my node API.
In mongoose method, I am stuck at thinking how to go further.
can anyone suggest the best way to achieve this?
router.get("/", ensureAuthenticated, (req, res) => {
Loan.aggregate([
{
$match: {
ePaidunpaid: "Unpaid"
}
}
]).then(function(data) {
console.log(data);
res.render("dashboard", { admin: req.user.eUserType, user: req.user,data:data });
});
});
Loan Model:
const Loan = new Schema({
sName: { type: String },
sPurpose: [String],
sBankName: String,
sBranchName: [String],
nTotalFees: { type: Number },
ePaidunpaid: { type: String ,default:'Unpaid'},
sCashOrCheque: { type: String },
});
Outcome:
Details of a user with a count of paid and unpaid clients
[
Paid:{
// Paid users
},
Unpaid:{
// Unpaid Users
},
]
Well in that case, try this -
Loan.aggregate([
{
$group: {
_id: "$ePaidunpaid",
data: { $push: "$$ROOT" },
count: { $sum: 1 }
}
}
]);
Output would be something like this -
{
"_id": "Paid",
"data": [
// All the documents having ePaidunpaid = Paid
{ _id: "asdasd123 1eqdsada", sName: "Some name", // Rest of the fields },
{ _id: "asdasd123 1eqdsada", sName: "Some name", // Rest of the fields }
],
count: 2
},
{
"_id": "Unpaid",
"data": [
// All the documents of having ePaidunpaid = Unpaid
{ _id: "asdasd123 1eqdsada", sName: "Some name", // Rest of the fields },
{ _id: "asdasd123 1eqdsada", sName: "Some name", // Rest of the fields }
],
count: 2
},
Explanation
First stage of the pipeline $group groups all the documents according to ePaidunpaidfield which only have two values Paid or Unpaid thus rendering only two documents respectively.
Next step is to accumulate original data (documents) being grouped together. This is achieved using $push accumulator on data field, pushing $$ROOT which effectively references the document currently being processed by pipeline stage.
Since you needed count of all paid and unpaid users hence a $sum accumulator to count all the items in each group.