Update id in mongodb collection - mongodb

Hello im learning MongoDB and my exercise is to update id in one of my collection to another id. I Have two collection
First collection "workers" :
{
"_id" : ObjectId("6224ec342c2d7202b9ad9af6"),
"id_worker" : 180,
"id_boss" : 100,
"hired" : ISODate("2005-02-20T00:00:00Z"),
"id_group" : 10
}
Second collection "groups":
{
"_id" : ObjectId("6224ebe12c2d7202b9ad9af1"),
"id_group" : 10,
"name" : "Administrators",
"addres" : "Example"
}
I would like to change an "_id" of my worker to its "identifiaction id" from collection "groups"
So my worker should look like:
{
"_id" : ObjectId("6224ec342c2d7202b9ad9af6"),
"id_worker" : 180,
"id_boss" : 100,
"hired" : ISODate("2005-02-20T00:00:00Z"),
"id_group" : ObjectId("6224ebe12c2d7202b9ad9af1")
}
I have query like this. When i use print it shows list of my workers with name of the group. How to change it to modify this collection? use update?
var workers = db.workers.find();
while (workers.hasNext()) {
worker = workers.next();
group = db.groups.findOne({"id_group": worker.id_group});
print(group.name);
}

Try this:
db.workers.aggregate([
{
$lookup: {
from: "groups",
localField: "id_group",
foreignField: "id_group",
as: "group"
}
},
{
$project: {
id_worker: 1,
id_boss: 1,
hired: 1,
id_group: { $first: "$group.name" }
}
}
])
Mongo Playground

Related

MongoDB Lookup MainField to FieldA or FieldB

I need to join two MongoDB Colletions with lookup, the MainField to join from de first collection has to join with the other collection through Field A or Field B.
MainField is an array, with this structure [Doc1.FieldA, Doc2.FieldA, Doc3.FieldB,...].
FieldA is Unique-Index.
FieldB is Non-Unique-Index, it is for group FieldB with a unique value.
The problem is that I need to keep the order of the MainField Array.
I like to do something like this:
db.getCollection("collection1").aggregate([
$lookup: {
from: "collection2",
localField: "mainField",
foreignField: $or:["fieldA","FieldB"]
as: "mainFieldInfo"
}]
Is it possible to do this lookup or I need a different approach?
Collections examples, the documents are simplified there are more fields
in each document.
Collection Machines (1 example) :
{
"_id" : ObjectId("5c793a188021710636865c33"),
"MachineName" : "CER3A",
"NextJobs" : [ //--> MainField
"ST105862", // match with FIELD B - Flags.STS
"OFT083520", // match with FIELD A - Lote
"OFT083365",
"ST105946"
]
}
Collection Works (2 example, 1 to match with FieldA, 1 to match Field B):
Field A example:
FieldB*(Flags.STS)* is empty
{
"_id" : ObjectId("5c1b89d0b6e97d001816595e"),
"Lote" : "OFT083520", //--> FIELD A
"Flags" : {
"ShipsFinished" : true,
"PlanFinished" : true,
"Finished" : true,
"IdDefecto" : false,
"EstadoOF" : 4,
"GCT" : "GCT018929",
"PedidoVenta" : "",
"STS" : "", //--> FIELD B
}
}
Field B Example (2 docs):
FieldA*(Lote)* is diferent in each document, FieldB*(Flags.STS)* is equal
{
"_id" : ObjectId("5dcd78e2a2061070185400e2"),
"Lote" : "OFT083671", //--> FIELD A
"Flags" : {
"B2" : 1,
"EstadoOF" : 4,
"Finished" : false,
"GCT" : "GCT024270",
"LaSI" : 0,
"PedidoVenta" : "P056048",
"SPO" : "PO23579",
"STS" : "ST105862", //--> FIELD B
"Inks" : "true",
}
}
{
"_id" : ObjectId("5dcd78e2a2061070185401f0"),
"Lote" : "OFT083672", //--> FIELD A
"Flags" : {
"B2" : 1,
"EstadoOF" : 4,
"Finished" : false,
"STS" : "ST105862", //--> FIELD B
"ShipsFinished" : false,
"TipoOF" : 1,
"EstatIQC" : 1,
}
}
You have to use the other form of $lookup stage, which allow to perform multiple conditions for the lookup stage.
Here's the query you have to run :
db.machines.aggregate([
{
$lookup: {
from: "works",
let: {
"nj": "$NextJobs"
},
pipeline: [
{
$match: {
$expr: {
$or: [
{
$in: [
"$Lote",
"$$nj"
]
},
{
$in: [
"$Flags.STS",
"$$nj"
]
}
]
}
}
}
],
as: "linkedWorks"
}
}
])
You can test it here

MongoDB: How to get the object names in collection?

and think you in advance for the help. I have recently started using mongoDB for some personal project and I'm interested in finding a better way to query my data.
My question is: I have the following collection:
{
"_id" : ObjectId("5dbd77f7a204d21119cfc758"),
"Toyota" : {
"Founder" : "Kiichiro Toyoda",
"Founded" : "28 August 1937",
"Subsidiaries" : [
"Lexus",
"Daihatsu",
"Subaru",
"Hino"
]
}
}
{
"_id" : ObjectId("5dbd78d3a204d21119cfc759"),
"Volkswagen" : {
"Founder" : "German Labour Front",
"Founded" : "28 May 1937",
"Subsidiaries" : [
"Audi",
"Volkswagen",
"Skoda",
"SEAT"
]
}
}
I want to get the object name for example here I want to return
[Toyota, Volkswagen]
I have use this method
var names = {}
db.cars.find().forEach(function(doc){Object.keys(doc).forEach(function(key){names[key]=1})});
names;
which gave me the following result:
{ "_id" : 1, "Toyota" : 1, "Volkswagen" : 1 }
however, is there a better way to get the same result and also to just return the names of the objects. Thank you.
I would suggest you to change the schema design to be something like:
{
_id: ...,
company: {
name: 'Volkswagen',
founder: ...,
subsidiaries: ...,
...<other fields>...
}
You can then use the aggregation framework to achieve a similar result:
> db.test.find()
{ "_id" : 0, "company" : { "name" : "Volkswagen", "founder" : "German Labour Front" } }
{ "_id" : 1, "company" : { "name" : "Toyota", "founder" : "Kiichiro Toyoda" } }
> db.test.aggregate([ {$group: {_id: null, companies: {$push: '$company.name'}}} ])
{ "_id" : null, "companies" : [ "Volkswagen", "Toyota" ] }
For more details, see:
Aggregation framework
$group
Accumulator operators
As a bonus, you can create an index on the company.name field, whereas you cannot create an index on varying field names like in your example.

Individual search result in multiple values in arrays

I have following model:
{
"_id" : ObjectId("5d61aaf8108e185191552bbb"),
"serials" : [
"e127av48-0697-4977-b096-5ce79c89a414",
"d163f80a-55ff-40fe-90b4-331ece5bebd5",
"4740021f-e9b5-4ca5-bf0e-8554c123bb94",
"320ffd42-f101-4b1d-8ff4-80bc693a29e6",
"fef5e68b-aed0-4a96-9488-7941c41d1c1f",
"2c0752ba-bf7a-4a3b-bd9f-14db4b2f8bae",
"6c5ff44d-5979-4bff-af12-9e6d282c3789",
"9c91bf91-72d7-4b71-827b-924947d6e93d",
"fb34b28e-afb1-4b6a-a3c1-5a1fe44246ee",
"91ab22ef-702f-4cbd-8919-a67a2b9a684c",
"ee1a7cb2-e088-47e6-a824-c8697df7d94c",
"0dc4c687-4db2-481e-a1a6-491320dede11",
"34612148-3e01-44ee-b262-de2035e63691",
"5ba85baf-e48a-40af-8578-55ff1a873c76",
"19fe3672-b6cb-4bb6-8d21-93412b938584",
"1d0d6f6d-1b49-461b-8661-ecbf43a6595e",
"d9a5455c-65ee-45e1-ae49-33cc15dec841",
"4a690a00-a76c-4d3e-aee3-78b2bb731b0c",
"ae331830-40b4-457c-8cc4-5d548f769c3e",
"fe3e460b-c89d-4ace-8a36-5ba2b53bf4d0",
"2cc6a2a0-e029-475f-a7fc-a46a79afb605",
"a7d07767-eada-4ce3-b083-9b048e9ae9f4"
],
"name" : "ApiCard",
"producer" : "Farmina",
"form" : "syrop",
"__v" : 0
}
I would like to retrive documents (multiple) from collection based on this serial numbers ("serials" field). For example i am finding:
[
"e127av48-0697-4977-b096-5ce79c89a414",
"d163f80a-55ff-40fe-90b4-331ece5bebd5",
"4740021f-e9b5-4ca5-bf0e-8554c123bb94",
"key that doesn't exist",
]
We have to assume that one of the serial number doesn't exist, so would like to get information for individual serial, expected output:
[
{
"serial":"e127av48-0697-4977-b096-5ce79c89a414",
"doc":{
....whole document where above serial is in array field "serials"
}
},
{
"serial":"e127av48-0697-4977-b096-5ce79c89a414",
"doc":{
....whole document where above serial is in array field "serials"
}
},
{
"serial":"e127av48-0697-4977-b096-5ce79c89a414",
"doc":{
....whole document where above serial is in array field "serials"
}
},
{
"serial":"key that doesn't exist",
"doc": null
}
]
I was trying the simplest solution - mongodb find by multiple array items, but unfortunately it'doesn't return info for individual serial number. I'am not sure it's possible to prepare this kind of query. I think some complex aggregation could perform it, but i don't even know this kind of pipelines.
Of course, i can get simple solution by using multiple aggregate or even find, but it could impact on performance, when application will be looking for 10000 records per request.
The following query can do the trick:
db.collection.aggregate([
{
$limit:1
},
{
$project:{
"_id":0,
"serialsToSearch":[
"e127av48-0697-4977-b096-5ce79c89a414",
"d163f80a-55ff-40fe-90b4-331ece5bebd5",
"4740021f-e9b5-4ca5-bf0e-8554c123bb94",
"key that doesn't exist",
]
}
},
{
$unwind:"$serialsToSearch"
},
{
$lookup:{
"from":"collection",
"let":{
"serial":"$serialsToSearch"
},
"pipeline":[
{
$match:{
$expr:{
$in:["$$serial","$serials"]
}
}
},
{
$project:{
"serials":0
}
}
],
"as":"searialsLookup"
}
},
{
$unwind:{
"path":"$searialsLookup",
"preserveNullAndEmptyArrays":true
}
},
{
$project:{
"serial":"$serialsToSearch",
"doc":{
$ifNull:["$searialsLookup",null]
}
}
}
]).pretty()
Data Set:
{
"_id" : ObjectId("5d61aaf8108e185191552bbb"),
"serials" : [
"e127av48-0697-4977-b096-5ce79c89a414",
"d163f80a-55ff-40fe-90b4-331ece5bebd5",
"4740021f-e9b5-4ca5-bf0e-8554c123bb94",
"320ffd42-f101-4b1d-8ff4-80bc693a29e6",
"fef5e68b-aed0-4a96-9488-7941c41d1c1f",
"2c0752ba-bf7a-4a3b-bd9f-14db4b2f8bae",
"6c5ff44d-5979-4bff-af12-9e6d282c3789",
"9c91bf91-72d7-4b71-827b-924947d6e93d",
"fb34b28e-afb1-4b6a-a3c1-5a1fe44246ee",
"91ab22ef-702f-4cbd-8919-a67a2b9a684c",
"ee1a7cb2-e088-47e6-a824-c8697df7d94c",
"0dc4c687-4db2-481e-a1a6-491320dede11",
"34612148-3e01-44ee-b262-de2035e63691",
"5ba85baf-e48a-40af-8578-55ff1a873c76",
"19fe3672-b6cb-4bb6-8d21-93412b938584",
"1d0d6f6d-1b49-461b-8661-ecbf43a6595e",
"d9a5455c-65ee-45e1-ae49-33cc15dec841",
"4a690a00-a76c-4d3e-aee3-78b2bb731b0c",
"ae331830-40b4-457c-8cc4-5d548f769c3e",
"fe3e460b-c89d-4ace-8a36-5ba2b53bf4d0",
"2cc6a2a0-e029-475f-a7fc-a46a79afb605",
"a7d07767-eada-4ce3-b083-9b048e9ae9f4"
],
"name" : "ApiCard",
"producer" : "Farmina",
"form" : "syrop",
"__v" : 0
}
Output:
{
"serial" : "e127av48-0697-4977-b096-5ce79c89a414",
"doc" : {
"_id" : ObjectId("5d61aaf8108e185191552bbb"),
"name" : "ApiCard",
"producer" : "Farmina",
"form" : "syrop",
"__v" : 0
}
}
{
"serial" : "d163f80a-55ff-40fe-90b4-331ece5bebd5",
"doc" : {
"_id" : ObjectId("5d61aaf8108e185191552bbb"),
"name" : "ApiCard",
"producer" : "Farmina",
"form" : "syrop",
"__v" : 0
}
}
{
"serial" : "4740021f-e9b5-4ca5-bf0e-8554c123bb94",
"doc" : {
"_id" : ObjectId("5d61aaf8108e185191552bbb"),
"name" : "ApiCard",
"producer" : "Farmina",
"form" : "syrop",
"__v" : 0
}
}
{ "serial" : "key that doesn't exist", "doc" : null }
Note: The query won't give expected output if the collection would be empty.
Aggregation stages details:
STAGE I: Limiting the records to 1, as initially, our motive is to inject the input array in aggregation. The injection would be done in no time.
STAGE II: Projecting the input array as serialsToSearch
STAGE III: Now we have the input array as a field, we can unwind it
STAGE IV: Lookup in the same collection with each field of the input array and check if the searched serial is present in serials array
STAGE V: unwinding the lookup output
STAGE VI: Projecting fields as per the response required.

Mongodb aggregate query count records for large dataset

I'm attempting to query all data from the errorlog collection, and in the same query grab a count of relevant irs_documents for each errorlog entry.
The problem is that there are too many records in the irs_documents collection to perform a $lookup.
Is there a performant method of doing this in one MongoDB query?
Failed attempt
db.getCollection('errorlog').aggregate(
[
{
$lookup: {
from: "irs_documents",
localField: "document.ssn",
foreignField: "ssn",
as: "irs_documents"
}
},
{
$group: {
_id: { document: "$document", error: "$error" },
logged_documents: { $sum : 1 }
}
}
]
)
Error
Total size of documents in $lookup exceeds maximum document size
Clearly this solution won't work. MongoDB is literally attempting to gather whole documents with $lookup, where I just want a count.
"errorlog" collection sample data:
/* 1 */
{
"_id" : ObjectId("56d73955ce09a5a32399f022"),
"document" : {
"ssn" : 1
},
"error" : "Error 1"
}
/* 2 */
{
"_id" : ObjectId("56d73967ce09a5a32399f023"),
"document" : {
"ssn" : 2
},
"error" : "Error 1"
}
/* 3 */
{
"_id" : ObjectId("56d73979ce09a5a32399f024"),
"document" : {
"ssn" : 3
},
"error" : "Error 429"
}
/* 4 */
{
"_id" : ObjectId("56d73985ce09a5a32399f025"),
"document" : {
"ssn" : 9
},
"error" : "Error 1"
}
/* 5 */
{
"_id" : ObjectId("56d73990ce09a5a32399f026"),
"document" : {
"ssn" : 1
},
"error" : "Error 8"
}
"irs_documents" collection sample data
/* 1 */
{
"_id" : ObjectId("56d73905ce09a5a32399f01e"),
"ssn" : 1,
"name" : "Sally"
}
/* 2 */
{
"_id" : ObjectId("56d7390fce09a5a32399f01f"),
"ssn" : 2,
"name" : "Bob"
}
/* 3 */
{
"_id" : ObjectId("56d7391ace09a5a32399f020"),
"ssn" : 3,
"name" : "Kelly"
}
/* 4 */
{
"_id" : ObjectId("56d7393ace09a5a32399f021"),
"ssn" : 9,
"name" : "Pippinpaddle-Oppsokopolis"
}
The error is self explanatory. Lookup is essentially combining two documents into single BSON document so MongoDB document size limit is biting you back.
You need to ask yourself, is it absolute necessary to perform both actions in one operation? if yes, do it the way you have to do in previous versions of MongoDB where $lookup is not supported.
Said that, perform two queries and perform merger in your client.
OPTION #1: you can aggregate on irs_documents and export computed result into another collection. Since, there will be very few objects in each document, I don't think you'll hit problem. But, you may hit memory problems and forced to use disk for aggregation framework. Try following solution and see if it works.
db.irs_documents.aggregate([
{
$group:{_id:"$ssn", count:{$sum:1}}
},
{
$out:"irs_documents_group"
}]);
db.errorlog.aggregate([
{
$lookup: {
from: "irs_documents_group",
localField: "document.ssn",
foreignField: "ssn",
as: "irs_documents"
}
},
{
$group: {
_id: { document: "$document", error: "$error" },
logged_documents: { $sum : 1 }
}
}
])
OPTION #2: If above solution is not working, you can always use map reduce, though it will not be an elegant solution but will work.

MongoDB custom sorting

i have a collection of records as follows
{
"_id":417,
"ptime":ISODate("2013-11-26T11:18:42.961Z"),
"p":{
"type":"1",
"txt":"test message"
},
"users":[
{
"uid":"52872ed59542f",
"pt":ISODate("2013-11-26T11:18:42.961Z")
},
{
"uid":"524eb460986e4",
"pt":ISODate("2013-11-26T11:18:42.961Z")
},
{
"uid":"524179060781e",
"pt":ISODate("2013-11-27T12:48:35Z")
}
],
},
{
"_id":418,
"ptime":ISODate("2013-11-25T11:18:42.961Z"),
"p":{
"type":"1",
"txt":"test message 2"
},
"users":[
{
"uid":"524eb460986e4",
"pt":ISODate("2013-11-23T11:18:42.961Z")
},
{
"uid":"52872ed59542f",
"pt":ISODate("2013-11-24T11:18:42.961Z")
},
{
"uid":"524179060781e",
"pt":ISODate("2013-11-22T12:48:35Z")
}
],
}
How to sort the above records with descending order of ptime and pt where users uid ="52872ed59542f" ?
If you want to do such a sort, you probably want to store your data in a different way. MongoDB in generally is not near as good with manipulating nested documents as top level fields. In your case, I would recommend splitting out ptime, pt and uid into their own collection:
messages
{
"_id":417,
"ptime":ISODate("2013-11-26T11:18:42.961Z"),
"type":"1",
"txt":"test message"
},
users
{
"id":417,
"ptime":ISODate("2013-11-26T11:18:42.961Z"),
"uid":"52872ed59542f",
"pt":ISODate("2013-11-26T11:18:42.961Z")
},
{
"id":417,
"ptime":ISODate("2013-11-26T11:18:42.961Z"),
"uid":"524eb460986e4",
"pt":ISODate("2013-11-26T11:18:42.961Z")
},
{
"id":417,
"ptime":ISODate("2013-11-26T11:18:42.961Z"),
"uid":"524179060781e",
"pt":ISODate("2013-11-27T12:48:35Z")
}
You can then set an index on the users collection for uid, ptime and pt.
You will need to do two queries to also get the text messages themselves though.
You can use the Aggregation Framework to sort by first ptime and then users.pt field as follows.
db.users.aggregate(
{$sort : {'ptime' : 1}},
{$unwind : "$users"},
{$match: {"users.uid" : "52872ed59542f"}},
{$sort : {'users.pt' : 1}},
{$group : {_id : {id : "$_id", "ptime" : "$ptime", "p" : "$p"}, users : {$push : "$users"}}},
{$group : {_id : "$_id.id", "ptime" : {$first : "$_id.ptime"}, "p" : {$first : "$_id.p"}, users : {$push : "$users"}}}
);
db.yourcollection.find(
{
users:{
$elemMatch:{uid:"52872ed59542f"}
}
}).sort({ptime:-1})
But you will have problems with order by pt field. You should use Aggregation Framework to project data or use Derick's approach.