I'm trying to get the Id of the new insert so I can push the Id onto another collection.
According to this post =>
Meteor collection.insert callback to return new id and this post => Meteor collection.insert callback issues, I should be able to
return Collection.insert(obj);
and it will return the ID of the newly inserted data to my client.
Instead I'm getting an Observable like this:
{_isScalar: false}
_isScalar: false
__proto__:
constructor: f Object()
hasOwnProperty: f hasOwnProperty()
//many other object properties
The documentation seems pretty clear that I should be getting the ID in return. Is this a bug? https://docs.meteor.com/api/collections.html#Mongo-Collection-insert]
My version of meteor is 1.4.4.5...
I've been working on this issue for a few days and I've tried getting the ID many different ways, nothing I've tried results in the ID.
Here's my full code for reference:
Server:
submitStuff: function(data): string{
var loggedInUser = Meteor.user();
if (!loggedInUser || !Roles.userIsInRole(loggedInUser,['user','admin'])){
throw new Meteor.Error("M504", "Access denied");
} else {
try{
let insertedData = null;
const model: MyModel[] = [{
data.stuff //bunch of data being inserted
}];
model.forEach((obj: MyModel) => {
insertedData = Collection.insert(obj);
});
return insertedData;
} catch(e) {
throw new Meteor.Error(e + e.reason, "|Throw new error|");
}
}
},
Client:
Meteor.call('submitData', data, (error, value) => {
if(error){
this.errors.push(error + error.reason + "|new Error code|");
}
else{
Meteor.call('userPushId', value, (error) => { //this pushes Id onto the second collection
if(error){
this.errors.push(error + error.reason + "|new Error code|");
}
});
}
Server
// ...
try {
const myModels = [{ ...whatever }];
// I'm using map so I return an array of id's.
//your forEach about technically should end up with only one id,
// which is the last insertion
const insertedDataIds = myModels.map(model => Collection.insert(model));
// you can also write to the secondary location here with something like:
const secondaryWrite = SecondaryCollection.insert({ something: insertedDataIds });
return insertedDataId's
}
//...
Client
also I don't know if this is just a typo on stack but your Meteor.call('submitData') should be Meteor.call('submitStuff') but that is probably not your actual issue.
Alright, so after searching around I realized I'm using angular-meteors rxjs package to create a MongoObservable when creating a collection instead of Mongo's regular collection.
Because I'm using MongoObservable and trying to invoke .insert() on that, the return is a bit different then a regular Mongo Collection and will return an Observable instead of the Id. Documentation Here
If you add a .collection after the collection name you will get the Id in return like so:
submitStuff: function(data): string{
var loggedInUser = Meteor.user();
if (!loggedInUser || !Roles.userIsInRole(loggedInUser,['user','admin'])){
throw new Meteor.Error("M504", "Access denied");
} else {
try{
let id = new Mongo.ObjectID();
const model: MyModel[] = [{
data.stuff //bunch of data being inserted
_id:
}];
model.forEach((obj: MyModel) => {
insertedData = Collection.collection.insert(obj); //added .collection
});
return insertedData;
} catch(e) {
throw new Meteor.Error(e + e.reason, "|Throw new error|");
}
}
},
Related
I am new to the SQL scene and I am trying to use Supabase for a web app that I am building.
I am trying to push an array objects to the DB, but what I am finding is that it only grabs the first element in the array and adds that to the DB. The end goal functionality is that it grabs the whole array of objects and pushes that and if it detects a change (why I am using upsert) then add that object to the array.
ownedNFTs is an array of objects.
const nakedDB = () => {
try {
setLoading(true);
ownedNFTs.forEach(async (nft) => {
let { data, error, status } = await supabase.from("Users").upsert({
wallet_address: wallet?.accounts[0].address,
NFTS: {
name: nft?.metadata?.name || "",
description: nft?.metadata?.description || "",
image: nft?.metadata?.image || "",
externalUrl: nft?.metadata?.external_url || "",
contractAddress: nft?.contract.address || "",
tokenId: nft?.id.tokenId || "",
blockchain: "Ethereum",
metaverse: nft?.metadata?.name.split(" ")[0] || "",
metadata: nft?.metadata || "",
symbol: nft?.contract.symbol || "",
},
}).eq('wallet_address', wallet?.accounts[0].address);
console.log(data)
if (error && status !== 406) {
throw error;
}
});
} catch (error) {
console.table(error);
} finally {
setLoading(false);
}
};
I have tried following the Supabase documentation on bulkupserting, but that is to create multiple rows at once. I have also tried creating a standard for loop, but that didnt work as well. My current implementation is using forEach() and that is not working as expected.
The problem why it was selecting the first element (I believe) is because the .foreach() function was not running multiple times. This was not a clean way to do a DB call in any case. In addition to that I do think that the data I was sending to the DB was not in the correct format as a pure array of objects. For example when you use .map() it returns two separate objects. Supabase (in my configuration) was not expecting this format.
The solution is below.
I created an empty array
I used .map() on my array of objects
Saved the result of the .map() to the array I created.
Sent the new array to the DB in one call.
const nakedDB = async () => {
try {
ownedNFTs.map((nft) => {
// console.log(nft)
nftArray.push(nft);
});
setLoading(true);
let { data, error, status } = await supabase
.from("Users")
.upsert({
wallet_address: wallet?.accounts[0].address,
NFTS: nftArray,
})
.eq("wallet_address", wallet?.accounts[0].address);
if (error && status !== 406) {
throw error;
}
} catch (error) {
console.table(error);
} finally {
setLoading(false);
}
};
** Updated Solution **
ownedNFTs is already an array of objects in the proper format. I removed the .map() and directly saved ownedNFTs to the DB and it saves all elements. The .map() solution was me over-engineering the issue.
I have this model in Mongoose and NodeJS:
const VisitorSchema = new Schema({
visits: [{date: Date, path: String, details: String}],
// ...
});
And this code:
// Get all visitors.
VisitorSchema.statics.getAllVisitors = function() {
try {
// Return all users.
return Visitor.find()
.sort({created: -1})
.exec();
} catch (err) {
console.log(err);
}
};
// Check and print new users.
VisitorSchema.statics.checkPaths = async function() {
console.log("Checking paths...");
let visitors = await this.getAllVisitors();
for (let visitor of visitors) {
try {
for (let v of visitors.visits) {
// ...
}
} catch (e) {
console.log(e);
console.log(Object.prototype.toString.call(visitor.visits));
throw(e);
}
}
};
Running this function unexpectedly throws:
Checking paths...
TypeError: visitors.visits is not iterable
at Function.VisitorSchema.statics.checkPaths
[object Array]
5efba3a0a97a823909802df5
(node:23663) UnhandledPromiseRejectionWarning: TypeError: visitors.visits is not iterable
at Function.VisitorSchema.statics.checkPaths
at processTicksAndRejections
....
I also checked the MongoDB object in the mongo shell and the sub-document visits for the relevant document is an array and seems OK:
> db.visitors.findOne({_id: ObjectId("...")})
{
"_id" : ObjectId("..."),
"visits" : [
{
"_id" : ObjectId("..."),
"date" : ISODate("..."),
"path" : "/"
},
...
]
}
How can an Array object not be iterable?
An array is always iterable in JS. Pay attention to the rows in the visitors collection, which may lack the visits property. If that property is not an array in MongoDB (this is allowed because MongoDB is a NoSQL database) it will still be cast to an empty array from your model definition.
In your specific case, you have a typo:
for (let v of visitors.visits) // plural
should probably be
for (let v of visitor.visits) // singular
question is possibly a duplicate but I haven't found anything that provides an appropriate answer to my issue.
I have an ExpressJS server which is used to provide API requests to retrieve data from a MongoDB database. I am using mongoosejs for the MongoDB connection to query/save data.
I am building a route that will allow me to find all data that matches some user input but I am having trouble when doing the query. I have spent a long while looking online for someone with a similar issue but coming up blank.
I will leave example of the code I have at the minute below.
code for route
// -- return matched data (GET)
router.get('/match', async (req, res) => {
const style_data = req.query.style; // grab url param for style scores ** this comes in as a string **
const character_data = req.query.character; // grab url param for character scores ** this comes in as a string **
// run matcher systems
const style_matches = style_match(style_data);
res.send({
response: 200,
data: style_matches
}); // return data
});
code for the query
// ---(Build the finder)
const fetch_matches_using = async function(body, richness, smoke, sweetness) {
return await WhiskyModel.find({
'attributes.body': body,
'attributes.richness': richness,
'attributes.smoke': smoke,
'attributes.sweetness': sweetness
});
}
// ---(Start match function)---
const style_match = async function (scores_as_string) {
// ---(extract data)---
const body = scores_as_string[0];
const richness = scores_as_string[1];
const smoke = scores_as_string[2];
const sweetness = scores_as_string[3];
const matched = [];
// ---(initialise variables)---
let match_count = matched.length;
let first_run; // -> exact matches
let second_run; // -> +- 1
let third_run; // -> +- 2
let fourth_run; // -> +- 3
// ---(begin db find loop)---
first_run = fetch_matches_using(body, richness, smoke, sweetness).then((result) => {return result});
matched.push(first_run);
// ---(return final data)---
return matched
}
example of db object
{
_id: mongoid,
meta-data: {
pagemd:{some data},
name: whiskyname
age: whiskyage,
price: price
},
attributes: {
body: "3",
richness: "3",
smoke: "0",
sweetness: "3",
some other data ...
}
}
When I hit the route in postman the JSON data looks like:
{
response: 200,
data: {}
}
and when I console.log() out matched from within the style match function after I have pushed the it prints [ Promise(pending) ] which I don't understand.
if I console.log() the result from within the .then() I get an empty array.
I have tried using the populate() method after running the find which does technically work, but instead of only returning data that matches it returns every entry in the collection so I think I am doing something wrong there, but I also don't see why I would need to use the .populate() function to access the nested object.
Am I doing something totally wrong here?
I should also mention that the route and the matching functions are in different files just to try and keep things simple.
Thanks for any answers.
just posting an answer as I seem to have fixed this.
Issue was with my .find() function, needed to pass in the items to search by and then also a call back within the function to return error/data. I'll leave the changed code below.
new function
const fetch_matches_using = async function(body, richness, smoke, sweetness) {
const data = await WhiskyModel.find({
'attributes.body': body,
'attributes.richness': richness,
'attributes.smoke': smoke,
'attributes.sweetness': sweetness
}, (error, data) => { // new ¬
if (error) {
return error;
}
if (data) {
console.log(data)
return data
}
});
return data; //new
}
There is still an issue with sending the found results back to the route but this is a different issue I believe. If its connected I'll edit this answer with the fix for that.
While working on Skygear JS SDK, is the query returning an Array?
readDummy: function(){
const Test = skygear.Record.extend('test_test');
const Query = new skygear.Query(Test);
skygear.publicDB.query(Query).then((records) => {
console.log(records.constructor === Array); // return false
console.log(JSON.stringify(records[0])); //do display correctly
//{"_id":"test_test/b9633d1a-ff3c-491b-82f3-93c8cefb5313","_access":[{"public":true,"level":"read"}],"content":"Hello World"}
}, (error) => {
console.error(error);
});
},
Appears the object is actually a QueryResult which extends Array.
Whatever you're passing the object to appears to be too particular about the exact type.
You could try this
Array.from(records)
to make it a native array.
I need to be able to pass the server some info when calling fetch on a Backbone collection. On my front-end I have this code, passing data:{} to the fetch function along with the success and error callbacks. Looking at the docs for Backbone fetch:
http://backbonejs.org/#Collection-fetch
perhaps I am not passing in the parameters correctly to the fetch function? Perhaps they should be in an array []...
var lineupCollection = new LineupCollection([]);
var loadTeamsOnPageLoad = (function() {
lineupCollection.url = '/api/lineups';
lineupCollection.fetch(
{
processData: true,
data: {
team_id:$("#team_id").attr("value")
}
},
{
success: function (result) {
if(window.teamDashboardTableView === undefined){
window.teamDashboardTableView = new TeamDashboardTableView({el: $("#team-dashboard-table-div")});
}
window.teamDashboardTableView.render();
},
error: function (err) {
setTimeout(function () {
alert('error fetching lineupCollection - ' + err.message);
}, 1);
}
}
);
})();
which will load a Backbone collection into a table.
It's call this method on the back-end:
exports.getBackboneLineups = function(req,res,next){
var user_id = req.mainUser._id;
console.log('req.params',req.params); //empty {}
console.log('req.team',req.team); //undefined
console.log('team_id',req.team_id); //undefined
console.log('req.data',req.data); //undefined
console.log('req.body',req.body); //empty {}
var team_id = req.team._id; //undefined EXCEPTION, can't read property _id of undefined
var system_db = req.system_db;
var Lineup = LineupModel.getNewLineup(system_db,user_id);
Lineup.find({team_id:team_id}, function (err, lineups) {
if (err) {
return next(err);
}
res.json(lineups);
});
};
however, I am totally failing as far as how to access the data that I supposedly past to the server from the client. I can't really find a good example and I am getting tired of guessing. What's the best way to do this?
edit:
also tried passing {data:{}, success: function(), error: function()} all in the same JavaScript object, but that didn't work.
fetch only takes one argument (see http://backbonejs.org/#Collection-fetch). Instead, you should probably alter your backend to specify the team id in the resource's URL. For example:
lineupCollection.url = function(){ return '/api/lineups?team_id=' + this.team_id; };
lineupCollection.team_id = $("#team_id").attr("value");
lineupCollection.fetch({ success: ..., error: ...});