Parse - Query users object contained in an array - javascript

I'm running a Parse Query on my User class.
I want to retrieve users that are contained in an array of strings (ID).
const usersQuery = new Parse.Query(User).containedIn('objectId', customersArrayId).find({ useMasterKey: true });
Which works, actually. I'm getting a [ParseUser { _objCount: 6, className: '_User', id: 'iYIJ7Zrmms' }], because only 1 user matches.
But well, my issue is that I'm only getting ParseUser { _objCount: 6, className: '_User', id: 'iYIJ7Zrmms' }. This class contains other fields (firstname, lastname, e.g.) that are not returned.
When I performed the same thing, looping on my customersArrayId and performing .get():
const customerId = customersArrayId[index];
promises.push(new Parse.Query(User).get(customerId).then((user) => {
return user.toJSON();
}, (error) => console.error(error)));
I'm getting the full object, as expected. But it doesn't seem to be the right way of querying Parse objects from an array of ids.
I can't find anything in the docs about it, any idea why containedIn only returns a part of the queried objects?

I actually get the difference:
new Parse.Query(User).get(customerId)
=> returns the Parse Object
new Parse.Query(User).containedIn('objectId', customersArrayId)
=> returns the Parse User, subclass of a Parse Object.
And well, then, this thread was useful: Get user from parse in Javascript
I ended up using :
usersQuery.then(customersResponse => {
const customers = [];
for (let index = 0; index < customersResponse.length; index++) {
const customer = {
...customersResponse[index].toJSON(),
...customersResponse[index].attributes
};
...
Still not sure that the best answer.
EDIT
router.get('/:userId/shop/customers/details', checkUserMatch, (req, res, next) => {
if('shops' in req.jwtData.data) {
const shopId = req.jwtData.data.shops[0];
const query = new Parse.Query('OtherStuff');
const Shop = Parse.Object.extend('Shops');
query.equalTo('shop', new Shop({id: shopId})).find().then((otherStuffs) => {
const customersArrayId = otherStuffs.map(otherStuff => otherStuff.toJSON().user.objectId);
const usersQuery = new Parse.Query('_User').containedIn('objectId', customersArrayId).find({ useMasterKey: true });
usersQuery.then(customersResponse => {
const customers = [];
for (let index = 0; index < customersResponse.length; index++) {
let customer = {
...customersResponse[index].toJSON(),
...customersResponse[index].attributes
};
const customerId = customer.objectId;
const stuffQuery = new Parse.Query('Stuff').equalTo('user', new UserModel({objectId: customerId})).find().then((stuff) => {
return stuff;
});
const otherStuffQuery = new Parse.Query('otherStuff').equalTo('user', new UserModel({objectId: customerId})).find().then((otherStuff) => {
return otherStuff;
});
Promise.all([stuffQuery, otherStuffQuery]).then((data) => {
const stuff = data[0];
const otherStuff = data[1];
customer = {
...customer,
stuff,
otherStuff,
}
customers.push(customer);
if(index === customersResponse.length - 1) { // last customer
res.json({
success: true,
data: customers
});
}
})
}
});
});
}
});

Related

Algolia - get mass records & delete with filter

I am using Algolia for search purposes and we got a huge pile of records. We want to delete some records and have decided to delete records that are older than X date.
First I was using this
const records = [];
const deleteRecordsBeforeDateAlgolia = (date) => {
let client;
*****
const index = client.initIndex('function_message');
//get the records before the given date
try {
index.search('',{
filters: `time_stamp < ${date}`
}).then(({hits}) => {
if(hits.length > 0) {
for (const hit of hits) {
index.deleteObject(hit.objectID);
records.push(hit.objectID);
}
}
if(hits.length === 0) {
console.log(`Deleted ${records.length} records`);
} else {
deleteRecordsBeforeDateAlgolia(date);
}
});
} catch (err) {
console.error(err);
}
};
but I realized this isnt that optimized + will be very slow when deleting on prod. Can you tell me how I can get huge amounts of data with a filter (timestamp in this case) and then delete all of them?
EDIT
const records = [];
const deleteRecordsBeforeDateAlgolia = (date) => {
let client;
//creds stuff
const index = client.initIndex('function_message');
//get the records before the given date
try {
const search = index.browseObjects({
filters: `time_stamp < ${date}`
}).then(res => {
//IT SHOWS RESPONSE IS UNDEFINED
res.forEach(record => {
records.push(record);
});
console.log(`found ${records.length} records`);
});
} catch (err) {
console.error(err);
}
};
browseObjects takes a batch callback function that's called on every batch of hits where you can specify what to do with the batch. The optional parameter list can be found here
Something like this should work
const records = [];
const deleteFromIndex = (idArray,index) => {
index.deleteObjects(idArray).then(({ objectIDs }) => {
console.log(objectIDs);
});
}
const deleteRecordsBeforeDateAlgolia = (date) => {
let client;
client = algoliasearch('algoliaApp', 'algoliaKey');
const index = client.initIndex('function_message');
try {
index.browseObjects({
filters: `time_stamp<${date}`,
query: '',
batch: (batch) => {
records.push(...batch); //push each batch into records array
}
})
.then(() => {
const idArray = records.map(({objectID}) => objectID) //get an array of objectIDs
deleteFromIndex(idArray, index)
});
} catch (err) {
console.error(err);
}
};
deleteRecordsBeforeDateAlgolia('some date')

Object data from Firebase is Undefined regardless of using Async and Await

I am trying to fetch data from different collections in my cloud Firestore database in advance before I process them and apply them to batch, I created two async functions, one to capture the data and another to execute certain code only after all data is collected, I didn't want the code executing and creating errors before the data is fetched when i try to access the matchesObject after the async function to collect data is finished, it keeps saying "it cannot access a property matchStatus of undefined", matchStatus is a field in my object, after a while, i see the data showing all the documents saved to matchObjects and the document i want is there, I logged the data being saved to the matches object, it retrieves all the data and I can confirm that the document I am looking for is fetched from firestore, it's like the code doesn't wait for the object to be finished before it runs, or something, i thought took care of that with async and await? could anyone shed some light as to why it is undefined one moment
axios.request(options).then(function(response) {
console.log('Total matches count :' + response.data.matches.length);
const data = response.data;
var matchesSnapshot;
var marketsSnapshot;
var tradesSnapshot;
var betsSnapshot;
matchesObject = {};
marketsObject = {};
tradesObject = {};
betsObject = {};
start();
async function checkDatabase() {
matchesSnapshot = await db.collection('matches').get();
matchesSnapshot.forEach(doc => {
matchesObject[doc.id] = doc.data();
console.log('matches object: ' + doc.id.toString())
});
marketsSnapshot = await db.collection('markets').get();
marketsSnapshot.forEach(doc2 => {
marketsObject[doc2.id] = doc2.data();
console.log('markets object: ' + doc2.id.toString())
});
tradesSnapshot = await db.collection('trades').get();
tradesSnapshot.forEach(doc3 => {
tradesObject[doc3.id] = doc3.data();
console.log('trades object: ' + doc3.id.toString())
});
betsSnapshot = await db.collection('bets').get();
betsSnapshot.forEach(doc4 => {
betsObject[doc4.id] = doc4.data();
console.log('bets object: ' + doc4.id.toString())
});
}
async function start() {
await checkDatabase();
// this is the part which is undefined, it keeps saying it cant access property matchStatus of undefined
console.log('here is matches object ' + matchesObject['302283']['matchStatus']);
if (Object.keys(matchesObject).length != 0) {
for (let bets of Object.keys(betsObject)) {
if (matchesObject[betsObject[bets]['tradeMatchId']]['matchStatus'] == 'IN_PLAY' && betsObject[bets]['matched'] == false) {
var sfRef = db.collection('users').doc(betsObject[bets]['user']);
batch11.set(sfRef, {
accountBalance: admin.firestore.FieldValue + parseFloat(betsObject[bets]['stake']),
}, {
merge: true
});
var sfRef = db.collection('bets').doc(bets);
batch12.set(sfRef, {
tradeCancelled: true,
}, {
merge: true
});
}
}
}
});
I think you missed on how firebase works. For fetch data use something like this
const getWithKey= async({
collection, value, orderBy = 'asc', where = 'id', operationString = '=='
}) => {
if (idControl(value)) {
const data = await db.collection(collection)
.where(where, operationString, value).get();
if (data?.docs[0]) {
return [data.docs[0].data()];
}
}
const documents = [];
await db
.collection(collection)
.orderBy(where, orderBy)
.get()
.then((item) => {
item.forEach((doc) => {
documents.push({ id: doc.id, ...doc.data() });
});
});
return documents || [];
}

Firebase Cloud Function error: Registration token(s) provided to sendToDevice() must be a non-empty string or a non-empty array

I want to send a notification to all users who are confirmed guests when the object confirmedGuests is created in the Firebase Realtime Database.
So, I first create an array of all the users from confirmedGuests object. Then, I iterate through all these users and push their deviceTokens to an array of deviceTokens. The array allDeviceTokens is expected to be the array of device tokens of all users in confirmedGuests.
However, when confirmedGuests object is created, the function returns an error.
Below is my cloud function
exports.sendNotification = functions.database
.ref('/feed/{pushId}/confirmedGuests')
.onCreate((snapshot, context) => {
const pushId = context.params.pushId;
if (!pushId) {
return console.log('missing mandatory params for sending push.')
}
let allDeviceTokens = []
let guestIds = []
const payload = {
notification: {
title: 'Your request has been confirmed!',
body: `Tap to open`
},
data: {
taskId: pushId,
notifType: 'OPEN_DETAILS', // To tell the app what kind of notification this is.
}
};
let confGuestsData = snapshot.val();
let confGuestItems = Object.keys(confGuestsData).map(function(key) {
return confGuestsData[key];
});
confGuestItems.map(guest => {
guestIds.push(guest.id)
})
for(let i=0; i<guestIds.length; i++){
let userId = guestIds[i]
admin.database().ref(`/users/${userId}/deviceTokens`).once('value', (tokenSnapshot) => {
let userData = tokenSnapshot.val();
let userItem = Object.keys(userData).map(function(key) {
return userData[key];
});
userItem.map(item => allDeviceTokens.push(item))
})
}
return admin.messaging().sendToDevice(allDeviceTokens, payload);
});
You're loading each user's device tokens from the realtime database with:
admin.database().ref(`/users/${userId}/deviceTokens`).once('value', (tokenSnapshot) => {
This load operation happens asynchronously. This means that by the time the admin.messaging().sendToDevice(allDeviceTokens, payload) calls runs, the tokens haven't been loaded yet.
To fix this you'll need to wait until all tokens have loaded, before calling sendToDevice(). The common approach for this is to use Promise.all()
let promises = [];
for(let i=0; i<guestIds.length; i++){
let userId = guestIds[i]
let promise = admin.database().ref(`/users/${userId}/deviceTokens`).once('value', (tokenSnapshot) => {
let userData = tokenSnapshot.val();
let userItem = Object.keys(userData).map(function(key) {
return userData[key];
});
userItem.map(item => allDeviceTokens.push(item))
return true;
})
promises.push(promise);
}
return Promise.all(promises).then(() => {
return admin.messaging().sendToDevice(allDeviceTokens, payload);
})

Removing Percent Encoding from Firebase Cloud Functions

The firebase function I'm currently using retrieves data from a certain branch in the database where the value may or may not have percent encoding. The value is a user's username and it's encoded if there's a '.' in the name. When the user gets a notification, it has their name in the body of it, and I'm trying to figure out how to removePercentEncoding if necessary. My cloud function:
exports.newPost = functions.database.ref('/{school}/posts').onWrite((change, context) => {
const school = context.params.school
const postUsername = admin.database().ref('/{school}/lastPost/lastPostUser').once('value')
var db = admin.database();
var val1, val2;
db.ref(`/Herrick Middle School/lastPost/lastPostUser`).once('value').then(snap => {
val1 = snap.val();
console.log(snap.val());
return val1
}).then(() => {
return db.ref("test2/val").once('value');
}).catch(err => {
console.log(err);
});
return loadUsers().then(users => {
let tokens = [];
for (let user of users) {
tokens.push(user.pushToken);
console.log(`pushToken: ${user.pushToken}`);
}
let payload = {
notification: {
title: school,
body: `${val1} just posted something.`,
sound: 'Apex',
badge: '1'
}
};
return admin.messaging().sendToDevice(tokens, payload);
});
});
function loadUsers() {
let dbRef = admin.database().ref('/Herrick Middle School/regisTokens');
let defer = new Promise((resolve, reject) => {
dbRef.once('value', (snap) => {
let data = snap.val();
let users = [];
for (var property in data) {
users.push(data[property]);
console.log(`data: ${property}`);
}
resolve(users);
}, (err) => {
reject(err);
});
});
return defer;
}
More specifically, I was hoping someone could shed some light on how to remove encoding from
val
Thanks in advance.
not sure i understand but either native JS decodeURI() or regex like this
var encoded = "john%doe%doe%bird";
console.log(encoded.replace(/%/g, "."));

Check for unique fields before creating object using Mongoose

I'm building a GraphQL Server where I need to do some sort of validation before committing data to database (MongoDB and Mongoose).
One of these checks is related to unique fields. So, a model may have one or more unique fields and I need to be able to check for that before saving into database.
So, I have build some helper functions to do it and the code is below:
Helper code:
import mongoose from "mongoose";
const isFieldUnique = (modelName, fieldName, fieldValue) => {
let model = mongoose.model(modelName);
let query = {};
query[fieldName] = fieldValue;
return model.findOne(query).exec();
};
const executeUniquePromises = (uniques, modelName, data) => {
let promises = [];
uniques.map(name => {
let value = data[name];
if (!value)
throw new Error("Cannot test uniqueness for a null field.");
promises.push(
isFieldUnique(modelName, name, value)
.then(value => {
if (value) {
let error = name + ' is not unique';
console.log(error);
return error;
}
console.log(name + ' is unique');
return null;
})
.catch(error => {
throw new Error(error);
})
)
});
return Promise.all(promises);
};
export const checkUniqueness = (uniques, modelName, data) => {
return new Promise((resolve, reject) => {
executeUniquePromises(uniques, modelName, data).then(result => {
let errors = [];
// Check for errors
result.map((error) => {
if (error)
errors.push(error);
});
if (errors.length > 0)
return reject(errors);
else
resolve();
});
});
}
Mongoose static create function:
import * as helper from './helper';
schema.statics.create = function (data) {
let uniques = ['name', 'email'];
helper.checkUniqueness(uniques,'Company', data)
.then(result => {
let user = new this(data);
return company.save();
})
.catch(error => {
throw new Error(error);
});
}
GraphQL code:
const createUser = {
type: UserType,
description: "Create a user",
args: {
data: {
name: "user",
type: new GraphQLNonNull(UserInputType)
}
},
resolve(root, args) {
return UserModel.create(args.data);
}
};
The helper code seens to be confused and I´m not using my usage of promises with other promises are the correct way of doing it.
Remember that I may need to check several fields for uniqueness, so that is why I´ve created the promise array.
One problem is that when I´m inserting data where there are not uniques matching I get no return in my GraphQL Server.
I want to find out a better way of doing it and discover why I´m not getting back the saved object.
MongoDB already handles unique out of the box. Set the field to unique: true in the Mongoose schema. You can use mongoose-beautiful-unique to make the error messages similar to the validation error messages. And finally, read this when you can't get unique: true to work.

Categories