Does mongoose have a findAndUpdate function? - reactjs

I am aware that mongoose has findOneAndUpdate that can only filter one value.
How can I filter two values?
await ReceiptDetails.findAndUpdate(
{patientIDnumber:patientIDnumber,
appNum:appNum
},
{dateIssued:date,
addedItem: addedItemValue,
paymentType:paymentType,
totalAmount:totalAmount,
officialReceiptNum:officialReceiptNum,
addedProcedurePrice:addedProcedurePrice,
amountPaid:amountPaid,
}
)
These are the only option that I have.

use updateMany() for updating multiple records, api doc says response of this function has fields such as matchedCount, modifiedCount, upsertedId etc, see below:
const res = await Person.updateMany({ name: /Stark$/ }, { isDeleted: true });
res.matchedCount; // Number of documents matched
res.modifiedCount; // Number of documents modified
res.acknowledged; // Boolean indicating everything went smoothly.
res.upsertedId; // null or an id containing a document that had to be upserted.
res.upsertedCount; // Number indicating how many documents had to be upserted. Will either be 0 or 1.

Related

Write data in to nested object in firebase firestore

I have a data structure that looks as follows:
This is the top level of the collection:
I want to write to increment the field count but I can't do it. I've tried so many different methods that I'd rather not go through all of them. The closest I've gotten was through:
const pageRef = admin.firestore().collection("pages").doc(image.page);
pageRef.set(
{
[`images.${image.position}.count`]: admin.firestore.FieldValue.increment(
1
),
},
{ merge: true }
);
But that leaves me with:
Please help. Changing the structure of pages is an option.
This is what I've tried to replicate:
Update fields in nested objects in firestore documents?
The issue is on how the point notaition is being used.
In the Post you shared the example they use is:
var setAda = dbFirestore.collection('users').doc('alovelace').update({
"first.test": "12345"
});
Applying this to your Code and model would be:
const pageRef = admin.firestore().collection("pages").doc(image.page);
pageRef.set(
{
`images[${image.position}].count`: admin.firestore.FieldValue.increment(
1
),
},
{ merge: true }
);
This will affect the element in the Array Images, the element image.position its value count.

Compare two big arrays value for value in Node.js

I have two arrays, one containing 200.000 product objects coming from a CSV file and one containing 200.000 product objects coming from a database.
Both arrays contains objects with the same fields, with one exception: the database objects have a unique ID as well.
I need to compare all 200.000 CSV objects with the 200.000 database objects. If the CSV object already exists in the database objects array I put it in an "update" array together with the ID from the match, and if it doesn't, then I put it in a "new" array.
When done, I update all the "update" objects in the database, and insert all the "new" ones. This goes fast (few seconds).
The compare step however takes hours. I need to compare three values: the channel (string), date (date) and time (string). If all three are the same, it's a match. If one of those isn't, then it's not a match.
This is the code I have:
const newProducts = [];
const updateProducts = [];
csvProducts.forEach((csvProduct) => {
// check if there is a match
const match = dbProducts.find((dbProduct) => {
return dbProduct.channel === csvProduct.channel && moment(dbProduct.date).isSame(moment(csvProduct.date), 'day') && dbProduct.start_time === csvProduct.start_time;
});
if (match) {
// we found a match, add it to updateProducts array
updateProducts.push({
id: match.id,
...csvProduct
});
// remove the match from the dbProducts array to speed things up
_.pull(dbProducts, match);
} else {
// no match, it's a new product
newProducts.push(csvProduct);
}
});
I am using lodash and moment.js libraries.
The bottleneck is in the check if there is a match, any ideas on how to speed this up?
This is a job for the Map collection class. Arrays are a hassle because they must be searched linearly. Maps (and Sets) can be searched fast. You want to do your matching in RAM rather than hitting your db for every single object in your incoming file.
So, first read every record in your database and construct a Map where the keys are objects like this {start_time, date, channel} and the values are id. (I put the time first because I guess it's the attribute with the most different values. It's an attempt to make lookup faster.)
Something like this pseudocode.
const productsInDb = new Map()
for (const entry in database) {
const key = { // make your keys EXACTLY the same when you load your Map ..
start_time: entry.start_time,
date: moment(entry.date),
entry.channel}
productsInDb.add(key, entry.id)
}
This will take a whole mess of RAM, but so what? It's what RAM is for.
Then do your matching more or less the way you did it in your example, but using your Map.
const newProducts = [];
const updateProducts = [];
csvProducts.forEach((csvProduct) => {
// check if there is a match
const key = { // ...and when you look up entries in the Map.
start_time: entry.start_time,
date: moment(entry.date),
entry.channel}
const id = productsInDb.get(key)
if (id) {
// we found a match, add it to updateProducts array
updateProducts.push({
id: match.id,
...csvProduct
});
// don't bother to update your Map here
// unless you need to do something about dups in your csv file
} else {
// no match, it's a new product
newProducts.push(csvProduct)
}
});

Get only dataValues from sequelize raw query instead of Model instance

I'm using sequelize and using raw query to get the datas from table. But I'm getting all of the model instances while I only need the dataValues.
My setup looks like this:
const sequelize = new Sequelize({
database: process.env.PGDATABASE,
username: process.env.PGUSER,
password: process.env.PGPASS,
host: process.env.PGHOST,
port: process.env.PGPORT,
dialect: "postgres"
});
getPostGres: () => {
return sequelize;
}
and the way I'm querying the database looks like this:
let messageRatingsArr = await getPostGres().query(
`SELECT mr.support_email, mr.support_name,
(select count(mrn."chatId") as total FROM message_ratings as mrn WHERE mrn."ratingType"='NEGATIVE' and mr.support_email = mrn.support_email) as negative,
(select count(mrp."chatId") as total FROM message_ratings as mrp WHERE mrp."ratingType"='POSITIVE' and mr.support_email = mrp.support_email) as positive,
(select count(mrm."chatId") as total FROM message_ratings as mrm WHERE mrm."ratingType"='MIXED' and mr.support_email = mrm.support_email) as mixed,
(select count(mru."chatId") as total FROM message_ratings as mru WHERE mru."ratingType"='NEUTRAL' and mr.support_email = mru.support_email) as neutral
FROM message_ratings mr
WHERE mr."createdAt" >= '${properFromDate}' AND mr."createdAt" <= '${properToDate}'
group by mr.support_email, mr.support_name
limit ${args.count} offset ${args.offset} `,
{
model: MessageRatingPG,
mapToModel: true
}
);
let messageRatings = messageRatingsArr.map(item=>{
return item.dataValues;
})
let result = connectionFromArray(messageRatings, args);
result.totalCount = messageRatings.length;
return result;
As you can see, since I'm mapping the data from the query which has all kinds of stuff like dataValues, _options, isNewRecord etc., looping through the array if I have a large data set isn't efficient, so what can I do to only get the dataValues?
From https://sequelize.org/master/manual/raw-queries.html:
In cases where you don't need to access the metadata you can pass in a query type to tell sequelize how to format the results. For example, for a simple select query you could do:
sequelize.query("SELECT * FROM `users`", { type: sequelize.QueryTypes.SELECT})
.then(users => {
// We don't need spread here, since only the results will be returned for select queries
})
Now, looking at your code, and comparing with the next paragraph in the docs:
A second option is the model. If you pass a model the returned data will be instances of that model.
// Callee is the model definition. This allows you to easily map a query to a predefined model
sequelize
.query('SELECT * FROM projects', {
model: Projects,
mapToModel: true // pass true here if you have any mapped fields
})
.then(projects => {
// Each record will now be an instance of Project
})
I'd suggest removing from your original code, the following:
{
model: MessageRatingPG,
mapToModel: true
}
and replacing it with { type: sequelize.QueryTypes.SELECT }
You have to add to your query the attribute raw. From the docs
Sometimes you might be expecting a massive dataset that you just want
to display, without manipulation. For each row you select, Sequelize
creates an instance with functions for update, delete, get
associations etc. If you have thousands of rows, this might take some
time. If you only need the raw data and don't want to update anything,
you can do like this to get the raw data.
Project.findAll({ where: { ... }, raw: true })

Sequelize doesn't return anything if value for $notIn is an empty array but it returns me if array contains an empty value

I want Sequelize to return to me all the values which IDs are not in an array.
Sequelize doesn't return anything if value for $notIn is an empty array but it returns me if array contains an empty value.
This returns me nothing:
db.foo.findAll({
where: {
id: {
$notIn: []
}
}
});
This returns me every value:
db.foo.findAll({
where: {
id: {
$notIn: ['']
}
}
});
How come it doesn't return all values if the array is empty? If it is empty, then it means all values which values are not in that array should be returned. Since the ID doesn't contain any value, sequelize should return to me all the values, right?
This issue will be fixed in Sequelize version 4. That version has not been fully disclosed yet, though. At least there is no documentation yet.
See the GitHub issue at https://github.com/sequelize/sequelize/issues/4859.
The changelog can be found at https://github.com/sequelize/sequelize/blob/master/changelog.md. It says
[FIXED] $notIn: [] is now converted to NOT IN (NULL) #4859
Maybe you can implement those version 4 changes in your current code yourself.
For those using < 4 version, here is a work around I used,
let idsToSkip = [...]; //generate array of ids, this may result in empty array
idsToSkip.push('-1'); // push a value that can not be the id in table. this will generate `not in ('-1')` query serving the purpose.
db.foo.findAll({
where: {
id: {
$notIn: idsToSkip
}
}
});
Hope it helps !!

In Firebase, is there a way to get the number of children of a node without loading all the node data?

You can get the child count via
firebase_node.once('value', function(snapshot) { alert('Count: ' + snapshot.numChildren()); });
But I believe this fetches the entire sub-tree of that node from the server. For huge lists, that seems RAM and latency intensive. Is there a way of getting the count (and/or a list of child names) without fetching the whole thing?
The code snippet you gave does indeed load the entire set of data and then counts it client-side, which can be very slow for large amounts of data.
Firebase doesn't currently have a way to count children without loading data, but we do plan to add it.
For now, one solution would be to maintain a counter of the number of children and update it every time you add a new child. You could use a transaction to count items, like in this code tracking upvodes:
var upvotesRef = new Firebase('https://docs-examples.firebaseio.com/android/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes');
upvotesRef.transaction(function (current_value) {
return (current_value || 0) + 1;
});
For more info, see https://www.firebase.com/docs/transactions.html
UPDATE:
Firebase recently released Cloud Functions. With Cloud Functions, you don't need to create your own Server. You can simply write JavaScript functions and upload it to Firebase. Firebase will be responsible for triggering functions whenever an event occurs.
If you want to count upvotes for example, you should create a structure similar to this one:
{
"posts" : {
"-JRHTHaIs-jNPLXOQivY" : {
"upvotes_count":5,
"upvotes" : {
"userX" : true,
"userY" : true,
"userZ" : true,
...
}
}
}
}
And then write a javascript function to increase the upvotes_count when there is a new write to the upvotes node.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.countlikes = functions.database.ref('/posts/$postid/upvotes').onWrite(event => {
return event.data.ref.parent.child('upvotes_count').set(event.data.numChildren());
});
You can read the Documentation to know how to Get Started with Cloud Functions.
Also, another example of counting posts is here:
https://github.com/firebase/functions-samples/blob/master/child-count/functions/index.js
Update January 2018
The firebase docs have changed so instead of event we now have change and context.
The given example throws an error complaining that event.data is undefined. This pattern seems to work better:
exports.countPrescriptions = functions.database.ref(`/prescriptions`).onWrite((change, context) => {
const data = change.after.val();
const count = Object.keys(data).length;
return change.after.ref.child('_count').set(count);
});
```
This is a little late in the game as several others have already answered nicely, but I'll share how I might implement it.
This hinges on the fact that the Firebase REST API offers a shallow=true parameter.
Assume you have a post object and each one can have a number of comments:
{
"posts": {
"$postKey": {
"comments": {
...
}
}
}
}
You obviously don't want to fetch all of the comments, just the number of comments.
Assuming you have the key for a post, you can send a GET request to
https://yourapp.firebaseio.com/posts/[the post key]/comments?shallow=true.
This will return an object of key-value pairs, where each key is the key of a comment and its value is true:
{
"comment1key": true,
"comment2key": true,
...,
"comment9999key": true
}
The size of this response is much smaller than requesting the equivalent data, and now you can calculate the number of keys in the response to find your value (e.g. commentCount = Object.keys(result).length).
This may not completely solve your problem, as you are still calculating the number of keys returned, and you can't necessarily subscribe to the value as it changes, but it does greatly reduce the size of the returned data without requiring any changes to your schema.
Save the count as you go - and use validation to enforce it. I hacked this together - for keeping a count of unique votes and counts which keeps coming up!. But this time I have tested my suggestion! (notwithstanding cut/paste errors!).
The 'trick' here is to use the node priority to as the vote count...
The data is:
vote/$issueBeingVotedOn/user/$uniqueIdOfVoter = thisVotesCount, priority=thisVotesCount
vote/$issueBeingVotedOn/count = 'user/'+$idOfLastVoter, priority=CountofLastVote
,"vote": {
".read" : true
,".write" : true
,"$issue" : {
"user" : {
"$user" : {
".validate" : "!data.exists() &&
newData.val()==data.parent().parent().child('count').getPriority()+1 &&
newData.val()==newData.GetPriority()"
user can only vote once && count must be one higher than current count && data value must be same as priority.
}
}
,"count" : {
".validate" : "data.parent().child(newData.val()).val()==newData.getPriority() &&
newData.getPriority()==data.getPriority()+1 "
}
count (last voter really) - vote must exist and its count equal newcount, && newcount (priority) can only go up by one.
}
}
Test script to add 10 votes by different users (for this example, id's faked, should user auth.uid in production). Count down by (i--) 10 to see validation fail.
<script src='https://cdn.firebase.com/v0/firebase.js'></script>
<script>
window.fb = new Firebase('https:...vote/iss1/');
window.fb.child('count').once('value', function (dss) {
votes = dss.getPriority();
for (var i=1;i<10;i++) vote(dss,i+votes);
} );
function vote(dss,count)
{
var user='user/zz' + count; // replace with auth.id or whatever
window.fb.child(user).setWithPriority(count,count);
window.fb.child('count').setWithPriority(user,count);
}
</script>
The 'risk' here is that a vote is cast, but the count not updated (haking or script failure). This is why the votes have a unique 'priority' - the script should really start by ensuring that there is no vote with priority higher than the current count, if there is it should complete that transaction before doing its own - get your clients to clean up for you :)
The count needs to be initialised with a priority before you start - forge doesn't let you do this, so a stub script is needed (before the validation is active!).
write a cloud function to and update the node count.
// below function to get the given node count.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.userscount = functions.database.ref('/users/')
.onWrite(event => {
console.log('users number : ', event.data.numChildren());
return event.data.ref.parent.child('count/users').set(event.data.numChildren());
});
Refer :https://firebase.google.com/docs/functions/database-events
root--|
|-users ( this node contains all users list)
|
|-count
|-userscount :
(this node added dynamically by cloud function with the user count)

Resources