Mongodb - unknown top level operator: $elemMatch" - node.js

I have a collection which contains some documents as follows,
{
"transactionId": 3432423,
"reviews": [
{
"fromUser": {
"userId": "5236aa1acd6e"
},
"toUser": {
"userId": "0ec8db9544cc"
},
"rating": 4.3,
"comment": ""
},
{
"toUser": {
"userId": "5236aa1acd6e",
"email": "yourlife#gmail.com",
"firstName": "your",
"lastName": "life"
},
"fromUser": {
"userId": "0ec8db9544cc",
"email": "mylife#gmail.com",
"firstName": "my",
"lastName": "life"
},
"rating": 4.3,
"comment": ""
}
]
}
i need to check if subdocument reviews present inside the document. i have used this query,
db.getCollection('_testCollection').find({ "$elemMatch": { "reviews": { "$exists": false } }})
it throws an error saying,
"errmsg" : "unknown top level operator: $elemMatch",

Neither $elemMatch or $exists is what you want to use to determine if reviews is an empty array.
Instead, either compare against []:
db.test.find({reviews: []})
Or use the $size operator:
db.test.find({reviews: {$size: 0}})

From the Docs :-
The $elemMatch operator matches documents that contain an array field with at least one element that matches all the specified query criteria.
db.getCollection('_testCollection').find({ "reviews": {"$elemMatch" : { "$exists": false } }})
$exists :- true or false // depend on requirement
Thanks

Related

How to update some collection with different query parameters and with different set values?

I have an array of mongoDB collection -
[
{
"_id": "630499244683ed43d56edd06",
"userId": "630499234683ed43d56edd05",
"isPaid": "true"
},
{
"_id": "6304c19bda84477b41b4bbfa",
"userId": "630499234683ed43d56edd05",
"isPaid": "true"
},
{
"_id": "6304c1b5da84477b41b4bbfb",
"userId": "630499234683ed43d56edd05",
"isPaid": "true"
},
{
"_id": "6304c1cbda84477b41b4bbfc",
"userId": "630499234683ed43d56edd05",
"isPaid": "true"
},
]
I just want to add the order property to all the object but for all the specific _id I need to add the different order value
Like this -
[
{
"_id": "630499244683ed43d56edd06",
"userId": "630499234683ed43d56edd05",
"isPaid": "true",
"order": 7
},
{
"_id": "6304c19bda84477b41b4bbfa",
"userId": "630499234683ed43d56edd05",
"isPaid": "true",
"order": 5
},
{
"_id": "6304c1b5da84477b41b4bbfb",
"userId": "630499234683ed43d56edd05",
"isPaid": "true",
"order": 0,
},
{
"_id": "6304c1cbda84477b41b4bbfc",
"userId": "630499234683ed43d56edd05",
"isPaid": "true",
"order": 2
},
]
Please let me know How do I implement this approach in mongoDB?
I'm not using mongoose in my project.
You have to add the structure of the coming data, how it grouped with _id and the count of order. If it is an array of object, you can use loop. I don't think there is a solution to update different values in only one code.
If you want specific ids to have specific order values you need to update them separately. You could use bulkWrite with the updateOne method.
db.collection.bulkWrite( [
{ updateOne: {
filter: { _id: ... },
update: { $set: { order: 1 } }
} },
...
] )
Refer to the MongoDB documentation and to your language driver in particular: https://www.mongodb.com/docs/v6.0/reference/method/db.collection.bulkWrite/#updateone-and-updatemany

How to rename keys of items in an array

I am very new to MongoDB and I need to do a somewhat complex Update operation on my collection.
I have this kind of collection:
[
{
"Id": 1,
"extension": [
{
"keyName": "Name",
"value": "Bob"
},
{
"keyAge": "Age",
"value": 20
}
]
},
{
"Id": 2,
"extension": [
{
"keyName": "Name",
"value": "Sam"
},
{
"key": "Name",
"value": "Sam"
}
]
},
{
"Id": 3,
"extension": [
{
"keyName": "Age",
"value": 25
},
{
"key": "Age",
"value": 25
}
]
},
{
"Id": 4
}
]
I would like to update any items in the extension array of all documents
so that when an item is found with a key property, to rename it keyAge.
Here is the expected result:
[
{
"Id": 1,
"extension": [
{
"keyName": "Name",
"value": "Bob"
},
{
"keyAge": "Age",
"value": 20
}
]
},
{
"Id": 2,
"extension": [
{
"keyName": "Name",
"value": "Sam"
},
{
"keyAge": "Name",
"value": "Sam"
}
]
},
{
"Id": 3,
"extension": [
{
"keyName": "Age",
"value": 25
},
{
"keyAge": "Age",
"value": 25
}
]
},
{
"Id": 4
}
]
I tried to use $rename in a similar way to this question:
MongoDB rename database field within array
but I get the same error $rename source may not be dynamic array
I think this solution might also apply to me, I tried using it but it's not updating anything on my side, so I guess I cannot understand how to apply that answer to me...
https://stackoverflow.com/a/49193743/215553
Thanks for the help!
I tried to use $rename in a similar way to this question: MongoDB rename database field within array but I get the same error $rename source may not be dynamic array
There is a note in $rename:
$rename does not work if these fields are in array elements.
You can try update with aggregation pipeline starting from MongoDB 4.2,
check condition id key field is exists
$map to iterate loop of extension array
$map to iterate loop of array that is converted from extension object to array in key-value format
$cond check condition if k is key then return keyAge otherwise return current
$arrayToObject back to convert key-value array return from above map to object original format
db.collection.update(
{ "extension.key": { $exists: true } },
[{
$set: {
extension: {
$map: {
input: "$extension",
in: {
$arrayToObject: {
$map: {
input: { $objectToArray: "$$this" },
in: {
k: {
$cond: [
{ $eq: ["$$this.k", "key"] }, // check "key" field name
"keyAge", // update new name "keyAge"
"$$this.k"
]
},
v: "$$this.v"
}
}
}
}
}
}
}
}],
{ multi: true }
)
Playground
Question : How to Rename or add new key for existing array object key?
Answer : Inside projection of mongodb query we have map property which will resolve this.
Solution Example :
{
parents: {
$map: {
input: "$parents",
as: "parent",
in: {
caaUserId: "$$parent._id",
email: "$$parent.email",
countryCode: "$$parent.countryCode",
mobile: "$$parent.mobile",
reportType : "single"
}
}
}
}
In this example if we want to rename $parent._id as caaUserId in parents array for each Element.
Then we can use map and define caaUserId like $$parent._id. This whole code will work in mongoose projection of Query.
It should return following :
{
"parents" : [
{
"caaUserId" : "62d17fa164057000149e283f",
"email" : "john.doe#hotmail.com",
"countryCode" : 91,
"mobile": 9876543210,
"reportType":"single",
},
{
"caaUserId" : "6195d50f15ae2b001293c486",
"email" : "akka.ash#hotmail.com",
"countryCode" : 91,
"mobile": 9876543211,
"reportType":"multi",
},
]
}
This is something that works in your case. Might not be the most readable though.
import json
data = json.loads(strdata)
for entry in data:
if 'extension' in entry:
for x in entry['extension']:
for k, v in x.items():
if k == 'key':
x['keyAge'] = x.pop(k)

CouchDB index with $or and $and not working but just $and does

For some reason, I have the following .find() commands and I am getting conflicting indexing errors. Below are examples of one working when I only try to get one type of document. But then if I try to get 2 types of documents it doesn't work for some reason.
Does anyone know why this would be the case?
My index file:
{
"_id": "_design/index",
"_rev": "3-ce41abcc481f0a180eb722980d68f103",
"language": "query",
"views": {
"index": {
"map": {
"fields": {
"type": "asc",
"timestamp": "asc"
},
"partial_filter_selector": {}
},
"reduce": "_count",
"options": {
"def": {
"fields": [
"type",
"timestamp"
]
}
}
}
}
}
Works:
var result = await POUCHDB_DB.find({
selector:{
$and: [{type:"document"},{uid:"123"}]
},
limit:50,
bookmark: bookmark,
sort: [{timestamp: "desc"}]
});
Doesn't work:
var result = await POUCHDB_DB.find({
selector:{
$or: [
{$and: [{type:"document"},{uid:"123"}]},
{$and: [{type:"page"},{uid:"123"}]}
]
},
limit:50,
bookmark: bookmark,
sort: [{timestamp: "desc"}]
});
Missing timestamp in selector
In order yo use the timestamp to sort, it must be in your selector. You can simply add it with a "$gte":null.
Redundant condition
The uid seems redundant for your query. For this reason, I would add it into a separate condition.
Finally, in order to use your index, you should create an index with the following fields: uid, timestamp, type (I think this one is optional).
{
"selector": {
"$and": [{
"uid": "123",
"timestamp": {
"$gte": null
}
},
{
"$or": [{
"type": "document"
},
{
"type": "page"
}
]
}
]
},
"sort": [{
"timestamp": "desc"
}]
}
Recommandation
If you want your queries to use your index, I would recommend to specify the "use_index" field. If you can version your indexes and queries, it will make the queries faster.

Cloudant/CouchDB query by content of nested array

In my cloudant database I have objects like these. I'd like to query for objects that based on properties in a nested array.
In the example below, how to I query for all objects where there is a vote with userId=="user1"? The query should return both objects. When I search for userId "user2" it should return the first one as the second object only has vote from user1 and user4.
{
"_id": "1",
"votes": [
{
"userId": "user1",
"comment": ""
},
{
"userId": "user2",
"comment": ""
},
{
"userId": "user3",
"comment": ""
}
]
}
{
"_id": "2",
"votes": [
{
"userId": "user1",
"comment": ""
},
{
"userId": "user4",
"comment": ""
}
]
}
This view will return a list of all votes (where a vote is userId and _id) sorted by userId, to only get user1 use ?key="user1"
function(doc) {
for(i in doc.votes)
emit(doc.votes[i].userId, doc._id);
}
/dbName/_design/foo/_view/bar?key="user1"
{"total_rows":5,"offset":0,"rows":[
{"id":"1","key":"user1","value":"1"},
{"id":"2","key":"user1","value":"2"}
]}

How to get a certain element out of an array within collection in MongoDB?

I have the following collection named 'CanvasCollection' in mongoDB and I want to get the image object with an id of "50e6fcc2edcbc10613000022" out of the array within this collection:
{
"_id": ObjectId("50d1f440471ca84e1f000007"),
"image": [{
"_id": ObjectId("50e6fcc2edcbc10613000022"),
"type": "image",
"filename": "50e6fcc2edcbc10613000022.jpg",
"origname": "2811879134_7a57d7c586_m.jpg",
"filepath": "upload/50e6fcc2edcbc10613000022.jpg",
"thumbpath": "upload/50e6fcc2edcbc10613000022_small.jpg",)
}],
"text": [{
"_id": ObjectId("50e6eda0edcbc1061300001b"),
"content": "Type your text here"
}]
}
This doesn't seem to work:
CanvasCollection.find({'_id': new BSON.ObjectID('50d1f440471ca84e1f000007')}, {'image' : {$elemMatch: {'_id': new BSON.ObjectID('50e6fcc2edcbc10613000022')}}})
Any ideas how to get exactly this:
{
"_id": ObjectId("50e6fcc2edcbc10613000022"),
"type": "image",
"filename": "50e6fcc2edcbc10613000022.jpg",
"origname": "2811879134_7a57d7c586_m.jpg",
"filepath": "upload/50e6fcc2edcbc10613000022.jpg",
"thumbpath": "upload/50e6fcc2edcbc10613000022_small.jpg",)
}
If you have MongoDb version 2.2 you can do it like this:
db.CanvasCollection.aggregate({$project: {image:1}},{ $unwind : "$image" },{ $match : {"image._id": ObjectId("50e6fcc2edcbc10613000022")}})
(this is from mongo shell), output would be like this:
{
"result" : [
{
"_id" : ObjectId("50d1f440471ca84e1f000007"),
"image" : {
"_id" : ObjectId("50e6fcc2edcbc10613000022"),
"type": "image",
"filename": "50e6fcc2edcbc10613000022.jpg",
"origname": "2811879134_7a57d7c586_m.jpg",
"filepath": "upload/50e6fcc2edcbc10613000022.jpg",
"thumbpath": "upload/50e6fcc2edcbc10613000022_small.jpg"
}
}
],
"ok" : 1
}
About unwind: http://docs.mongodb.org/manual/reference/aggregation/unwind/.
Similar topic: Retrieve only the queried element in an object array in MongoDB collection..
If you have older MongoDb version I think it's impossible.

Resources