Cosmos DB query fail if parent object is null - mongodb

I am converting Mongodb to Cosmos DB without any code change. I use spring-boot-starter-data-mongo api and org.springframework.data.mongodb.core.query.Query to select only few fields in document not all fields.
Selecting _id,address.primary.email,address.primary.secondary from emp1 it works fine, if I use the same query select emp2, it fails, because emp2 doesn't have secondary address.
How to fix this issue ?
Query: { "_id" : { "$in" : ["Emp2"] } }, Fields: { "_id" :
1,"address.primary.email":1,"address.secondary.email":1},, Sort: { }
mongoTemplate.find(query,EmployeeColl.class);
[
{
"_id": "Emp1",
"name": "Name1",
"address": {
"primary": {
"email": "eamp1#gmail.com"
},
"secondary": {
"email": "emp1sec#gmail.com"
}
}
},
{
"_id": "Emp2",
"name": "Name2",
"address": {
"primary": {
"email": "emp2#gmail.com"
}
}
}
]
Exception:
UncategorizedMongoDbException: Query failed with error code 1 and error message
org.springframework.data.mongodb.core.MongoExceptionTranslator.translateExceptionIfPossible(MongoExceptionTranslator.java:132)
at org.springframework.data.mongodb.core.MongoTemplate.potentiallyConvertRuntimeException(MongoTemplate.java:2607)
at org.springframework.data.mongodb.core.MongoTemplate.executeFindMultiInternal(MongoTemplate.java:2474)

Related

MongoDb regex search in array objects

I have the following collection:
{
"invoice": {
"data": [{
"name": "VOUCHERNUMBER",
"value": "59302311"
}, {
"name": "VOUCHERDATE",
"value": "2020-02-20"
}
]
}
},
{
"invoice": {
"data": [{
"name": "VOUCHERNUMBER",
"value": "59112389"
}, {
"name": "VOUCHERDATE",
"value": "2020-02-20"
}
]
}
},
{
"invoice": {
"data": [{
"name": "VOUCHERNUMBER",
"value": "59302378"
}, {
"name": "VOUCHERDATE",
"value": "2020-02-11"
}
]
}
}
My task is to build a query that find all invoices which invoicenumbers includes "11" (or any other substring).
So I built the following statement:
{"invoice.data.name": "VOUCHERNUMBER", "invoice.data.value": {$regex : "11"} }
I'm expecting a result of the first two objects, but because of the second value in the third object, mongodb returns me all three objects. Then I tried
{$and : [{"invoice.data.name": "VOUCHERNUMBER"}, {"invoice.data.value": {$regex : "11"}}]}
with the same result ...
So I'm running out of ideas. Is there a solution to search for the string only in the value field where the corresponding "name" field contains "VOUCHERNUMBER"?
You need $elemMatch.
The $elemMatch operator matches documents that contain an array field with at least one element that matches all the specified query criteria.
db.collection.find({
"invoice.data": {
"$elemMatch": {
"name": "VOUCHERNUMBER",
"value": {
$regex: "11"
}
}
}
})
Sample Mongo Playground

mongodb query update select nested fields

this is my document in mongo:
"calendar": {
"_id": "5cd26a886458720f7a66a3b8",
"hotel": "5cd02fe495be1a4f48150447",
"calendar": [
{
"_id": "5cd26a886458720f7a66a413",
"date": "1970-01-01T00:00:00.001Z",
"rooms": [
{
"_id": "5cd26a886458720f7a66a415",
"room": "5cd17d82ca56fe43e24ae5d3",
"price": "",
"remaining": 0,
"reserved": 0
},
{
"_id": "5cd26a886458720f7a66a414",
"room": "5cd17db6ca56fe43e24ae5d4",
"price": "",
"remaining": 0,
"reserved": 0
}
]
},
}
I need to update the objects in the inner rooms array . I tried a query that selects a matching element no syntax error but an error comes in:
"errmsg" : "The field 'calendar.0.rooms.0.price' must be an array but
is of type string in document {_id:
ObjectId('5cd26a886458720f7a66a3b8')}",
and this my query:
db.calendars.updateOne({_id:ObjectId("5cd26a886458720f7a66a3b8"),
"calendar":{"$elemMatch":{"_id":ObjectId("5cd26a886458720f7a66a413"),"rooms._id":
ObjectId("5cd26a886458720f7a66a415")}}},
{"$push":{"calendar.$[outer].rooms.$[inner].price":"100000"}}, {"arrayFilters":[{"outer._id":ObjectId("5cd26a886458720f7a66a413")},{"inner._id":ObjectId("5cd26a886458720f7a66a415")}]})
this is some reference I found in StackOverflow but not helped:
Updating a Nested Array with MongoDB
You can use below query
db.getCollection("test").updateOne(
{
"_id": ObjectId("5cd26a886458720f7a66a3b8"),
"calendar.calendar": {
"$elemMatch": {
"_id": ObjectId("5cd26a886458720f7a66a413"),
"rooms._id": ObjectId("5cd26a886458720f7a66a415")
}
}
},
{ "$set": { "calendar.calendar.$[outer].rooms.$[inner].price": "100000" } },
{
"arrayFilters": [
{ "outer._id": ObjectId("5cd26a886458720f7a66a413") },
{ "inner._id": ObjectId("5cd26a886458720f7a66a415") }
]
}
)
I will update my answer with some explanation afterward

mongodb aggregation lookup using ObjectId

I am struggling with what seems to a basic lookup in mongodb. I can't figure out what is going wrong after trying multiple combinations. I see that there are many questions on SO addressing it, but nothing works after trying all the answers.
Here's my users collection
Here's my items collection
Each item has a single owner which maps to the user's _id in users collection.
For my query, I am trying to fetch items with their corresponding users. This is the query
db.items.aggregate([
{
"$lookup" : {
localField : "owner",
from : "users",
foreignField : "_id",
as : "users"
}
}
])
It returns this -
I have tried different variations - such as
db.items.aggregate([
{
"$lookup" : {
localField : "owner.str",
from : "users",
foreignField : "_id.str",
as : "users"
}
}
])
which results in the array of users getting populated incorrectly (with all users!).
What am I doing wrong?
EDIT
here's the items collection
{
"_id": {
"$oid": "5b268395c176db1548bd92c2"
},
"title": "Item #1",
"description": "Item 1 Description",
"categories": [
{
"$ref": "categories",
"$id": {
"$oid": "5b268248c176db1548bd92af"
}
}
],
"status": "borrowed",
"image": "http://cdn.site.com/testimage.png",
"borrower": "5b2684a0c176db1548bd92d5",
"owner": {
"$ref": "users",
"$id": {
"$oid": "5aecc8012d3d947ba130800d"
}
}
}
{
"_id": {
"$oid": "5b2c2c4d3c70af2da07d1266"
},
"title": "Item 2",
"description": "Item 2 Description",
"categories": [
{
"$ref": "categories",
"$id": {
"$oid": "5b268248c176db1548bd92af"
}
}
],
"status": "Available",
"image": "http://cdn.site.com/testimage1.png",
"borrower": null,
"owner": {
"$ref": "users",
"$id": {
"$oid": "5b2684a0c176db1548bd92d5"
}
}
}
Here's the users collection :
{
"_id": {
"$oid": "5aecc8012d3d947ba130800d"
},
"email": "n.y#gmail.com",
"registeredOn": "20/10/2018 12:12:29 AM",
"avatarURL": "http://gravtar.com/12334",
"friends": []
}
{
"_id": {
"$oid": "5b2684a0c176db1548bd92d5"
},
"email": "j.v#gmail.com",
"registeredOn": "20/10/2018 12:12:29 AM",
"avatarURL": "http://gravtar.com/12334",
"friends": [],
"wishlist": [
{
"$ref": "items",
"$id": {
"$oid": "5b2831543c70af2da07d11da"
}
}
]
}
The localField and foreignField should be of the same data type. Your are DBRef and ObjectId respectively.
If you want to populate a items record with the corresponding user, I think you can use the populate option in mongoose find API
Sample code:
this.read = function(query, populate, onSuccess, onFailure, forcePull, sortQuery, ttl, limitValue, selectQuery) {
var scope = this;
logger.info(this.TAG, "Read operation execution started on ORM Object: "
+ modelName + " with query: " + JSON.stringify(query));
if (query) {
var queryObject = this._model.find(query);
if (populate) {
queryObject = queryObject.populate(populate);
}
if (selectQuery) {
queryObject = queryObject.select(selectQuery);
}
if (sortQuery) {
queryObject = queryObject.sort(sortQuery);
}
if (limitValue) {
queryObject = queryObject.limit(limitValue);
}
queryObject.exec(function(error, records) {
if (error) {
logger.error(scope.TAG, "Error while ORM read: " + JSON.stringify(error));
onFailure(error);
return;
}
logger.info(scope.TAG, "Record read successfully: " + JSON.stringify(records));
onSuccess(records);
});
}
}
You can pass populate as an array of strings like ['owner'] in your case

MongoDB query in java for array fields using ProjectionOperation

Below is the JSON structure for a Store document:
{
{
"_id":"87348378",
"name": "ABC store",
"type": "Books",
"books": [
{
"name": "love",
"id": "1",
"types":{
"type":"love",
"number":"1"
}
},
{
"name": "coreman",
"id": "2",
"types":{
"type":"love",
"number":"1"
}
}
]
},
{
"_id":"87348",
"name": "Some store",
"type": "Books",
"books": [
{
"name": "JAVA",
"id": "1",
"types":{
"type":"Programming",
"number":"2"
}
},
{
"name": "coreman",
"id": "2",
"types":{
"type":"Programming",
"number":"3"
}
}
]
}
}
I need to get the all the stores, which are of a Bookstore type. But, I just want to return certain fields from the Books array(Name & type from types)
Using the following query to get the result:
db.getCollection('store').aggregate([{
"$project" : { "name" : 1 , "type" : 1 ,
"books":{"name":1,"types":{"type":1}}}
},
{ "$group" : { "_id" : "$type",
"books" : { "$push" : "$$ROOT"}}}])
Could anyone help me out to generate the same query with Spring/Java.
Note: I have seen addInclude method has like below in spring docs.
reference: https://docs.spring.io/spring-data/data-mongo/docs/current/api/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.html
**org.springframework.data.mongodb.core.aggregation**
andInclude
public ProjectionOperation andInclude(String... fieldNames)
Includes the given fields into the projection.
Parameters:
fieldNames - must not be null.
Returns:
andInclude
public ProjectionOperation andInclude(Fields fields)
Includes the given fields into the projection.
Parameters:
fields - must not be null.
Returns:

Upsert KV pair in subdocument for specific rules

How to update a document and insert key-value in subdocument for specific rules?
MongoDB version: 3.4
Use this CLI to insert simulation data
db.country.insertMany([{"_id":"us","groups":[{"group":"1"},{"group":"2"} ]},{"_id":"eu","groups":[{"group":"1"},{"group":"2"}]}, {"_id":"jp","groups":[{"group":"2"}]}])
original data
db.country.find()
{
"_id": "us", "groups": [ { "group" : "1" }, { "group": "2" } ]
}
{
"_id": "eu", "groups": [ { "group" : "1" }, { "group" : "2" } ]
}
{
"_id": "jp", "groups": [ { "group" : "2" } ]
}
How to get this result? ( just add status: happy to group 1 )
{
"_id": "us", "groups": [ { "group" : "1", "status": "happy" }, { "group": "2" } ]
}
{
"_id": "eu", "groups": [ { "group" : "1", "status": "happy" }, { "group" : "2" } ]
}
{
"_id": "jp", "groups": [ { "group" : "2" } ]
}
I know how to select all groups that match group=1
db.country.aggregate([
{'$unwind': '$groups'},
{'$match': {'groups.group': '1'}} ,
{'$project': {'group': '$groups.group', _id:0 }}
])
{ "group" : "1" }
{ "group" : "1" }
and also know how to use update + $set like this
// { "_id": 1, "people": {"name": "tony" } }
db.test.update({_id: 1}, { $set: {'people.country': 'taiwan'}})
// { "_id": 1, "people": {"name": "tony" , "country": "taiwan" } }
but how to merge update + $set and aggregate function? Please help me.
pymongo is OK for me.
How to get this result? ( just add status: happy to group 1 )
Use $ to refer the position of the matched sub-document in array.
db.coll.update_many({'groups.group':'1'}, {'$set': {'groups.$.status': 'happy'}})
see more here