I have this collection setup in mongo:
[{
"_id": {
"$oid": "5f55fdede873f132ec09207e"
},
"edition_id": 35889464,
"rounds": [
{
"round": 1,
"image": "https://i.imgur.com/cT7aHQ9.gif",
"type": "normal",
"questions": [
{
"songs": [
"2vfvGlqCB7oertO5VLE0sz"
],
"open": false,
"setValue": 100,
"varValue": 50,
"type": "normal"
}
]
}
]
}]
I'm trying to update a field open:
const questionToggle = await db.collection('editions').updateOne(
{ edition_id: editionNumber },
{
$set: {
[`questionToggle.$.rounds.${roundIndex}.questions.${questionIndex}.open`]: targetValue,
},
},
);
res.json(questionToggle);
I keep getting MongoError: The positional operator did not find the match needed from the query. any ideas on what I'm doing wrong? my variables are:
edition number 35889464
round 0
question 0
editionNumber, roundIndex, and questionIndex, respectively. I've checked and they are all of type number.
I got it working:
const edition = await db.collection('editions').updateOne(
{ edition_id: editionNumber },
{
$set: {
[`rounds.${roundIndex}.questions.${questionIndex}.open`]: targetValue,
},
},
);
Related
Problem:
I need to only update one document in a nested array of subdocuments. My previous query was updating all matching documents which is no good Example Below. So I decided to use aggregation so that I could add a limit stage so that I could only update one item, but I cannot get the update to happen through node and I am not even getting errors.
Old Query/Update:
This query updates all documents that match the shape of userId: "empty" I need it to only update the first match
const query = await db.collection('events').updateOne({
_id: new ObjectId(eventId),
createdBy: new ObjectId(createdBy),
"weights.weight": weight
},
{
$set: {
"weights.$.spotsAvailable.$[el2]": {
"name": applicantName,
"userId": new ObjectId(applicantId)
}
}
},
{
arrayFilters: [
{
"el2.userId": "empty"
}
]
})
I have tested the aggregation in the MongoDB compass aggregation builder and it works fine.
But in the actual node code no luck
I have tried:
Pluging in variables without the new ObjectId syntax
Plugin in variables with the new ObjectId syntax
Using the exact same hardcoded values that I got from copying the aggregation code from compass for the node driver
Example document
{
"_id": {
"$oid": "6398c34ca67dbe3286452f23"
},
"name": "test",
"createdBy": {
"$oid": "636c1778f1d09191074f9690"
},
"description": "testing",
"date": {
"$date": {
"$numberLong": "1645488000000"
}
},
"location": {
"type": "Point",
"coordinates": [
0,
0
]
},
"weights": [
{
"spotsAvailable": [
{
"name": "empty",
"userId": "empty"
},
{
"name": "empty",
"userId": "empty"
},
{
"name": "empty",
"userId": "empty"
}
],
"weight": 12
},
{
"spotsAvailable": [
{
"name": "empty",
"userId": "empty"
},
{
"name": "empty",
"userId": "empty"
}
],
"weight": 15
}
],
"eventApplicants": [
{
"userId": {
"$oid": "636c1778f1d09191074f9690"
},
"name": "Wayne Wrestler",
"weight": 15
}
]
}
Code:
const acceptOrRemoveApplicant = async (eventId: ObjectId, createdBy: ObjectId, applicantId: ObjectId, applicantName: string, boolean: boolean, weight: number): Promise<boolean | undefined> => {
console.log({ eventId, createdBy, applicantId, applicantName, boolean, weight })
if (boolean == true) {
try {
/*
* Requires the MongoDB Node.js Driver
* https://mongodb.github.io/node-mongodb-native
*/
const agg = [
{
'$match': {
'_id': new ObjectId('6398c34ca67dbe3286452f23'),
'createdBy': new ObjectId('636c1778f1d09191074f9690')
}
}, {
'$unwind': {
'path': '$weights'
}
}, {
'$unwind': {
'path': '$weights.spotsAvailable'
}
}, {
'$match': {
'weights.spotsAvailable.name': 'empty',
'weights.weight': 15
}
}, {
'$limit': 1
}, {
'$set': {
'weights.spotsAvailable.name': 'Wayen',
'weights.spotsAvailable.userId': '123456'
}
}
]
const client = await clientPromise;
const db = client.db();
const query = db.collection('events').aggregate(agg);
// const query = await db.collection('events').updateOne({
// _id: new ObjectId(eventId),
// createdBy: new ObjectId(createdBy),
// "weights.weight": weight
// },
// {
// $set: {
// "weights.$.spotsAvailable.$[el2]": {
// "name": applicantName,
// "userId": new ObjectId(applicantId)
// }
// }
// },
// {
// arrayFilters: [
// {
// "el2.userId": "empty"
// }
// ]
// })
if (query) {
console.log("we queried")
console.log({ query })
return true
} else {
throw new Error("User not added to event")
}
} catch (e) {
console.error(e);
}
My project is in nodeJs with express and i use mongoose for the request to my database mongoDb.
I have a model Media the structure is:
{
"_id": {
"$oid": "6354f982a11464ff4f7bac60"
},
"userId": {
"$oid": "6353aa119d39ccbb3263123f"
},
"date": "2022-10-23",
"base64": "data:image/png;base64,iVBORw0KGgoAAA....=",
"label": "Noamount",
"uriPath": "xxxx",
"s3Path": "xxxx",
"amount": 0,
"__v": 0,
"type": "tva"
}
Like you seen there is a field date, imagine that i have 3 medias on differents month:
{
"date": "2022-10-23",
"label": "monthTen",
"type" : "tva",
... // Other property
},
{
"date": "2022-09-10",
"label": "monthNineFirst",
"type" : "tva",
... // Other property
},
{
"date": "2022-09-19",
"label": "monthNineSecond",
"type" : "other",
... // Other property
}
I want to output something like this:
// Ordery by type in first place and by month
// number like 9 = the month inside the date property
{
tva: {
9: [{ label: "monthNineFirst"... }],
10: [{ label: "monthNineTen"... }]
},
other: {
9: [{ label: "monthNineSecond"... }]
}
}
Is found the property aggregate but i don't understand it correctly.
I know how to dow that in JS it's easy, but can i do that directly on the request with the property aggregate and $group?
There is what i have done so far https://mongoplayground.net/p/-vAbdnnqOfD
Here's one way to do it by extending your mongoplayground.net start.
db.collection.aggregate([
{ // group all docs by month
$group: {
_id: {
$month: {
$dateFromString: {
dateString: "$date",
format: "%Y-%m-%d"
}
}
},
data: {"$push": "$$ROOT"}
}
},
{ // group all groups into a single doc
"$group": {
"_id": null,
"groupData": {
"$push": {
// k,v for $arrayToObject
"k": "$_id",
"v": {
"$sortArray": {
"input": "$data",
"sortBy": {"date": 1}
}
}
}
}
}
},
{
"$replaceWith": {
"$arrayToObject": {
"$map": {
"input": {
// sort by month
"$sortArray": {
"input": "$groupData",
"sortBy": {"k": 1}
}
},
"in": {
"$mergeObjects": [
"$$this",
{ // rewrite k as string
"k": {"$toString": "$$this.k"}
}
]
}
}
}
}
}
])
Try it on mongoplayground.net.
I need to output a list of unique values in a collection. I've used the distinct method when the values are either a string or a number. But in this situation, the values are an array of objects.
The simplified model looks like this:
const mongoose = require('mongoose');
const ItemModel = mongoose.Schema({
category: [{
lang: String,
text: String
}],
discount: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('Items', ItemModel);
A typical find() query without parameters produces this result:
[
{
"_id": "5fd8712b374a9a1410f786bf",
"category": [
{
"_id": "5fd8712b374a9a1410f786c2",
"lang": "RU",
"text": "Домашняя одежда"
},
{
"_id": "5fd8712b374a9a1410f786c3",
"lang": "EN",
"text": "Homewear"
}
],
"discount": "45%",
"date": "2020-12-12T11:12:37.811Z",
"__v": 0
},
{
"_id": "5fd4a5b95e1a251ac96b2e08",
"category": [
{
"_id": "5fd4a5b95e1a251ac96b2e0b",
"lang": "RU",
"text": "Домашняя одежда"
},
{
"_id": "5fd4a5b95e1a251ac96b2e0c",
"lang": "EN",
"text": "Homewear"
}
],
"discount": "35%",
"date": "2020-12-12T11:12:37.811Z",
"__v": 0
},
{
"_id": "5fd49e415e1a251ac96b2dfc",
"category": [
{
"_id": "5fd49e415e1a251ac96b2dff",
"lang": "RU",
"text": "Активный отдых"
},
{
"_id": "5fd49e415e1a251ac96b2e00",
"lang": "EN",
"text": "Active"
}
],
"discount": "50%",
"date": "2020-12-12T10:06:53.120Z",
"__v": 0
}
]
I need to output a list of unique "category.text" values where "lang" equals "EN". At the output, we should get an array like this:
[ "Active", "Homewear" ]
How to do it the right way, with good performance?
Regards.
You can use aggregation pipeline,
$unwind to deconstruct category array
$match category.lang is EN
$group by null, and get unique text using $addToSet
StockModel.aggregate([
{ $unwind: "$category" },
{ $match: { "category.lang": "EN" } },
{
$group: {
_id: null,
test: { $addToSet: "$category.text" }
}
}
])
Playground
use aggregation :
take a look at the example below :
StockModel.aggregate([
{
$group: {
_id: 0,
title: { $addToSet: '$title' },
stock_id: { $addToSet: '$stock_id' },
},
},
]);
with aggregation $group you can distinct your collections via filter.
I'm converting MongoDB Query to Elasticsearch in NodeJS platform. While developing I'm facing some difficulties with grouping and filtering data (getting nested objects like hits.hits._source) within Elasticsearch Query like we doing in MongoDB Query.
Example:-
UserModel.aggregate([
{
$match: {
uId: req.body.uId, timestamp: { $gte: req.body.date, $lte: new Date() }
},
},
{
$group: {
_id: "$eId",
location: {
$push: {
time: "$timestamp", lat: "$lat"
}
},
timestamp: {
$push: "$timestamp"
},
testId: { $first: "$testId" },
}
},
{
$project: {
eId: 1, location: 1, testId: 1, max: { $max: "$timestamp" }
}
},
{ $unwind: { path: "$location", preserveNullAndEmptyArrays: true } },
{
$redact: {
$cond: {
if: { $eq: ["$location.time", "$max"] },
then: "$$DESCEND",
else: "$$PRUNE"
}
}
},
{
$project: {
eId: 1, latitude: "$location.lat", testId: 1
}
},
]).exec(function (err, result) {
console.log(result)
});
What will be the equivalent query in Elasticsearch?
I'm looking for solution with grouping, unwinding and projecting (MongoDB concepts to Elasticsearch) required data with minimal nested response.
Thanks in Advance.
EDIT:-
Adding Elasticsearch Document:-
{
"timestamp": "2019-10-08T:02:50:15.54Z",
"status" : 1,
"eId": "5d5d7ce0c89852e7bad4a407",
"location": [
2.000,
34.5664111801
],
"zId": "5d5d7ce0c89852e7bad4a4ef"
},
{
"timestamp": "2019-10-09T:02:50:15.54Z",
"status" : 1,
"eId": "5d5d7ce0c89852e7bad4a408",
"location": [
2.100,
35.5664111801
],
"zId": "5d5d7ce0c89852e7bad4a4ef"
},
{
"timestamp": "2019-10-09T:03:50:15.54Z",
"status" : 1,
"eId": "5d5d7ce0c89852e7bad4a407",
"location": [
4.100,
35.5664111801
],
"zId": "5d5d7ce0c89852e7bad4a4ef"
},
{
"timestamp": "2019-10-09T:03:40:15.54Z",
"status" : 1,
"eId": "5d5d7ce0c89852e7bad4a407",
"location": [
2.100,
35.5664111801
],
"zId": "5d5d7ce0c89852e7bad4a4e1"
},
{
"timestamp": "2019-10-10T:03:40:15.54Z",
"status" : 1,
"eId": "5d5d7ce0c89852e7bad4a407",
"location": [
3.100,
35.5664111801
],
"zId": "5d5d7ce0c89852e7bad4a4e1"
}
Match with status =1, and Group By eId
With that results, group by timestamp and get max timestamp value
Expected Result:-
[
{
"_id": "5d5d7ce0c89852e7bad4a407",
"max": "2019-10-10T:03:40:15.54Z", // max timestamp
"zId": [
"5d5d7ce0c89852e7bad4a4e1",
"5d5d7ce0c89852e7bad4a4ef"
]
},
{
"_id": "5d5d7ce0c89852e7bad4a408",
"max": "2019-10-09T:02:50:15.54Z",
"zId": [
"5d5d7ce0c89852e7bad4a4ef"
]
}, // ...etc
]
Thanks for the documents. Sadly, I do not know any way to retrieve only the documents having the max timestamp field value.
The following query will allow you to filter by status and group by eId then get the max timestamp value, but it will not return the documents having the max timestamp value.
{
"size": 0,
"query": {
"term": {
"status": 1
}
},
"aggregations": {
"eId_group": {
"terms": {
"field": "eId"
},
"aggregations": {
"max_timestamp": {
"max": {
"field": "timestamp"
}
}
}
}
}
}
This second query use a top_hits aggregation to retrieve the documents grouped by eId. The returned documents are sorted by decreasing timestamp value so the documents having the max timestamp will be firsts, but you may also get documents with different timestamps.
{
"size": 0,
"query": {
"term": {
"status": 1
}
},
"aggregations": {
"eId_group": {
"terms": {
"field": "eId"
},
"aggregations": {
"max_timestamp": {
"max": {
"field": "timestamp"
}
},
"top_documents": {
"top_hits": {
"size": 20,
"sort": { "timestamp": "desc"}
}
}
}
}
}
}
I used the following mapping for the index
PUT /test_index
{
"mappings": {
"properties": {
"timestamp": {
"type": "date"
},
"eId": {
"type": "keyword"
},
"zId": {
"type": "keyword"
},
"status": {
"type": "keyword"
}
}
}
}
I have a mongodb aggregation framework query as shown below.I am unable to parse the output of the below query
myModel.aggregate(
[
{
"$match": { "$and": [{ "serviceActiveFlag": "Y" }, { "hospitalName": hospitalName }] }
},
//decompile array
{ $unwind: "$Treatment" },
{
$group: {
_id: "$Treatment.departmentName", "procedureList": {
$push: { "procedureName": "$Treatment.name", "cost": "$Treatment.costLowerBound" }
}
}
},
{
$project: {
"_id": 0,
"department": '$_id',
"procedureList": 1
}
}
], function (err, result) {
})
Output of the above query is shown below
{
"data": [
{
"procedureList": [
{
"procedureName": "Root Canal",
"cost": 10200
}
],
"department": "Dental"
},
{
"procedureList": [
{
"procedureName": "Bone Grafting",
"cost": 20000
}
],
"department": "Ortho"
}
]
}
How do i retrieve the value corresponding to key data?
I tried result.data[0],but I got undefined error
Expected output is given below
Expected output
[
{
"procedureList": [
{
"procedureName": "Root Canal",
"cost": 10200
}
],
"department": "Dental"
},
{
"procedureList": [
{
"procedureName": "Bone Grafting",
"cost": 20000
}
],
"department": "Ortho"
}
]
I think you forgot to parse the json-data, that's why it isn't able to index, Please use the code below to parse the json and then try accessing the array. if you encounter any problems, please mention them.
result = JSON.parse(result)
result.data[0]
Result is an object so,
let data = result.data