Nested query in Reactive Component - ReactJS - reactjs

I try to update query of the component by using setQuery() prop. My data is structured like this.
{
root : {
category 1: {
item 1 {...},
item 2 {...},
},
category 2: {
item 1 {...},
item 2 {...},
},
...
}
}
I need to get item 1 under the category 1. When user select item 1 I update query like this. But it doesn't work.
this.props.setQuery({
"query": {
"bool": {
"must": [
{
"query_string": {
"query": 'category 1 AND item 1',
"fields": ["item", "category", "keywords"],
"analyzer": "standard"
}
}
]
}
}
});
I try find out how to solve this using nested query. But I couldn't find any use full thing. Please give me any clue to achieve this.

I found a solution for this.
this.props.setQuery({
query: {
bool: {
must: [
{
terms: {
'parentName': ['category 1']
},
},
{
terms: {
'childName': 'item 1',
},
},
],
},
},
});
I think this will helps to someone.

Related

How to query in a nested array and project only the matching items from array?

The structure looks like this:
{
clientName: "client1",
employees: [
{
"employeename": 1,
"configuration": {
"isAdmin": true,
"isManager": false
}
}
{
"employeename": 2,
"configuration": {
"isAdmin": false,
"isManager": false
}
}...
]
},
{
...
}
`
I want to see the employees who are admins inside a specific client, given that, I have the client name. How can I write a query in MongoDB for this? I want to be able to see (project) only employees who match?
Can I combine it to match multiple conditions? for example: someone who is an admin and a manager.
I have tried doing:
db.collection.find({clientName: "client1", "employees.configuration.isAdmin": true}, {"employees.employeename": 1})
This just return all the employees.
I've also tried using $elemMatch, to no avail.
Any help is appreciated.
You can do it with Aggregation framework:
$match - to filter the document based on clientName property
$filter with $eq - to filter only employees that have admins
db.collection.aggregate([
{
"$match": {
"clientName": "client1"
}
},
{
"$set": {
"employees": {
"$filter": {
"input": "$employees",
"cond": {
"$eq": [
"$$this.configuration.isAdmin",
true
]
}
}
}
}
}
])
Working example

adding data to nested document : mongodb

I want to add new record within 'music'. My document looks similar to
{
"username": "erkin",
"email": "erkin-07#hotmail.com",
"password": "b",
"playlists": [
{
"_id": 58,
"name": "asdsa",
"date": "09-01-15",
"musics": {
"song-one":{
"song-one": "INNA - Cola Song (feat. J Balvin)",
"duration": "3.00"
},
"song-two":{
"song-two": "blabla",
"duration": "3.00"
}
}
}
]
}
After navigating to "music" and then using $set to add/update multiple records at once. But new records is getting added in lexicographical manner(behaviour of $set).
I'm using query similar to (so in my case song-four is coming before song-three) :
db.getCollection('myCollection').update({username:"erkin"},{$set:{"playlists.musics.song-three":{...},"playlists.musics.song-four":{...}}})
Is there any way I can add new records to that location in a way my $set query is arranged ?
As playlists is an array:
Option 1: Update the first document in playlists.
Specify the index: 0 for the first document in playlists.
playlists.0.musics.song-three
db.collection.update({
username: "erkin"
},
{
$set: {
"playlists.0.musics.song-three": {
"song-three": "song-three"
},
"playlists.0.musics.song-four": {
"song-four": "song-four"
}
}
})
Sample Demo on Mongo Playground (Option 1)
Option 2: Update all documents in playlists.
With $[] all positional operator.
playlists.$[].musics.song-three
db.collection.update({
username: "erkin"
},
{
$set: {
"playlists.$[].musics.song-three": {
"song-three": "song-three"
},
"playlists.$[].musics.song-four": {
"song-four": "song-four"
}
}
})
Sample Demo on Mongo Playground (Option 2)
Option 3: Update specified document in playlists.
With $[<identifier>] filtered positional operator.
playlists.$[playlist].musics.song-three
db.collection.update({
username: "erkin"
},
{
$set: {
"playlists.$[playlist].musics.song-three": {
"song-three": "song-three"
},
"playlists.$[playlist].musics.song-four": {
"song-four": "song-four"
}
}
},
{
arrayFilters: [
{
"playlist._id": 58
}
]
})
Sample Demo on Mongo Playground (Option 3)

How to fix MongoDB array concatination error?

I have a collection in mongodb with a few million documents. there is an attribute(categories) that is an array that contains all the categories that a document belongs to. I am using following query to convert the array into a comma separated string to add it to SQL server through a spoon transformation.
for example
the document has ["a","b","c",...] and i need a,b,c,.... so i can pit it in a column
categories: {
$cond: [
{ $eq: [{ $type: "$categories" }, "array"] },
{
$trim: {
input: {
$reduce: {
input: "$categories",
initialValue: "",
in: { $concat: ["$$value", ",", "$$this"] }
}
}
}
},
"$categories"
]
}
when i run the query i get the following error and i cannot figure out what the problem is.
com.mongodb.MongoQueryException: Query failed with error code 16702 and error message '$concat only supports strings, not array' on server
a few documents had this attribute as string and not array so i added a type check. but still the issue is there. any help on how to narrow down the issue will be very appreciated.
A few other attributes were the same in the same collection and this query is working fine for the rest of them.
I don't see any problem in your aggregation. It shouldn't give this error. Can you try to update your mongodb version?
However, your aggregation is not working properly reduce wasn't working . I converted it to this:
db.collection.aggregate([
{
"$project": {
categories: {
$cond: [
{
$eq: [{ $type: "$categories" }, "array"]
},
{
'$reduce': {
'input': '$categories',
'initialValue': '',
'in': {
'$concat': [
'$$value',
{ '$cond': [{ '$eq': ['$$value', ''] }, '', ', '] },
'$$this'
]
}
}
},
"$categories"
]
}
}
}
])
Edit:
So, if you have nested arrays in the categories field. We can flat our arrays with unwind stage. So if you can add these 3 stages above the $project stage. Our aggregation will work.
{
"$unwind": "$categories"
},
{
"$unwind": "$categories"
},
{
"$group": {
_id: null,
categories: {
$push: "$categories"
}
}
},
Playground

MongoDB: How to copy Documents to a new field of associated Documents from other collections?

Collections that I have:
Product:
[
{
"_id":"product_id_1",
"name":"Product 1",
"price":50
},
{
"_id":"product_id_2",
"name":"Product 2",
"price":100
}
]
Category:
[
{
"_id":"category_id_1",
"name":"Category 1"
},
{
"_id":"category_id_2",
"name":"Category 2"
}
]
Audit:
[
{
"_id":"audit_id_1",
"resource_type":"product",
"resource_id":"product_id_1",
"attribute":"name",
"executionTime":"2021-01-10T00:00:00.000Z"
},
{
"_id":"audit_id_2",
"resource_type":"product",
"resource_id":"product_id_1",
"attribute":"name",
"executionTime":"2021-01-09T00:00:00.000Z"
},
{
"_id":"audit_id_3",
"resource_type":"product",
"resource_id":"product_id_1",
"attribute":"price",
"executionTime":"2021-01-10T00:00:00.000Z"
},
{
"_id":"audit_id_4",
"resource_type":"category",
"resource_id":"category_id_1",
"attribute":"name",
"executionTime":"2021-01-10T00:00:00.000Z"
},
{
"_id":"audit_id_5",
"resource_type":"category",
"resource_id":"category_id_1",
"attribute":"name",
"executionTime":"2021-01-09T00:00:00.000Z"
}
]
Collection Audit is using for saving details about each Product or Category documents updates.
For example, we see that the attribute name of Product with id product_id_1 was changed twice:
9th of January and 10th of January.
attribute price of the same Product was changed only once: 10th of January.
The same kind of information saved for Category collection as well.
The goal that I want to achieve is:
Extract existing Documents from Audit collection that contain information only about the latest changes for each unique attribute per each unique resource and copy them to a new field of related document of Product/Category collections.
As result, the Product/Category collections should look like this:
Product:
[
{
"_id":"product_id_1",
"name":"Product 1",
"price":50,
"audit":[
{
"_id":"audit_id_1",
"resource_type":"product",
"resource_id":"product_id_1",
"attribute":"name",
"executionTime":"2021-01-10T00:00:00.000Z"
},
{
"_id":"audit_id_3",
"resource_type":"product",
"resource_id":"product_id_1",
"attribute":"price",
"executionTime":"2021-01-10T00:00:00.000Z"
}
]
},
{
"_id":"product_id_2",
"name":"Product 2",
"price":100,
"audit":[
]
}
]
Category:
[
{
"_id":"category_id_1",
"name":"Category 1",
"audit":[
{
"_id":"audit_id_4",
"resource_type":"category",
"resource_id":"category_id_1",
"attribute":"name",
"executionTime":"2021-01-10T00:00:00.000Z"
}
]
},
{
"_id":"category_id_2",
"name":"Category 2",
"audit":[
]
}
]
I tried to write a query by myself, and this is what I got:
db.getCollection("audit").aggregate([
{
$match: {
"resource_type": "product"}
},
{
$sort: {
executionTime: -1
}
},
{
$group: {
_id: {
property: "$attribute",
entity: "$resource_id"
},
document: {
$first: "$$ROOT"
}
}
},
{
$replaceRoot: {
newRoot: "$document"
}
}
]).forEach(function(a){
db.getCollection("product").update({"_id" :ObjectId(a.resource_id)},{addToSet : {audit:[a]}})
});
The problems that I see with my solution are:
it will update only one Product collection. It means that I need to execute my code at list twice, for each existing collections.
forEach statement, I am not sure where exactly this command executed on the server-side or on client-side, assume Audit collection contains approx 100k documents, from the performance point of view, how fast this command will be executed?
So, definitely, I have a feeling that I need to rewrite my solution, but I have doubts about how to make it better.
For example, I read about $merge command, which can do a quite similar job that I do in forEach section, but I do not know how to apply $merge in the aggregation flow that I wrote above properly.
First of all forEach is executed on the client side, which means you download result of the aggregation and make 1 update request per each document in the result. Although it is the most flexible way it is the most expensive one. Aggregation pipeline with $out and $merge on the other hand is executed on the serverside so you don't pipe data through the client.
Secondly, if you need to update 2 collections you will need at least 2 queries. There is no way to $out to multiple collections.
Finally, you need to use the subquery syntax of the $lookup. It is more flexible and let you define "joining" logic in pipeline terms. For products it would be:
db.products.aggregate([
{
$lookup: {
from: "audit",
let: {
id: "$_id"
},
pipeline: [
{ "$match": {
$expr: { $eq: [ "$resource_id", "$$id" ] }, // the foreign key match
resource_type: "product" // the discriminator
} },
{ $sort: { "executionTime": -1 } }, // chronological order
{ "$group": {
_id: {
attribute: "$attribute", // for each unique attribute
id: "$resource_id" // per each unique resource
},
value: {
$first: "$$ROOT" // pick the latest
}
} },
{ "$replaceRoot": { "newRoot": "$value" } }
],
as: "audit"
}
}
])
The $out stage and its limitations you already learned from the previous answer.
The second pipeline to update categories will be exactly the same but with another $out destination and another value in the discriminator.
want to post the code written by myself:
db.getCollection("product").aggregate([
{ $match: {} },
{
$lookup: {
from: 'audit',
localField: '_id',
foreignField: 'resource_id',
as: 'audit'
}
},
{
$unwind: '$audit'
},
{
$sort: { "audit.executionTime": -1 }
},
{
$group: {
_id: {
property: "$audit.attribute",
entity: "$audit.resource_id"
},
document: {
$first: "$$ROOT"
}
}
},
{
$replaceRoot: {
newRoot: "$document"
}
},
{
$group: {
_id: "$_id",
audit: { $push: "$audit" }
}
},
{
$merge: {
into: 'product',
on: "_id",
whenMatched: 'merge',
whenNotMatched: 'insert'
}
}])

Node.JS + MongoDB aggregation Find array in array from DataBase in MongoDB

I have a Model in MongoDB which include an array(category) which has an array(picture) inside it.
Shirt Model looks like this:
{ _id: 100, category: [ { name: 'CatName', _id: 101, picture: [Object] } ] }
And my Picture Array looks like this:
[ { path: 'p1', _id: 102 },
{ path: 'p2', _id: 103 } ] }
I Pass 3 variables
main array id which is 100 (var1)
category name which is CatName (var2)
picture id which is 102 (var3)
I want to GET an array which looks like:
{ _id: 100, category: [ { name: 'CatName', _id: 101,
picture: [ { path: 'p1', _id: 102 }]
} ]
}
What I have tried is this:
Shirt.find( {_id: var1} ,
{category: { $and:[ { name: var2 } , { picture: {$elemMatch: {_id: var3}}} ] }} )
.exec(function(err, product) {
console.log('Result ' + product );
res.jsonp(product);
});
But the Result I receive is Undefined for First Code
Second Code That I tried:
Shirt.find( {_id: var1} ,
{category: {$elemMatch: { name: var2,picture: {$elemMatch: {_id: var3} }}} } )
And Result from second code filter the array for var1 and var2
But it contain the whole picture array which means it does not filter var3
What is the correct code to find what I want?
Is this a correct approach for a Shopping Website database Or you have a better suggestion?
Am I applying Parent and Child Database Correctly?
Thanks!
Following mongo query will give you expected result:
db.collection.find({
"category": {
$elemMatch: {
"name": "CatName",
"picture": {
$elemMatch: {
"_id": 102
}
}
}
}
})
You need to convert it in node.js format.
Following format may help you. Not Tested
collection.find({
_id: "" // add your match Id here} ,
{
category: {
"$elemMatch": {
"name": "CatName",
"picture": {
$elemMatch: {
"_id": 102
}
}
}
}
}
})
.exec(function(err, product) {
console.log('Result ' + product);
res.jsonp(product);
});
AFTER EDIT QUESTION
You should use mongo aggregation to get required values. Query will be like following -
db.collection.aggregate({
$unwind: '$category'
}, {
$unwind: '$category.picture'
}, {
$match: {
_id: 100, // add here your var1 variable instead of 100
'category.name': 'CatName', // add here your var2 variable instead of 'CatName'
'category._id': 102, //add your match id here if any otherwise remove this condition
'category.picture._id': 102 //add your var3 instead of 102
}
}, {
$group: {
_id: '$category.name',
'name': {
$first: '$category.name'
},
'picture': {
'$push': '$category.picture'
}
}
})

Resources