Best Practice to Update MongoDB Document - node.js

I'm sorry it's a technical question about how to implement transaction or pipeline in mongodb. So I have orders collection, in order document it has array of object which contains order items. In each order item, it has an array that contains material object. Let's say those materials are ingredients of an item, this object contains inventoryId and amount.
I have inventories collection which has availability property.
The order is finished so it needs to update the inventory availability based on material amount?
Here I give an example of the document.
// orders collection
{
"_id": "SomeId",
"items": [
{
"_id": "SomeId",
"materials": {
"inventoryId": "abc",
"amount": 5,
}
},
],
}
// inventories collection
{
"_id": "abc",
"availability": 100,
}
Thanks in advance for any help.

Related

Cloudant Sorting on a nullable field

I want to sort on a field lets say name which is indexed in Cloudant DB. I am getting all the documents both which has this name field and which doesn't by using the index without sort . But when i try to sort with the name field I am not getting the documents which doesn't have this name field in the doc.
Is there any way to do this by using the query indexes. I want all the documents in sorted order which doesn't have the name field too.
For Example :
Below are some documents:
{
"_id": 1234,
"classId": "abc",
"name": "Happa"
}
{
"_id": 12345,
"classId": "abc",
"name": "Prasanth"
}
{
"_id": 123456,
"classId": "abc",
}
Below is the Query what i am trying to execute:
{
"selector": {
"classId": "abc",
"name" :{
"or" : [
{"$exists": true},{"$exists": false}
]
}
},
"sort": [{ "classId": "asc" }, { "name": "asc" }],
"use_index": "idx-classId_name"
},
I am expecting all the documents to be returned in a sorted order including the document which doesn't have that name field.
Your query makes no sense to me as it stands. You're requesting a listing of documents which either have, or don't have a specific field (meaning every document), and expecting to sort those on this field that may or may not exist. Such an order isn't defined out of the box.
I'd remove the name clause from the selector, sorting only on the classId field which appear in every document, and then do the secondary partial ordering on the client side, so you can decide how you intend to mix in the documents without the name field with those that have it.
Another solution is to use a view instead of a Cloudant Query index. I've not tested this, but hopefully the intent is clear:
function(doc) {
if (doc && doc.classId) {
var name = doc.name || "[notfound]";
emit(doc.classId+"-"+name, 1);
}
}
which will key the docs on "classId-name" and for docs with no name, a specified sentinel value.
Querying the view should return the documents lexicographically ordered on this compound key (which you can reverse with a query parameter if you wish).

How to define an index to use in a Mango Query

I am trying to create a CouchDB Mango Query with an index with the hope that the query runs faster. At the moment I have the following Mango Query which returns what I am looking for but it's slow. Therefore, I assume, I need to create an index to make it faster. I need help figuring out how to create that index.
selector: {
categoryIds: {
$in: categoryIds,
},
},
sort: [{ publicationDate: 'desc' }],
You can assume that my documents are let say news articles from different categories. Therefore in each document I have a field that contains one or more categories that the news article belongs to. For that I have an array of categoryIds for each document. My query needs to be optimized for queries like "Give me all news that have categoryId1 in their array of categoryIds sorted by publicationDate". What I don't know how to do is 1. How to define an index 2. What that index should be 3. How to use that index in "use_index" field of the Mango Query. Any help is appreciated.
Update after "Alexis Côté" answer:
If I define the index like this:
{
"_id": "_design/0f11ca4ef1ea06de05b31e6bd8265916c1bbe821",
"_rev": "6-adce50034e870aa02dc7e1e075c78361",
"language": "query",
"views": {
"categoryIds-json-index": {
"map": {
"fields": {
"categoryIds": "asc"
},
"partial_filter_selector": {}
},
"reduce": "_count",
"options": {
"def": {
"fields": [
"categoryIds"
]
}
}
}
}
}
And run the Mango Query like this:
{
"selector": {
"categoryIds": {
"$in": [
"e0bd5f97ac35bdf6893351337d269230"
]
}
},
"use_index": "categoryIds-json-index"
}
It still does return the results but they are not sorted in the order I want by publicationDate. So I am not clear what you are suggesting the solution is.
You can create an index as documented here
In your case, you will need an index on the "categoryIds" field.
You can specify the index using "use_index": "_design/<name>"
Note:The query planner should automatically pick this index if it's compatible.

Mongoose - Aggregation of two queries with condition

I've two different collections that are connected by the id of the garden. I've a list of gardens and I've a list of allocations where it will be stored the start and the end date of the allocation. I can check if a garden is allocated by verifying if today is between both dates in the allocation table.
Garden
{
"_id": "5b98df3c9275f2291c0d7dc3",
"id": "h1",
"size": 43
}
Allocation
{
"_id": "5b9bcb8ecb9dee0015150549",
"user": "5b9a2cd21eb58700141a3449",
"garden": "5b98df5c9275f2291c0d7dc6",
"start_date":"2018-09-14T00:00:00.000Z",
"end_date": "2018-11-14T00:00:00.000Z"
}
How can I return all the existing gardens with an aditional field 'ocupied' with true or false depending on if they exist on the allocation document between start_date and end_date?
I'd like to get an array of gardens with the following data
{
"_id": "5b98df3c9275f2291c0d7dc3",
"id": "h1",
"size": 43,
"occupied": true
}
You can do it one of two ways.
var today = ISODate();
Using $lookup
db.garden.aggregate([
{"$lookup":{
"from":"allocation",
"localField":"_id",
"foreignField":"garden",
"as":"garden"
}},
{"$unwind":"$garden"},
{"$addFields":{
"occupied":{
"$and":[
{"$gte":["$garden.start_date",today]},
{"$lt":["$garden.end_date",today]}
]
}
}},
{"$project":{"garden":0}}
])
Using $lookup with pipeline
db.garden.aggregate([
{"$lookup":{
"from":"allocation",
"let":{"garden_id":"$_id"},
"pipeline":[
{"$match":{"$expr":{"$eq":["$$garden_id","$garden"]},"start_date":{"$gte":today},"end_date":{"$lt":today}}}
],
"as":"garden"
}},
{"$addFields":{
"occupied":{"$gt":[{"$size":"$garden"},0]}
}},
{"$project":{"garden":0}}
])

Mongoose : how to set a field of a model with result from an agregation

Here is my sample :
Two simple Mongoose models:
a Note model, with among other fields an id field that is a ref for the Notebook model.
a Notebook model, with the id I mentioned above.
My goal is to output something like that:
[
{
"notes_count": 7,
"title": "first notebook",
"id": "5585a9ffc9506e64192858c1"
},
{
"notes_count": 3,
"title": "second notebook",
"id": "558ab637cab9a2b01dae9a97"
}
]
Using aggregation and population on the Note model like this :
Note.aggregate(
[{
"$group": {
"_id": "$notebook",
"notes_count": {
"$sum": 1
}
}
}, {
"$project": {
"notebook": "$_id",
"notes_count": "$notes_count",
}
}]
gives me this kind of result :
{
"_id": "5585a9ffc9506e64192858c1",
"notes_count": 7,
"notebook": {
"_id": "5585a9ffc9506e64192858c1",
"title": "un carnet court",
"__v": 0
}
}
Forget about __v and _id fields, would be easy to handle with a modified toJSON function.
But in this function neither doc nor ret params gives me access to the computed notes_count value.
Obviously, I could manage this in the route handler (parse result and recreate the datas that will be returned) but, is there a proper way to do that with mongoose ?
You can't use the aggregate method to update. As you have noted, you'll need to use output from the aggregate constructor to update the relevant documents.
As the Mongoose aggregate method will return a collection of plain objects, you can iterate through this and utilise the _id field (or similar) to update the documents.

Linking nested documents together and facetting in ElasticSearch

I have a mapping which looks like this:
"mappings": {
"mydoc": {
"properties": {
"event": {
"type": "nested",
"properties": {
"eventType": {
"type": "string"
},
"idList": {
"type": "integer"
},
"id": {
"type": "integer"
},
}
}
}
}
}
A mydoc document contains a nested array of event documents.
Within a mydoc document, I want to find all IDs where:
There exists an event with event.type='A' and event.idList contains some ID X
There exists another event with event.type='B' and event.id equals X
Across the index, I want a list of IDs where this criteria holds and also a count (for each ID) of the number of mydoc documents this occurred in.
Is it possible to achieve this in ElasticSearch? I was thinking it might be possible with a nested facet filter or a terms filter lookup but I have not seen a way to do it with these yet.
I think that a parent-child relation might suit your case better then a nested document.
Then you can query you (child) events document directly if you're searching only in the scope of the events (or add a condition on the _parent field to limit to a specific top document).
And you can use the has_child filter or query to search (or facet) on your top documents with conditions on the events (see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-has-child-filter.html )

Resources