Index on nested document in MongoDB - mongodb

I have a nested JSON document like:
{
"docId": 1901603742,
"sl": [ {"slid","val"}],
"accounts": {
"123": {
"smartAccountId": "123",
"smartAccountName": "Dummy name",
"101": {
"virtualAccountId": "101",
"virtualAccountName": "DEFAULT"
},
"102": {
"virtualAccountId": "102",
"virtualAccountName": "DEFAULT"
}
},
"234": {
"smartAccountId": "234",
"smartAccountName": "Dummy name",
"201": {
"virtualAccountId": "201",
"virtualAccountName": "DEFAULT"
}
}
}
}
here I need to put an Index on the "smartAccountId" and "virtualAccountId". The problem is the key for the nested document is not fixed, its the "smartAccountId" or "virtualAccountId" we are using as the key (123 in the example), how can we get such a document indexed on MongoDB?
Thanks
PS: I already have an array in the original document, so cant introduce one more array, as we wont be able to index more than one array in a given document.

Related

Add new array in embedded array field by fetching max value + 1 from existing array embedded field

Below is the structure of my Collection.
{
"_id": "61b70e9d7ba0e2a555e06a59",
"ProjId": 12,
"ArtifactAttributes": [
{
"_t": "ArtifactAttributes",
"AttrId": 1,
"AttributeName": "Description",
"AttributeValue": "Test Description"
},
{
"_t": "ArtifactAttributes",
"AttrId": 2,
"AttributeName": "Details",
"AttributeValue": "Test Details"
}
]
}
I wanted to add below mentioned array in the ArtifactAttributes array field present in the collection but while adding the same wanted to set AttrId by fetching max value + 1 of AttrId present in the existing array list.
AttrId is totally dependents on the max value of the AttrId present in the existing array it could 3,4,5 anything.
{
"_t": "ArtifactAttributes",
"AttrId": 3,
"AttributeName": "Owner",
"AttributeValue": "Test Owner"
}
Final document after adding above array will be as below
{
"_id": "61b70e9d7ba0e2a555e06a59",
"ProjId": 12,
"ArtifactAttributes": [
{
"_t": "ArtifactAttributes",
"AttrId": 1,
"AttributeName": "Description",
"AttributeValue": "Test Description"
},
{
"_t": "ArtifactAttributes",
"AttrId": 2,
"AttributeName": "Details",
"AttributeValue": "Test Details"
},
{
"_t": "ArtifactAttributes",
"AttrId": 3,
"AttributeName": "Owner",
"AttributeValue": "Test Owner"
}
]
}
I tried around a little bit, to use $push or $addField does not work, so I came up with concatinating 2 arrays where I define your object as an object of the 2nd array for concatination.
So simplified:
Set the Array you allready have, as a concatination of that array with a new array containing your object with calculated AttrId of the other arrays AttrId.
I directly added the function into the object you want to insert:
let toBeInserted = {
"_t": "ArtifactAttributes",
"AttributeName": "Owner",
"AttributeValue": "Test Owner"
}
// If you are using typescript make sure AttrId is previously unset
// because it will throw an error if it wants to insert this into a "number" field
toBeInserted["AttrId"] = {
'$add': [{ '$max': '$ArtifactAttributes.AttrId' }, 1]
}
await this.testSchema.updateOne({}, // Make sure the following is an array, array indicates aggregation, this does not work without
[{
'$set': {
'ArtifactAttributes': {
'$concatArrays': [
'$ArtifactAttributes', [
toBeInserted
]
]
}
}
}])
Here proof of it working:

how to update partial document of an array

i have a person document, that have list of pets:
{
"personId": "kjadfh97r0",
"pets": [
{
"petId": "dfjkh32476",
"name": "kitty",
"kind": "cat"
},
{
"petId": "askdjfh2794857",
"name": "rexy",
"kind": "dog"
}
]
}
I want to find certain pen inside of certain person and update just some fields, so I did something like:
db.people.findAndModify({
query: { "personId": "kjadfh97r0", "pets.petId": "dfjkh32476" },
update: {"$set":{"pets.$":{"kind":"tiger"}}}
})
but what happens to me is that the whole document is replaced with "kind":"tiger", and I just wanted to update the "kind" field any keep the rest.
You should specify entire path for $set when you update nested document using positional operator, otherwise the document will be replaced:
db.people.findAndModify({
query: { "personId": "kjadfh97r0", "pets.petId": "dfjkh32476" },
update: { $set: {"pets.$.kind": "tiger"} }
})

Exclude some text fields in MongoDB text index

I have a collection that has a text index like this:
db.mycollection.createIndex({ "$**" : "text"},{ "name" : "AllTextIndex"}))
The collection's documents have many text data blocks. I want to exclude some of them in order to not get results that includes text matched from the excluded blocks.
However, I don't want to define each field in the text index like below in order to exclude the, for example, NotTextSearchableBlock block:
db.application.ensureIndex({
"SearchableTextBlock1": "text",
"SearchableTextBlock2": "text",
"SearchableTextBlock3": "text"
})
Here is a document example:
{
"_id": "e0832e2d-6fb3-47d8-af79-08628f1f0d84",
"_class": "com.important.enterprise.acme",
"fields": {
"Organization": "testing"
},
"NotTextSearchableBlock": {
"something": {
"SomethingInside": {
"Text":"no matcheable text"
}
}
},
"SearchableTextBlock1": {
"someKey": "someTextCool"
},
"SearchableTextBlock2": {
"_id": null,
"fields": {
"Status": "SomeText"
}
},
"SearchableTextBlock3": {
"SomeSubBlockArray": [
{
"someText": "Pepe"
}
]
}
}
To answer your question, there is no documented way to exclude certain fields from a text index. (See Text Indexes in mongodb's documention.)
As you already know:
When establishing a text index, you have to identify the specific text field(s) to index. The field must be a string or an array of string elements.
db.mycollection.createIndex({
mystringfield : "text"
mystringarray : "text"
})
You may also index all text found within a collection's documents by using the wildcard specifier $**.
db.mycollection.createIndex(
{ "$**" : "text" },
{ name : "mytextindex" }
)

Sorting by document values in couchbase and scala

I am using couchbase and I have a document (product) that looks like:
{
"id": "5fe281c3-81b6-4eb5-96a1-331ff3b37c2c",
"defaultName": "default name",
"defaultDescription": "default description",
"references": {
"configuratorId": "1",
"seekId": "1",
"hsId": "1",
"fpId": "1"
},
"tenantProducts": {
"2": {
"adminRank": 1,
"systemRank": 15,
"categories": [
"3"
]
}
},
"docType": "product"
}
I wish to get all products (this json is product) that belong to certain category, So i've created the following view:
function (doc, meta) {
if(doc.docType == "product")
{
for (var tenant in doc.tenantProducts) {
var categories = doc.tenantProducts[tenant].categories
// emit(categories, doc);
for(i=0;i<categories.length;i++)
{
emit([tenant, categories[i]], doc);
}
}
}
}
So i can run the view with keys like:
[["tenantId", "Category1"]] //Can also have: [["tenant1", "Category1"],["tenant1", "Category2"] ]
My problem is that i receive the document, but i wish to sort the documents by their admin rank and system rank, these are 2 fields that exists in the "value".
I understand that the only solution would be to add those fields to my key, determine that my key would be from now:
[["tenantId", "Category1", "systemRank", "adminRank"]]
And after i get documents, i need to sort by the 3rd and 4th parameters of the key ?
I just want to make sure i understand this right.
Thanks

MongoDB: How to update a compound item of an array ensuring no duplicates

Here below is a hypothetical Users collection where more than one address is allowed:
{
"firstName": "Joe",
"lastName": "Grey",
...
"addresses":
[
{
"name": "Default",
"street": "..."
...
},
{
"name": "Home",
"street": "..."
...
},
{
"name": "Office",
"street": "..."
...
}
]
}
Every address has a name... which should be unique – e.g. there couldn't be two addresses named Default. If I want to update let's say the address at index 1 (Home), how do I ensure the update data does not contain names Default and Office?
I guess a two-steps approach (i.e. find and then update) wouldn't be very correct since data might be updated between the find and the subsequent update operation, isn't?
var renamed = 'Office'; // from user input
var users = getUserMongoCollection();
users.update({_id:userId, 'addresses.name': { $ne : renamed } },
{ $set : { 'addresses.1.name' : renamed } }, function(err){
//all done!
});
Find the record by ID, and only update it if the array doesn't contain the new name.