Adding object into an array of a document in mongoDB - arrays

Scenario:
There are two collections, one contains products and the other contains reviews. Both products and reviews collection have a field called "productID". Task is to gather all fields from individual review document and add them to the corresponding product document.
My approach:
I am trying to collect all the products and then iterate through each one.
This is to individually extract productID and pass it over to find all reviews that match with the productID.
All the reviews are then stored in a variable.
4.Finally, I try to update the current product by pushing the extracted fields from documents that match with productID
Code:
var cursor = db.products.find({}, {_id: 0})
while (cursor.hasNext()) {
var currDoc = cursor.next();
var pID = currDoc.productID;
var revs = db.reviews.find({productID: pID}, {
_id: 0,
stars: 1,
reviewTitle: 1,
reviewText: 1,
})
db.products.update({ productID: pID }, { $push: { reviews: revs } })
Expect:
products {
productID: ##,
productName: "asdfghjkl",
productPrice: ##.##,
reviews:
[
{
stars: 1,
reviewTitle: "Avoid",
reviewText: "Not very great"
}
]
}
Actual:
BSONError: cyclic dependency detected

Convert your PID to string. it will return as an object which needs to be changed as string
db.products.find().forEach(function(doc){
var reviews = db.reviews.find({'productId':str(doc._id)}, {
_id: 0,
stars: 1,
reviewTitle: 1,
reviewText: "Not very great",
})
db.products.update({ "_id": doc._id },{ "$set": { "reviews": reviews } });
})

Related

FaunaDB get document stored with foreign key

I've created two tables in FaunaDB, "customers" and "orders". I created an Index, to get a document by a data.transactionObj.transactionId.
So far so good, but how can I extend the query below, to get the document referenced in data.customerRef?
This query:
Map(
Paginate(Match(Index("orders_by_trx"), "220704142800610948")),
Lambda("x", Get(Var("x")))
)
returns
{
data: [
{
ref: Ref(Collection("orders"), "336256180042072264"),
ts: 1660641327310000,
data: {
customerRef: Ref(Collection("customers"), "340132521031237836"),
transactionObj: {
transactionId: "220704142200610948",
status: "transmitted",
refno: "l56pwwmiHuber",
currency: "CHF",
paymentMethod: "TWI",
amount: 100
},
cart: {
cards: [
{
id: "629f6e2f909b7c89f114a874",
name: "MCA Couchtisch Sakura",
verkaufspreis: 99,
anzahl: 1,
}
]
}
}
}
]
}
You can compose the response as an arbitrary JSON value.
You haven't specified what structure you need, so I'm assuming that you want something like:
{
order: <order document>,
customer: <customer document>,
}
To accomplish that, here's an updated query that should work:
Map(
Paginate(Match(Index("orders_by_trx"), "220704142800610948")),
Lambda(
"order_ref",
Let(
{
order: Get(Var("order_ref")),
customer_ref: Select(["data", "customerRef"], Var("order")),
customer: Get(Var("customer_ref")),
},
{
order: Var("order"),
customer: Var("customer")
}
)
)
)
The query used the Let function to hold intermediate results and compose the response structure.
Fauna queries can perform any number of reads and writes provided that you don't exceed the transaction limits.

Graphql mutation query : how to access the data elements

const MUTATION_QUERY = gql`
mutation MUTATION_QUERY(
$name: bigint!
) {
insert_name(
objects: {
name: $name
}
) {
returning {
id
name
}
}
}
`;
const [onClick, { error, data }] = useMutation<{}, {}>(MUTATION_QUERY, {
variables: {
name: 1234,
},
});
My mutation query is inserting name in my table and autogenerating the id. On console logging the data variable I can view the fields id and name in the data object. But I am not able to access them them individually. How can I console.log "id". Thank you.
the console.log(data) looks like : {insert_name: {...}}
which expands to :
insert_name:
returning: Array(1)
0: {id: 1, name: 1234}
length: 1
_proto_: Array(0)
_typename: "insert_name_mutation_response
You can access the fields of an object with .
For example, if your object looks like this -
data = {
id: 1,
name: 'Jane',
}
You can get just the id with data.id
This works no matter how many layers deep your object may go, so take this example -
data = {
person: {
id: 1,
name: 'Jane',
}
}
You could get the id of person with data.person.id.
console.log(data.insert_name.returning[0].id) will give you the id returned.
For it to work in typescript we need to change the query to add the return type of data
const [onClick, { error, data }] = useMutation<{ReturnInsertNameProps}, {}>(MUTATION_QUERY, {
variables: {
name: 1234,
},
});
interface ReturnInsertNameProps {
insert_name: ReturnQueryProps;
}
interface ReturnProps {
returning: MessageProps[];
}
interface NameProps {
id: number;
name: number;
}
We can also use onCompleted method provided in useMutation if we want to process the result of the query.

Normalizing my JSON response using normalizr with redux

I'm trying to reshape my Redux store so I can query by and filter my data easily.
I have an API endpoint that returns back an order.
The order looks like this at a high level:
Order
+ references
+ item_details
- category_id
- product_id
- product
So an order has many references, and the references have many item_details.
The item details has a category and product.
const data = {
id: 3939393,
discount: 0,
references: [
{
id: 123,
order_id: 3939393,
name: "order 1",
item_details: [
{
id: 100,
order_id: 3939393,
product_id: 443,
sort_order: 1,
category_id: 400,
product: {
id: 443,
name: "hello world",
price: 199
}
},
{
id: 200,
order_id: 3939393,
product_id: 8080,
sort_order: 2,
category_id: 500,
product: {
id: 8080,
name: "hello json",
price: 299
}
}
]
}
]
};
export default data;
So far my schema definitions look like this:
export const productSchema = new schema.Entity("products");
export const itemDetailSchema = new schema.Entity("itemDetails", {
product: productSchema
});
export const references = new schema.Entity("references", {
item_details: new schema.Array(itemDetailSchema)
});
export const orderSchema = new schema.Entity("orders");
const result = normalize(data, orderSchema);
console.log("result is: " + JSON.stringify(result));
How can I get the products in its own section in the normalized JSON? Currently the products are still embedded inside the JSON.
Would I use normalizr to create state "index" type looks like this:
productsInReferences: {
123: [400, 8080]
}
If not, how exactly to I generate these types of JSON lookups?
I created a codesandbox with my code so far.
https://codesandbox.io/s/xpl4n9w31q
I usually think Normalization schemes from the deepest nested structure all the way to the one containing the data for those cases. Remember that you can do explicit array definition through [someSchema], also every level should be contained on a nested schema, in this case you forgot the references: [referenceSchema] on the orderSchema.
The correct normalization would be:
// Product Schema
const productSchema = new schema.Entity("products");
// Item detail schema containing a product schema OBJECT within the property product
const itemDetailSchema = new schema.Entity("itemDetails", {
product: productSchema
});
// Reference schema containing an ARRAY of itemDetailSchemes within the property item_details
const referenceSchema = new schema.Entity("references", {
item_details: [itemDetailSchema]
});
// Order schema containing an ARRAY of reference schemes within the property references
const orderSchema = new schema.Entity("orders", {
references: [referenceSchema]
});
const result = normalize(data, orderSchema);
console.dir(result);
This would be the result after normalizing.
Object
entities:
itemDetails: {100: {…}, 200: {…}}
orders: {3939393: {…}}
products: {443: {…}, 8080: {…}}
references: {123: {…}}
result: 3939393

Mongoose Pull objects by their property values from an array (possibly empty) [duplicate]

Here is array structure
contact: {
phone: [
{
number: "+1786543589455",
place: "New Jersey",
createdAt: ""
}
{
number: "+1986543589455",
place: "Houston",
createdAt: ""
}
]
}
Here I only know the mongo id(_id) and phone number(+1786543589455) and I need to remove that whole corresponding array element from document. i.e zero indexed element in phone array is matched with phone number and need to remove the corresponding array element.
contact: {
phone: [
{
number: "+1986543589455",
place: "Houston",
createdAt: ""
}
]
}
I tried with following update method
collection.update(
{ _id: id, 'contact.phone': '+1786543589455' },
{ $unset: { 'contact.phone.$.number': '+1786543589455'} }
);
But it removes number: +1786543589455 from inner array object, not zero indexed element in phone array. Tried with pull also without a success.
How to remove the array element in mongodb?
Try the following query:
collection.update(
{ _id: id },
{ $pull: { 'contact.phone': { number: '+1786543589455' } } }
);
It will find document with the given _id and remove the phone +1786543589455 from its contact.phone array.
You can use $unset to unset the value in the array (set it to null), but not to remove it completely.
You can simply use $pull to remove a sub-document.
The $pull operator removes from an existing array all instances of a value or values that match a specified condition.
Collection.update({
_id: parentDocumentId
}, {
$pull: {
subDocument: {
_id: SubDocumentId
}
}
});
This will find your parent document against given ID and then will remove the element from subDocument which matched the given criteria.
Read more about pull here.
In Mongoose:
from the document:
To remove a document from a subdocument array we may pass an object
with a matching _id.
contact.phone.pull({ _id: itemId }) // remove
contact.phone.pull(itemId); // this also works
See Leonid Beschastny's answer for the correct answer.
To remove all array elements irrespective of any given id, use this:
collection.update(
{ },
{ $pull: { 'contact.phone': { number: '+1786543589455' } } }
);
To remove all matching array elements from a specific document:
collection.update(
{ _id: id },
{ $pull: { 'contact.phone': { number: '+1786543589455' } } }
);
To remove all matching array elements from all documents:
collection.updateMany(
{ },
{ $pull: { 'contact.phone': { number: '+1786543589455' } } }
);
Given the following document in the profiles collection:
{ _id: 1, votes: [ 3, 5, 6, 7, 7, 8 ] }
The following operation will remove all items from the votes array that are greater than or equal to ($gte) 6:
db.profiles.update( { _id: 1 }, { $pull: { votes: { $gte: 6 } } } )
After the update operation, the document only has values less than 6:
{ _id: 1, votes: [ 3, 5 ] }
If you multiple items the same value, you should use $pullAll instead of $pull.
In the question having a multiple contact numbers the same use this:
collection.update(
{ _id: id },
{ $pullAll: { 'contact.phone': { number: '+1786543589455' } } }
);
it will delete every item that matches that number. in contact phone
Try reading the manual.

update single item within array where multiple conditions apply to any array item

I have a basic mongoose model with an attribute instruments which represents an array. Therefore it consists multiple items, which each have the following attributes: name, counter. The document itself has an autogenerated _id of type ObjectID.
Model
var ParticipationSchema = new Schema({
instruments: [{
name: String,
counter: Number
}],
// etc.
},
{
timestamps: true
});
I'd like now to change exactly 1 item within the instruments array, only if it matches the following requirements:
The document id has to equal 58f3c77d789330486ccadf40
The instruments-item's name which should be changed has to equal 'instrument-1'
The instrument-item's counter has to be lower than 3
Query
let someId = '58f3c77d789330486ccadf40';
let instrumentName = 'instrument-1'
let counter = 3;
Participation.update(
{
$and: [
{ _id: mongoose.Types.ObjectId(someId) },
{ 'instruments.name': instrumentName },
{ 'instruments.counter': { $lt: counter } }
]},
{
$set: {
'instruments.$.currentcounter' : counter
}
},
(err, raw) => {
// ERROR HANDLING
}
});
Let's assume I have 3 entries within the instruments-attribute:
"instruments": [
{
"name": "instrument-1",
"counter": 2
},
{
"name": "instrument-1",
"counter": 2
},
{
"name": "instrument-1",
"counter": 2
}
]
Desired behaviour: change the first element's counter attribute to 3, no matter, when running the update code 1 time, do no action when running it more times.
Actual behaviour:
it changes the 1st element's counter attribute to 3 when running it the 1st time
it changes the 2nds element's counter attribute to 3 when running it the 2nd time
it changes the 3rds element's counter attribute to 3 when running it the 3rd time
Although the queries are anded, they don't seem to run element-wise. To resolve the issue, $elemMatch can be used:
Participation.update({
_id: mongoose.Types.ObjectId(someId),
'instruments': {
$elemMatch: {
'name': instrumentName,
'counter': { $lt: counter } }}
},
// etc.
more info:
API reference on $elemMatch
Thanks to #VEERAM's for pointing out that this kind of behaviour is also documented the mongodb homepage.

Resources