No index exists for this sort - couchDB - couchdb

Getting error when accesing the data by using sort method
Error is
No index exists for this sort
Create index code is
db.createIndex({
index: { fields: ["timestamp", "upVote"] },
}).then(() => {
this.intitialDatafromDB(db);
});
find function is
db.find({
selector: { upVote: { $gt: 5 } },
sort: [{ upVote: "desc" }],
fields: ["news"],
}).then((result: any) => {
this.data = result.docs;
}).catch((err: any) => {
console.log(err);
});

The reason your query is failing is that the order of index fields matters.
From the pouchdb documentation Indexing on more than one field
One thing to note is that the order of these fields matters when
creating your index
By specifying the index as so
fields: ['timestamp','upVote']
The index looks like this
timestamp
upVote
1590399369500
3
1590399369600
4
1590399369700
1
1590399369700
2
1590399369700
3
1590399369800
1
Note the timestamp 1590399369700, and how the secondary field upVote sorts.
If your index fields were ordered like so
fields: ['upVote','timestamp']
Given the theoretical data above, the index would look like this
upVote
timestamp
1
1590399369700
1
1590399369800
2
1590399369700
3
1590399369500
3
1590399369700
4
1590399369600
and your query would return the results you expect, as is demonstrated in the snippet below. I recommend reading over Map/reduce queries; grasping the concepts presented in that documentation will provide a deeper understanding of why this is so.
const g_result = 'result';
const getEl = id => document.getElementById(id);
let db;
async function view() {
const view = getEl(g_result);
const result = await db.find({
selector: {
upVote: {
$gt: 5
},
},
sort: [{
'upVote': 'desc'
}],
fields: ['news','upVote']
}, );
view.innerText = JSON.stringify(result, undefined, 3);
}
// canned test documents
function getDocsToInstall() {
return [{
timestamp: 1590399369508,
upVote: 3,
news: "new item 1"
},
{
timestamp: 1590399248600,
upVote: 4,
news: "new item 2"
},
{
timestamp: 1590399248600,
upVote: 5,
news: "new item 3"
},
{
timestamp: 1590399248700,
upVote: 6,
news: "new item 4"
},
{
timestamp: 1590399248900,
upVote: 7,
news: "new item 5"
},
{
timestamp: 1590399249000,
upVote: 8,
news: "new item 6"
},
]
}
// init example db instance
async function initDb() {
db = new PouchDB('test', {
adapter: 'memory'
});
await db.bulkDocs(getDocsToInstall());
await db.createIndex({
index: {
fields: ['upVote', 'timestamp']
}
});
};
(async() => {
await initDb();
await view();
})();
https: //stackoverflow.com/questions/69122670/no-index-exists-for-this-sort-couchdb#
<script src="https://github.com/pouchdb/pouchdb/releases/download/7.1.1/pouchdb-7.1.1.min.js"></script>
<script src="https://github.com/pouchdb/pouchdb/releases/download/7.1.1/pouchdb.memory.min.js"></script>
<script src="https://github.com/pouchdb/pouchdb/releases/download/7.1.1/pouchdb.find.min.js"></script>
<pre id='view'></pre>
<div style='margin-top:2em'></div>
<pre id='result'>
</pre>

Related

count nested array inside documen using updateOne() function

const reset_qr_list_and_update_count = await stock_read_log.updateOne(
{
payload: {$ne:req.body.payload},
"qr_list.payload": req.body.new_qr_list[indexx].payload,
company_id:req.body.company_id
},
{
"$pull": {
"qr_list": {
payload: req.body.new_qr_list[indexx].payload
}
},
$set:{
qty: xx
},
}
);
$set:{
qty: model.aggreation({
//the query
}).count()
},
after pulling one of the list above,i want to re-count list left ,how can i achieve that within this function?

Conditionally update item or delete item in one query in mongoose

So, what I'm trying to achieve here is two things. First one is to decrement the quantity of a product. Second is that if that product quantity is 0 then delete that product.
So I'm trying to set a condition that if product quantity is 0 then delete that product otherwise simply decrement the quantity.
Here is my cart document:
And here is my function:
const cart = await Cart.findOneAndUpdate(
{ userID: req.body.userID, "items.productID": req.body.productID },
{$lt: ['items.productID', 0] ? { $pull: { items: { productID: req.body.productID } } } : { $inc: { 'items.$.quantity': -1 } }},
{ new: true }
);
I have tried to achieve that with ternary operator but it didn't worked. Can anybody help me with this please? Thank you.
You cannot use a ternary operator in your query. Not only is this not supported in MongoDB, it will also simply be evaluated by node.js, not by MongoDB itself. In fact, conditional update operations of the nature you're describing are themselves not something that MongoDB is capable of handling.
Your best bet is to perform two queries, where the first one performs the decrement operation and the second pulls any items out that have a quantity less than or equal to 0:
const cartAfterDecrement = await Cart.findOneAndUpdate(
{ userID: req.body.userID, "items.productID": req.body.productID },
{ $inc: { 'items.$.quantity': -1 } },
{ new: true }
);
const cartAfterPull = await Cart.findOneAndUpdate(
{ userID: req.body.userID, items: { $elemMatch: {
productID: req.body.productID,
quantity: { $lte: 0 }
} } },
{ $pull: {items: { quantity: { $lte: 0 } } } },
{ new: true }
);
const cart = cartAfterPull ? cartAfterPull : cartAfterDecrement;
If you wish to allow products with quantities equal to 0, then modifying the second query is a trivial matter.
The only downside to this approach is that there will be a natural, very slight delay between these two updates, which may result in a very rare case of a race condition occurring where a cart is returned and contains an item with a quantity less than or equal to 0. In order to avoid this, you will need to either filter these values out with server code or by retrieving your cart(s) using aggregation, or you will need to make use of transactions to ensure that the two operations occur in sequence without the document being retrieved between the two write operations.
With that said, constructing the appropriate array filtering logic or transaction management is beyond the scope of this question, so will not be included in this answer.
Query
you can do it with a pipeline update ( >= MongoDB 4.2)
find the user that has that productID
reduce the items, to an array with item but
if productID not the one i look for, keep it as it is
else if quantity=0 remove it(don't add it to the reduced array)
else keep it with quantity-1
*findAndModify accepts pipeline also, for nodejs driver you must find the method that wraps this command, or run the command directly
Test code here
update(
{"userID": req.body.userID,
"items.productID": req.body.productID},
[{"$set":
{"items":
{"$reduce":
{"input":"$items",
"initialValue":[],
"in":
{"$switch":
{"branches":
[{"case":{"$ne":["$$this.productID", req.body.productID]},
"then":{"$concatArrays":["$$value", ["$$this"]]}},
{"case":{"$gt":["$$this.quantity", 0]},
"then":
{"$concatArrays":
["$$value",
[{"$mergeObjects":
["$$this", {"$subtract":["$this.quantity", 1]}]}]]}}],
"default":"$$value"}}}}}}])
Thanks #B. Fleming and all others for your answer. I have just use a simple code implementation for that however it wasn't what I was expected but still it's good to go.
module.exports.deleteCartItem = async (req, res, next) => {
var condition;
var update;
const product = await Cart.findOne({ userID: req.body.userID,
items: {
$elemMatch: {
productID: req.body.productID,
quantity: { $lte: 0 }
}
}
});
// IF PRODUCT WITH 0 QUANTITY PULL/DELETE THAT PRODUCT
if (product !== null) {
condition = { userID: req.body.userID, items: { $elemMatch: { productID: req.body.productID, quantity: { $lte: 0 } } } },
update = { $pull: {items: { quantity: { $lte: 0 } } } }
}
// OTHERWISE DECREMENT THE QUANTITY FOR THAT PRODUCT
else {
condition = { userID: req.body.userID, "items.productID": req.body.productID }
update = { $inc: { 'items.$.quantity': -1 } }
}
try {
const cart = await Cart.findOneAndUpdate(condition, update, { new: true });
if (cart !== null) {
res.status(200).json({
statusCode: 1,
message: "Item Removed",
responseData: cart
});
} else {
res.json({
statusCode: 0,
msgCode: 462,
message: 'Not found',
responseData: null
});
}
} catch (err) {
console.log('Server Error: ', err);
next(new Error('Server Error, Something was wrong!'));
}
}

How can I update a document's nested array of object item by 1?

I'm trying to update a mongoose document with the help of findOneAndUpdate but I'm unable to do so. The document looks like this in the database:
{
'docId': 1001,
'totalViews': 3,
'docInfo': [
{
id: 1,
views: 2
},
{
id: 2,
views: 1
}
]
}
I'm trying to update totalViews by 1 which will make the total count to be 4. And I also need to update the second object's views property by 1 in imageInfo array. Which will have a views count of 2.
I tried doing this by first fetching the whole document with the help of:
const doc = await Doc.find({ docId: 1001 });
Then found the index of the docInfo array item which needs to be updated. Which is the object with id 2.
const docIndex = doc[0].docInfo.findIndex( item => {
return item.id === 2;
});
Then used findOneAndUpdate to update the items:
await Doc.findOneAndUpdate(
{ docId: 1001, "docInfo.id": 2 },
{
$set: {
[ `docInfo.${2}.views` ]: 1++,
'totalViews': 1++
}
}, { new: true }
);
With this I'm getting this error:
SyntaxError: Invalid left-hand side expression in postfix operation
What am I doing wrong here?
What you are doing is invalid, you can use $inc operator to increment a number, and don't need to find a query as well,
await Doc.findOneAndUpdate(
{ docId: 1001, "docInfo.id": 2 },
{
$inc: {
'docInfo.$.views': 1,
'totalViews': 1
}
},
{ new: true }
);
Playground

MongoDb TypeError: Cannot read property 'insertMany' of undefined

import { MongoClient } from 'mongodb';
import assert from 'assert';
import config from './config';
console.log('Inside File');
MongoClient.connect(config.mongodbUri, (err, db) => {
assert.equal(null, err);
db.products.insertMany( [
{ item: "card", qty: 15 },
{ item: "envelope", qty: 20 },
{ item: "stamps" , qty: 30 }
] );
db.close();
});
For the above code i get the error Cannot read property insertMany of undefined.
But when i just enter the below
db.products.insertMany( [
{ item: "card", qty: 15 },
{ item: "envelope", qty: 20 },
{ item: "stamps" , qty: 30 }
] );
in the console the products seems to get inserted in the db just fine.
Can someone please let me know the cause of this issue?
Can try using db.collection('products').insertMany and callback function
MongoClient.connect(config.mongodbUri, (err, db) => {
assert.equal(null, err);
db.collection('products').insertMany( [
{ item: "card", qty: 15 },
{ item: "envelope", qty: 20 },
{ item: "stamps" , qty: 30 }
] , function(error, doc) {
if(error) {
console.log(error);
} else {
console.log('success');
}
db.close();
});
});
insertMany() is a mongoDB function, it will work if you try it in the console,
but in nodeJs you're dealing with Mongoose ( the mongoDb driver ) which doesn't have this function, to store items with mongoose, you need to do products.save() and it doesn't support bulk inserts ( you can only insert one at a time ) so you have to go through a loop :
var items = [{ item: "card", qty: 15 },
{ item: "envelope", qty: 20 },
{ item: "stamps" , qty: 30 }];
var Products = mongoose.model('Products'); // don't know what you have as a model
for ( var i in items) {
Products.save(items[i]);
}
of course you will need to handle errors ..etc.
if you don't already have a model here's a snippet:
var Schema = mongoose.Schema;
var products = new Schema({
item : String
, qty : int
});
mongoose.model("Products", Products);
Basically the function you're trying to use insertMany() is MongoDB function which can be directly used in the mongo console without any issues. But in case if you want to use it through your application that means using MongoClient library, you can do that too..
What you need to do is very simple, just use it the with the collection name you wanted to insert and passing the mandatory callback fn() as the other argument.
Syntax:
db.collection(<collectionName>).insertMany([{arglist1},{arglist2},
{arglistn..}],Callbackfn())
I Hope it'll be okay.

Updating Reference Along With Other Values Mongoose

Here is a schema that I am working on.
var testSchema = mongoose.Schema({
userCreated : {
type : mongoose.Schema.Types.ObjectId,
ref : "User"
},
points : {type: Number, default: 0},
numVotes : {type: Number, default: 0},
createdAt : Date,
updatedAt : Date,
}, { timestamps : true });
Now, I am trying to write a function that will increment two fields on this document (points and numVotes, as well as an additional points field that exists on the user schema.
Here is my attempt.
testSchema.statics.incrementTest = function(id, ...) {
this.findByIdAndUpdate(id, {$inc : {
points : 5,
numVotes : 1,
'userCreated.points' : 5
}}).exec();
}
Now, this code that I have written does not work. However, when I comment out the 'userCreated.points' : 5 line, the other two fields do increment as expected. My question is, what is the best way using mongoose to update the fields on a document and the fields on a subdocument at the same time?
The data here is contained in different collections, so no single update statement is able to increment counters in both at the same time.
In order to get a consistent view you are going to need to "chain" your update statements and use the return results of each to build the response.
Depending on your needs you can either use a Promise with this:
testSchema.statics.incrementTest = function(id) {
var self = this;
return new Promise(function(resolve,reject) {
self.findByIdAndUpdate(
id,
{
"$inc": {
"points": 5,
"numVotes": 1
}
},
{ "new": true }
).then(function(test) {
var userModel = test.schema.path("userCreated").options.ref;
mongoose.model(userModel).findByIdAndUpdate(
test.userCreated,
{ "$inc": { "points": 5 } },
{ "new": true }
).then(function(user) {
test.userCreated = user;
resolve(test);
})
}).catch(reject)
})
};
Which you can then invoke on your model:
Test.incrementTest("56fe279d363ce91765d9e39e").then(function(test) {
console.log(JSON.stringify(test,undefined,2));
}).catch(function(err) {
throw err;
})
Or you can use async.waterfall from the async library if that suits you better:
testSchema.statics.incrementTest = function(id,callback) {
var self = this;
async.waterfall(
[
function(callback) {
self.findByIdAndUpdate(
id,
{
"$inc": {
"points": 5,
"numVotes": 1
}
},
{ "new": true },
callback
)
},
function(err,test) {
if (err) callback(err);
var userModel = test.schema.path("userCreated").options.ref;
mongoose.model(userModel).findByIdAndUpdate(
test.userCreated,
{ "$inc": { "points": 5 } },
{ "new": true },
function(err,user) {
if ( typeof(user) !== "undefined" )
test.userCreated = user;
callback(err,test);
}
);
}
],
callback
);
};
Which has a similar usage:
Test.incrementTest("56fe279d363ce91765d9e39e",function(err,test) {
if (err) throw err;
console.log(JSON.stringify(test,undefined,2));
})
Both should be giving you a result back that shows the incremented data in both objects for both collections:
{ points: 5,
numVotes: 1,
__v: 0,
userCreated: { points: 5, __v: 0, _id: 56ff1aa6dba6d13e798fc894 },
createdAt: Sat Apr 02 2016 12:04:38 GMT+1100 (AEDT),
updatedAt: Sat Apr 02 2016 12:04:38 GMT+1100 (AEDT),
_id: 56fe279d363ce91765d9e39e }

Resources