Upsert embedded object in mongoDB - node.js

Given this Person collection:
{
"_id" : ObjectId("4f8e95a718bcv9c74da1e6511a"),
"name" : "John",
"hobbies" : [{
"id" : 001,
"name" : "reading",
"location" : "home"
},{
"id" : 002,
"name" : "sport",
"location" : "outside"
}]
}
and these new/edited Hobby objects:
{
"name" : "walking",
"location" : "outside"
}
and
{
"id" : 001,
"name" : "reading",
"location" : "outside"
}
If I know the Person that I want to manage, what is be the best way to upsert embedded objects?
Currently my approach is to find the Person object, make the required modifications to it in my code, and then save it back to the DB. This works. But I'd like to simplify and reduce the number of round trips to the database.

Related

Arangodb AQL Joining, merging, embedding nested three collections or more

I have the following collections, based on the example Arangodb doc here but have added a third collection called region
Users
{
"name" : {
"first" : "John",
"last" : "Doe"
},
"city" : "cities/2241300989",
"_id" : "users/2290649597",
"_rev" : "2290649597",
"_key" : "2290649597"
}
Cities
{
"population" : 1000,
"name" : "Metropolis",
"region" : "regions/2282300990",
"_id" : "cities/2241300989",
"_rev" : "2241300989",
"_key" : "2241300989"
}
Regions
{
"name" : "SomeRegion1",
"_id" : "regions/2282300990",
"_rev" : "2282300990",
"_key" : "2282300990"
}
I want to have a target result like this
[
{
"user" : {
"name" : {
"first" : "John",
"last" : "Doe"
},
"_id" : "users/2290649597",
"_rev" : "2290649597",
"_key" : "2290649597"
},
"city" : {
"population" : 1000,
"name" : "Metropolis",
"_id" : "cities/2241300989",
"_rev" : "2241300989",
"_key" : "2241300989",
"region" : {
"name" : "SomeRegion1",
"_id" : "regions/2282300990",
"_rev" : "2282300990",
"_key" : "2282300990"
}
}
}
]
The example in the Arangodb doc here only has queries for two collections
FOR u IN users
FOR c IN cities
FILTER u.city == c._id RETURN merge(u, {city: c})
# However I want to have more than two collections e.g.
FOR u IN users
FOR c IN cities
For r IN regions
FILTER u.city == c._id and c.region == r._id RETURN merge(????????)
How would you get the result with three collections joined as above? What happens if I want a forth nested one?
When you store a document _id that references another collection, then you can leverage the DOCUMENT AQL command.
So your AQL query becomes a bit simpler, like this:
FOR u IN users
LET city = DOCUMENT(u.city)
LET city_with_region = MERGE(city, { region: DOCUMENT(city.region})
RETURN MERGE(u, { city: city_with_region})
This query could be collapsed even more, but I left it like this so it's more self documenting.
What is cool about DOCUMENT is you can return only a single attribute of a document, such as LET region_name = DOCUMENT(city.region).name.
I've also found that in most cases it's more performant that doing a subquery to locate the document.
Probably something like this:
FOR u IN users
FOR c IN cities
For r IN regions
FILTER u.city == c._id AND c.region == r._id
RETURN { user: u, city: MERGE(c, {region: r } }
Is there a particular reason why you store ids instead of keys to refer to cities and regions? The _id is just a virtual field that consists of the _key prefixed by the collection name (plus a slash). So this would work just as well (I intentionally omit the internal _id and _rev fields):
Users
{
"name" : {
"first" : "John",
"last" : "Doe"
},
"city" : "2241300989",
"_key" : "2290649597"
}
Cities
{
"population" : 1000,
"name" : "Metropolis",
"region" : "2282300990",
"_key" : "2241300989"
}
Regions
{
"name" : "SomeRegion1",
"_key" : "2282300990"
}
FOR u IN users
FOR c IN cities
For r IN regions
FILTER u.city == c._key AND c.region == r._key
RETURN { user: u, city: MERGE(c, {region: r } }

MongoDB DBRefs to query reference collection field

I have two different collection in two different MongoDB databases.
Collection users in db1:
{
"_id" : "xyz",
"name" : "John",
"project_id" : "abc"
}
Collection project in db2:
{
"_id" : "abc",
"regionCode" : "1AB"
}
Now I want to get list of all users who belong to region 1AB. How do I achieve that.
I read about DBRefs. So I changed structure of collections users but still i can not query directly to reference collection attribute directly
collection users new structure :
{
"_id" : "xyz",
"name" : "John",
"project_id" : "abc",
"projectData" : {
"$ref" : "project",
"$id" : "abc",
"$db" : "db2"
}
}
Now how do I get list of all users that belong to project regioncode 1AB.
I am using nodejs native client

Updating a specific doc in array in subdocument in MongoDb

I have a document user that have a schema as:
{
"_id" : ObjectId("57b2d706f61d04e8d99dd983"),
"addressesAsVendor" : [
{
"_id" : "V1",
"addressLine1" : "al1",
"addressLine2" : "al2",
"street" : "street",
"city" : "city",
"country" : "IN",
"pincode" : "490020",
"location" : {
"latitutde" : "lat1",
"longitude" : "lon1"
}
},
{
"_id" : "V2",
"addressLine1" : "al1",
"addressLine2" : "al2",
"street" : "street",
"city" : "city",
"country" : "IN",
"pincode" : "490020",
"location" : {
"latitutde" : "lat2",
"longitude" : "lon2"
}
}
]
}
Now let's suppose I want to update the V1 id of the addressesAsVendor array which is inside the user Id 57b2d706f61d04e8d99dd983 with the data:
{
"addressLine1" : "al1 new",
"addressLine2" : "al2 new",
"street" : "street new",
"city" : "city new",
"country" : "IN new",
"pincode" : "490020 new",
"location" : {
"latitutde" : "lat1 new",
"longitude" : "lon1 new"
}
}
So the new user doc will look like:
{
"_id" : ObjectId("57b2d706f61d04e8d99dd983"),
"addressesAsVendor" : [
{
"_id" : "V1",
"addressLine1" : "al1 new",
"addressLine2" : "al2 new",
"street" : "street new",
"city" : "city new",
"country" : "IN new",
"pincode" : "490020 new",
"location" : {
"latitutde" : "lat1 new",
"longitude" : "lon1 new"
}
},
{
"_id" : "V2",
"addressLine1" : "al1",
"addressLine2" : "al2",
"street" : "street",
"city" : "city",
"country" : "IN",
"pincode" : "490020",
"location" : {
"latitutde" : "lat2",
"longitude" : "lon2"
}
}
]
}
How this can be achieved in MongoDb and what's the best way of keeping multiple addresses, which can be easily shown in the Address page of the user and also minimum in load, I mean will be easy to access when required.
Please shed your views.
:)
You can do the update using the positional $ operator:
var data = {
"addressLine1" : "al1 new",
"addressLine2" : "al2 new",
"street" : "street new",
"city" : "city new",
"country" : "IN new",
"pincode" : "490020 new",
"location" : {
"latitutde" : "lat1 new",
"longitude" : "lon1 new"
}
};
collection.update(
{ "addressesAsVendor._id" : "V1" },
{ "$set": { "addressesAsVendor.$": data } },
function(err, result) {
if (err) return handleError(err);
console.log(result);
}
)
The positional operator in the above saves the index (0 in the case above) of the element from the array that matched the query. This means that if you knew the position of the element beforehand (which is nearly impossible in a real life case), you could just change the update statement to: { "$set": { "addressesAsVendor.0": data } }.
Since the positional $ operator acts as a placeholder for the first element that matches the query document, and the array field must appear as part of the query document hence the query { "addressesAsVendor._id" : "V1" } is essential to get the $ operator to work properly.
Please note that the positional $ operator (for now) updates the first relevant document ONLY, there is a JIRA ticket for this.
For your follow-up question which seeks to find the best way of keeping multiple addresses, which can be easily shown in the Address page of the user and also minimum in load:
Your current schema is a better approach than creating a separate collection of addresses since separate collections require more work i.e. finding a user + its addresses is two queries and requires extra work whereas the above schema embedded documents are easy and fast (single seek). There are no big differences for inserts and updates. So, separate collections are good if you need to select individual documents, need more control over querying, or have huge documents. Embedded documents are good when you want the entire document, the document with a $slice of the embedded addressesAsVendor, or with no addresses at all.
As a general rule, if you have a lot of "addresses" or if they are large, a separate collection might be best.
Smaller and/or fewer documents tend to be a natural fit for embedding.
You can use updateOne (MongoDB query) to update the document.
NodeJS equivalent is:-
collection.update(criteria, update[[, options], callback]);
Please change the object id accordingly.
db.address.updateOne({ "_id" : ObjectId("57c04425c400a6b59c9bc1ee"), "addressesAsVendor._id" : "V1" }, { $set: { "addressesAsVendor.$.addressLine1" : "al1 new",
"addressesAsVendor.$.addressLine2" : "al2 new",
"addressesAsVendor.$.street" : "street new",
"addressesAsVendor.$.city" : "city new",
"addressesAsVendor.$.country" : "IN new",
"addressesAsVendor.$.pincode" : "490020 new",
"addressesAsVendor.$.location" : {
"latitutde" : "lat1 new",
"longitude" : "lon1 new"
}
} });

Find documents with sub-documents matching both of two (or more) properties

In Node with Mongoose I want to find an object in the collection Content. It has a list of sub-documents called users which has the properties stream, user and added. I do this to get all documents with a certain user's _id property in there users.user field.
Content.find( { 'users.user': user._id } ).sort( { 'users.added': -1 } )
This seems to work (although I'm unsure if .sort is really working here. However, I want to match two fields, like this:
Content.find( { 'users.user': user._id, 'users.stream': stream } } ).sort( { 'users.added': -1 } )
That does not seem to work. What is the right way to do this?
Here is a sample document
{
"_id" : ObjectId("551c6b37859e51fb9e9fde83"),
"url" : "https://www.youtube.com/watch?v=f9v_XN7Wxh8",
"title" : "Playing Games in 360°",
"date" : "2015-03-10T00:19:53.000Z",
"author" : "Econael",
"description" : "Blinky is a proof of concept of enhanced peripheral vision in video games, showcasing different kinds of lens projections in Quake (a mod of Fisheye Quake, using the TyrQuake engine).\n\nDemo and additional info here:\nhttps://github.com/shaunlebron/blinky\n\nThanks to #shaunlebron for making this very interesting proof of concept!\n\nSubscribe: http://www.youtube.com/subscription_center?add_user=econaelgaming\nTwitter: https://twitter.com/EconaelGaming",
"duration" : 442,
"likes" : 516,
"dislikes" : 13,
"views" : 65568,
"users" : [
{
"user" : "54f6688c55407c0300b883f2",
"added" : 1427925815190,
"_id" : ObjectId("551c6b37859e51fb9e9fde84"),
"tags" : []
}
],
"images" : [
{
"hash" : "1ab544648d7dff6e15826cda7a170ddb",
"thumb" : "...",
"orig" : "..."
}
],
"tags" : [],
"__v" : 0
}
Use $elemMatch operator to specify multiple criteria on an array of embedded documents:
Content.find({"users": {$elemMatch: {"user": user.id, "stream": stream}}});

how to query a collection based on value of a field array in mongoose

I have the following user schema
{
"__v" : 26,
"_id" : ObjectId("52b47058fe5e3493a2cf8365"),
"live_sockets" : [
"CEGok0rSz2UtBIHX9DlV",
"s6M45OA0MDBs4aGL9DlW",
"2XiszSuyiPAGr-Ga9ZCN",
"lIFNEeUgXRB6tgvP9ZCO",
"JPtzQOD_52maf6VS9gtb",
"kDL06aXiI8WWlig19gtc",
"75Bt5p6WqgmRcyer9xSm",
"Ge_sKLen32Q91wLW9xSn",
"EpHt3qju34GTZevU_Bsd",
"n0hq0EQAjJOptxdy_qEB",
],
"name" : {
"first" : "Test",
"last" : "User"
},
"notifications_count" : 0,
"role" : 1
}
How can i query a user based on a particular live socket as socket_ids are unique.
If i have a variable called current_socket_id how can i find a user which the socket belongs to..?
db.collection.find({"live_sockets" : {$in : [socketId]}},{"name" : 1,"_id":0})
Will give the name for the socket id

Resources