updating a referenced collection in mongo - mongodb

I a model schema(created from mongoose)(say collection A) has a reference to another collection(say collection B), then how do you upsert to this referenced collection so that they respect the referenced relationship.
e.g.
A = [...{
name: "a",
b_id: "EXISTENT_ID"
}]
B = [..., {
_id: "EXISTENT_ID",
"subject": "science",
"age": 23
}]
I tried to bulk update like this:
A.bulkWrite([{
updateOne: {
filter: {_id: "EXISTENT_ID"},
update: {$set: {"A.b_id": {subject: "maths", "age": 22}}},
upsert: true
}
}])
and I get a write error that says: Updating the path 'a.b_id' would create a conflict at 'b_id', I was expecting the associated reference to be updated, since the schema of A is defined as:
Schema({
name: String,
b_id: {
type: mongoose.Types.ObjectId,
ref: 'B',
required: true,
}
})
The reason why I'd have to bulkWrite A is because the record is to be created if it doesn't exist and the reference linked.
For now I'm using javascript to do things manually making multiple round trips to the database, but, I'd like to use queries if possible. Is something I'm doing currently wrong or is there a mechanism to do this sort of a referenced update? I'd like a header to proceed. Thanks in advance.

Related

MongoDB Compass: Cannot find index to verify that join fields will be unique

I'm trying to $merge documents from Read-Only View (aka ROV) into On-Demand Materialized View (aka OMV). With Compass for now. I've created a pipeline in Aggregations section of Compass:
[{
$project: {
_id: 0,
RunDate: 1,
Manufacturer: 1,
Name: 1,
Workgroup: 1,
TotalPhysicalMemory: 1,
Model: 1,
PartOfDomain: 1,
Domain: 1,
NumberOfLogicalProcessors: 1
}
}, {
$merge: {
into: 'omv_WSCReportIndex',
on: 'Name',
whenMatched: 'replace',
whenNotMatched: 'insert'
}
}]
Basically I want OMV with unique 'Name' field where I will be inserting computer docs like this one (and overwriting ones, if needed):
RunDate: "2022-08-23 10:54:38Z"
Manufacturer: "VMware, Inc."
Name: "SRV010"
Workgroup: null
TotalPhysicalMemory: 16
Model: "VMware7,1"
PartOfDomain: true
Domain: "test.loc"
NumberOfLogicalProcessors: 4
Problem is I'm getting an error when I click RUN: Cannot find index to verify that join fields will be unique. I've tried creating index on 'omv_WSCReportIndex' for 'Name' field like "1-Type Index with Unique", "Text-Type Index with Unique", tried to create index with Collation object..
I can't just figure it out how to make this OMV. Please help!

Is there a way to sort the order of columns in mongodb?

I am learning MongoDB and I've encountered a thing that mildly annoys me.
Let's say I got this collection:
[
{
_id: ObjectId("XXXXXXXXXXXXXX"),
name: "Tom",
followers: 10,
active: true
},
{
_id: ObjectId("XXXXXXXXXXXXXX"),
name: "Rob",
followers: 109,
active: true
},
{
_id: ObjectId("XXXXXXXXXXXXXX"),
name: "Jacob",
followers: 2,
active: false
}
]
and I rename the name column to username with the command:
db.getCollection('users').update({}, { $rename: { "name" : "username" }}, false, true)
now the username property is at the end of the record, example:
[
// ... rest of collection has the same structure
{
_id: ObjectId("XXXXXXXXXXXXXX"),
followers: 109,
active: true,
username: "Rob"
}
// ... rest of collection has the same structure
]
How do I prevent this from happening or how do I place them in a specific order? This is infuriating to work with in Robo/Studio 3T. I've got a collection with about 15 columns which are now out of order which in the GUI because of this
The $rename operator logically performs an $unset of both the old name and the new name, and then performs a $set operation with the new name. As such, the operation may not preserve the order of the fields in the document; i.e. the renamed field may move within the document.
Documentation
It is the behaviour from version 2.6
Since it is JSON based, you can get any field easily. And you have very less columns.
Keys in JSON objects are in their very nature unordered. See RFC 4627 which defines JSON, section 1 "Introduction":
An object is an unordered collection of zero or more name/value
pairs, where a name is a string and a value is a string, number,
boolean, null, object, or array.
(Emphasis mine)
Therefore, it would even be correct, if you wrote
{
"name": "Joe",
"city": "New York"
}
and got back
{
"city": "New York",
"name": "Joe"
}

MongoDb updating old documents

I am new to MongoDb and to the NoSQL concept of storing information. Unlike in SQL Server, adding or updating a column name in a table will affect all of the records. I can assume that when I pull a record, that column will be there. It may or may not have a value depending on how it was setup.
But how would you handle a situation in MongoDb where you have an outdated document and you want to pull it and now your code is looking for a property that did not exist or was just recently renamed?
There are few examples of different technics.
Assuming we have a collection pets with documents like:
{
kind: "cat",
age: 2
}
and at some point we added a field "nick", so new documents look like:
{
kind: "cat",
age: 2,
nick: "Tom"
}
Your app require "nick" field to list pets.
Use defaults
Update old documents with default values (you may known it as 'ALTER TABLE' magic in SQL terms):
db.pets.updateMany({nick: {$exists: false}}, {$set: {nick: "NONAME"}});
If you need to support both versions you need to do it runtime.
On application side:
db.pets.find({}).forEach(pet => print(p.nick || "NONAME") );
On db side:
db.pets.aggregate([
{$project: {
kind: 1,
age: 1,
nick: { $ifNull: [ "$nick", "NONAME" ] }
}}
])
Ignore invalid documents
Remove them:
db.pets.remove({nick: {$exists: false}})
If you need to support both versions you need to filter them out runtime:
db.pets.find({
kind: {$exists: true},
age: {$exists: true},
nick: {$exists: true}
);
You can make it more defensive by specifying type:
db.pets.find({
kind: {$type: "string"},
age: {$type: "int"},
nick: {$type: "string"}
);
Stop program execution
db.pets.find({}).forEach(pet => if(!pet.nick){throw new Error("Pet " + pet._id + " has no nick!")} );

MongoDB: working with relational schema

I am new to MongoDB and would appreciate any suggestion I can get here. We have recently changed the data storage from MySQL to MongoDB but we didn't change the schema. Now we store data as follow: each document in collection has data from all tables for a given entry. Each table now a field in document with rows stored in array. All operations that were fast and easy in SQL are very slow now and require to write customer code.
My current issue is how to implement SQL join for fields (tables before). An example:
{
table1:[{id: 1, prop1: a}, {id: 2, prop1: b}],
table2:[{id: 1, prop2: c}]
table3:[{id: 2, prop3: d}]
}
I need to get list of objects merged as follow:
[
{id: 1, prop1: a, prop2:c},
{id: 2, prop1: b, prop3:d}
]
Could you recommend how to best achieve this transformation? Thank you!
Perform aggregation with $lookup on tables.
e.g.
db.table1.aggregate([
{
$lookup:{
from: "table2",
localField: "id",
foreignField: "id",
as: "table2"
}
}
])
It will return output as
{id: 1, prop1: a, table2: [{id: 1, prop2: c}]}
for better understanding follow https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/

Mongodb, concat more updates in one query

There is a way to concat more updates?
For example I would like to change more values in the same element. So having this...
{
cc: [
{ user_id: "1", hasSeen:true}
,{ user_id: "2", hasSeen:false}
,{ user_id: "3", hasSeen:false}
]
,conversation: [{
user_id: "1",
text: "message by 1, to 2and3"
}]
}
...I would like to push a new conversation object and also change all the hasSeen values.
For do the first point, no problem, I just push only a new conversation object. And it works...
...update(
{ _id : _param.conversation_id }
,{ $push:{ conversation:{user_id:"2",text:"message by 2, to 1,3"} }}
)
.exec(function(err, numAffected, rawResponse) {
});
But I would like also to change the three "hasSeen" values in the same time. is it possible?
Can I do it with one query? or i should split it in two queries?
ps: I use mongoose.
Currently the positional operator (which I think you will need here) does not work in such a manner that you can do a type of conditional update whereby you iterate through a list of $incs or $sets to change those subdocument values.
There is a JIRA for such a thing that could possibly help you: https://jira.mongodb.org/browse/SERVER-6566?page=com.atlassian.jira.plugin.system.issuetabpanels:changehistory-tabpanel but it is filed under "features we are not sure of".
Best to split this up currently.