Error: Collection method aggregate is synchronous - javascript

I'm trying the following code:
const Conn = mongoose.createConnection('mongodb://127.0.0.1:27017/db');
const addresses = Conn.collection('users').aggregate([
{
$project: {
_id: false,
ethAddr: true,
}
}
]);
I receive the following error:
[...]\backend\node_modules\mongoose\lib\drivers\node-mongodb-native\collection.js:100
throw new Error('Collection method ' + i + ' is synchronous');
^
Error: Collection method aggregate is synchronous
at NativeCollection.<computed> [as aggregate] ([...]\backend\node_modules\mongoose\lib\drivers\node-mongodb-native\collection.js:100:15)
at file:///[...]/backend/scripts/dbGetMerkleRoot.js:11:46
at file:///[...]/backend/scripts/dbGetMerkleRoot.js:28:3
at ModuleJob.run (node:internal/modules/esm/module_job:185:25)
at async Promise.all (index 0)
at async ESMLoader.import (node:internal/modules/esm/loader:281:24)
at async loadESM (node:internal/process/esm_loader:88:5)
at async handleMainPromise (node:internal/modules/run_main:65:12)
What in cattle's name I'm doing wrong?

It seems to be a problem with the way mongoose connects to the database. Creating the connection but not connecting prior to invoking the aggregation method causes that exception to be thrown. I should've used it this way:
// create custom connection
const Conn = mongoose.createConnection();
// connect to database
await Conn.openUri('mongodb://127.0.0.1:27017/db');
// #type {AggregationCursor}
const addresses = Conn.collection('users').aggregate([
{
$project: {
_id: false,
ethAddr: true,
}
}
]);
console.log( await addresses.toArray() );
It's frustrating the exception itself is poorly documented.

Related

MongooseError: Query was already executed:

I'm trying to update the document but the error says the query has already been executed.
MongooseError: Query was already executed: footballs.updateOne({ date: 'January 4' }, {})
app.post('/api/bookslot', async (req, res) => {
console.log(req.body);
try {
const token = req.headers['x-access-token'];
const decoded = jwt.verify(token, 'secret123');
const email = decoded.email;
const user = await UserModel.findOne({ email: email });
let sportname = req.body.selectedSport.toLowerCase();
const time = req.body.slotTime;
const seats = req.body.availableSeats - 1;
if (!sportname.endsWith('s')) {
sportname = sportname.concat('s');
}
const NewSlotModel = mongoose.model(sportname, slotSchema);
var update = {};
update[time] = seats - 1;
console.log(update);
const a = await NewSlotModel.updateOne(
{ date: req.body.slotDate },
{ $set: update },
function (err, success) {
if (err) return handleError(err);
}
);
return res.json({ status: 'ok' });
} catch (e) {
console.log(e);
res.json({ status: 'error' });
}
});
where am I going wrong?
You are using both async/await and callbacks in your code, causing mongoose to throw an error.
The actual effect of using them both is exactly the error type that you are receiving:
Query was already executed
Mongoose v6 does not allow duplicate queries.
Mongoose no longer allows executing the same query object twice. If
you do, you'll get a Query was already executed error. Executing the
same query instance twice is typically indicative of mixing callbacks
and promises, but if you need to execute the same query twice, you can
call Query#clone() to clone the query and re-execute it. See gh-7398
Duplicate Query Execution
To fix the issue, just remove the third argument from the await
NewSlotModel.updateOne
Making it:
const a = await NewSlotModel.updateOne(
{ date: req.body.slotDate },
{ $set: update }
);
Mongoose v6. Don't support callbacks any longer.. check the image.
const productCount = await Product.countDocuments((count) => count) BAD
const productCount = await Product.countDocuments(); GOOD

Can anyone tell me why it is showing array is empty?

I want to send notification to multiple devices and for that am getting the token via querying the docs and saving the token to array but it shows that array is empty. Most probably error is because am not able to add elements in the array.
My code is:-
var registrationTokens=[];
const indexOfSender=usersList.indexOf(chatItem.senderUsername);
let removedUsername=usersList.splice(indexOfSender,1); //to remove the senders name from list
usersList.forEach(async(element)=>{
const query = admin.firestore().collection('users').where("username","==",element);
const querySnapshot = await query.get();
if (querySnapshot.docs.length > 0) {
const doc = querySnapshot.docs[0];
const data = doc.data();
registrationTokens.push(data.androidNotificationToken); //adding token over here
}
else {
console.log("Unable to get token for the username ", element);
}
});
const message =
{
notification: {
title:'Message',
body: body,
imageUrl: url,
},
tokens: registrationTokens,
data: { recipient: senderUserId },
};
admin.messaging().sendMulticast(message)
.then(response =>
{
if (response.failureCount > 0) {
const failedTokens = [];
response.responses.forEach((resp, idx) => {
if (!resp.success) {
failedTokens.push(registrationTokens[idx]);
}
});
console.log('List of tokens that caused failures: ' + failedTokens);
}
else
{
console.log('Successfully sent messages ', response);
}
});
Error
Error: tokens must be a non-empty array
at FirebaseMessagingError.FirebaseError [as constructor] (/workspace/node_modules/firebase-admin/lib/utils/error.js:42:28)
at FirebaseMessagingError.PrefixedFirebaseError [as constructor] (/workspace/node_modules/firebase-admin/lib/utils/error.js:88:28)
at new FirebaseMessagingError (/workspace/node_modules/firebase-admin/lib/utils/error.js:254:16)
at Messaging.sendMulticast (/workspace/node_modules/firebase-admin/lib/messaging/messaging.js:294:19)
at sendNotificationForGroupChat (/workspace/index.js:238:35)
at exports.onCreateMessage.functions.region.firestore.document.onCreate (/workspace/index.js:116:9)
at process._tickCallback (internal/process/next_tick.js:68:7)
async inside forEach does not work the way you expect. If you add some logging, you will see that the loop ends before any of its async work is complete, leaving your tokens array empty before you pass it to FCM. Each iteration through the loop simply generates a promise that is not resolved. You will need to rewrite the code to actually wait for all those promises before calling FCM.
Read more about that:
Using async/await with a forEach loop
for-of loop will work just fine with asynchronous calls :)
Cheers

TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received an instance of Object

I am using the source code from a security rules tutorial to attempt to do integration testing with Jest for my Javascript async function async_create_post, used for my firebase HTTP function create_post The files involved has a directory structure of the following:
Testing file: root/tests/handlers/posts.test.js
File to be tested: root/functions/handlers/posts.js
Helper code from the tutorial: root/tests/rules/helpers.js
And here is the source code that is involved:
posts.test.js
const { setup, teardown} = require("../rules/helpers");
const {
async_get_all_undeleted_posts,
async_get_post,
async_delete_post,
async_create_post
} = require("../../functions/handlers/posts");
describe("Post Creation", () => {
afterEach(async () => {
await teardown();
});
test("should create a post", async () => {
const db = await setup();
const malloryUID = "non-existent uid";
const firstPost = {
body: "First post from Mallory",
author_id: malloryUID,
images: ["url1", "url2"]
}
const before_post_snapshot = await db.collection("posts").get();
expect(before_post_snapshot.docs.length).toBe(0);
await async_create_post(firstPost); //fails at this point, expected to create a new post, but instead threw an error
const after_post_snapshot = await db.collection("posts").get();
expect(after_post_snapshot.docs.length).toBe(1);
});
});
posts.js
const {admin, db } = require('../util/admin');
//admin.initializeApp(config); //my credentials
//const db = admin.firestore();
const { uuid } = require("uuidv4");
const {
success_response,
error_response
} = require("../util/validators");
exports.async_create_post = async (data, context) => {
try {
const images = [];
data.images.forEach((url) => {
images.push({
uid: uuid(),
url: url
});
})
const postRecord = {
body: data.body,
images: images,
last_updated: admin.firestore.FieldValue.serverTimestamp(),
like_count: 0,
comment_count: 0,
deleted: false,
author_id: data.author_id
};
const generatedToken = uuid();
await db
.collection("posts")
.doc(generatedToken)
.set(postRecord);
// return success_response();
return success_response(generatedToken);
} catch (error) {
console.log("Error in creation of post", error);
return error_response(error);
}
}
When I run the test in Webstorm IDE, with 1 terminal running Firebase emulators:start , I get the following error message.
console.log
Error in creation of post TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received an instance of Object
at validateString (internal/validators.js:120:11)
at Object.basename (path.js:1156:5)
at GrpcClient.loadProto (/Users/isaac/Desktop/project/functions/node_modules/google-gax/src/grpc.ts:166:23)
at new FirestoreClient (/Users/isaac/Desktop/project/functions/node_modules/#google-cloud/firestore/build/src/v1/firestore_client.js:118:38)
at ClientPool.clientFactory (/Users/isaac/Desktop/project/functions/node_modules/#google-cloud/firestore/build/src/index.js:330:26)
at ClientPool.acquire (/Users/isaac/Desktop/project/functions/node_modules/#google-cloud/firestore/build/src/pool.js:87:35)
at ClientPool.run (/Users/isaac/Desktop/project/functions/node_modules/#google-cloud/firestore/build/src/pool.js:164:29)
at Firestore.request (/Users/isaac/Desktop/project/functions/node_modules/#google-cloud/firestore/build/src/index.js:961:33)
at WriteBatch.commit_ (/Users/isaac/Desktop/project/functions/node_modules/#google-cloud/firestore/build/src/write-batch.js:485:48)
at exports.async_create_post (/Users/isaac/Desktop/project/functions/handlers/posts.js:36:5) {
code: 'ERR_INVALID_ARG_TYPE'
}
at exports.async_create_post (/Users/isaac/Desktop/project/functions/handlers/posts.js:44:13)
Error: expect(received).toBe(expected) // Object.is equality
Expected: 1
Received: 0
<Click to see difference>
at Object.<anonymous> (/Users/isaac/Desktop/project/tests/handlers/posts.test.js:59:45)
Error in creation of post comes from the console.log("Error in creation of post", error); in posts.js, so the error is shown in the title of this post.
I want to know why calling the async_create_post from posts.test.js will cause this error and does not populate my database with an additional record as expected behaviour. Do inform me if more information is required to solve the problem.
Here are some code snippets that may give more context.
helpers.js [Copied from the repository]
const firebase = require("#firebase/testing");
const fs = require("fs");
module.exports.setup = async (auth, data) => {
const projectId = `rules-spec-${Date.now()}`;
const app = firebase.initializeTestApp({
projectId,
auth
});
const db = app.firestore();
// Apply the test rules so we can write documents
await firebase.loadFirestoreRules({
projectId,
rules: fs.readFileSync("firestore-test.rules", "utf8")
});
// write mock documents if any
if (data) {
for (const key in data) {
const ref = db.doc(key); // This means the key should point directly to a document
await ref.set(data[key]);
}
}
// Apply the actual rules for the project
await firebase.loadFirestoreRules({
projectId,
rules: fs.readFileSync("firestore.rules", "utf8")
});
return db;
// return firebase;
};
module.exports.teardown = async () => {
// Delete all apps currently running in the firebase simulated environment
Promise.all(firebase.apps().map(app => app.delete()));
};
// Add extensions onto the expect method
expect.extend({
async toAllow(testPromise) {
let pass = false;
try {
await firebase.assertSucceeds(testPromise);
pass = true;
} catch (error) {
// log error to see which rules caused the test to fail
console.log(error);
}
return {
pass,
message: () =>
"Expected Firebase operation to be allowed, but it was denied"
};
}
});
expect.extend({
async toDeny(testPromise) {
let pass = false;
try {
await firebase.assertFails(testPromise);
pass = true;
} catch (error) {
// log error to see which rules caused the test to fail
console.log(error);
}
return {
pass,
message: () =>
"Expected Firebase operation to be denied, but it was allowed"
};
}
});
index.js
const functions = require('firebase-functions');
const {
async_get_all_undeleted_posts,
async_get_post,
async_delete_post,
async_create_post
} = require('./handlers/posts');
exports.create_post = functions.https.onCall(async_create_post);
The error message means that a method of the path module (like path.join) expects one of its arguments to be a string but got something else.
I found the offending line by binary search commenting the program until the error was gone.
Maybe one of your modules uses path and you supply the wrong arguments.

Neo4J - Nodes not creating when connecting through JavaScript

I am trying to add a node to a neo4j database in a node.js app and the code seems to execute fine, but no node is created.
Here is my code:
var neo4j = require('neo4j-driver');
// the rest of my program
const driver = new neo4j.driver(
"bolt://localhost:7687",
neo4j.auth.basic(neouser, neopass)
);
const session = driver.session();
var cypher =
"MERGE (b:TRIGGER {name:'" +
rname +
"'}) MERGE (c:FIELD{name:'" +
trigger +
"'}) MERGE (b)<-[:ACTIVATES]-(c)";
try {
const result = session.run(cypher);
console.log(result);
} finally {
console.log("finally");
session.close();
}
driver.close();
and the Error result:
Result {
_stack: '\n' +
' at captureStacktrace (C:\\NGToolbox\\NGToolBox\\node_modules\\neo4j-driver\\lib\\result.js:263:15)\n' +
' at new Result (C:\\NGToolbox\\NGToolBox\\node_modules\\neo4j-driver\\lib\\result.js:68:19)\n' +
' at Session._run (C:\\NGToolbox\\NGToolBox\\node_modules\\neo4j-driver\\lib\\session.js:174:14)\n' +
' at Session.run (C:\\NGToolbox\\NGToolBox\\node_modules\\neo4j-driver\\lib\\session.js:135:19)\n' +
' at C:\\NGToolbox\\NGToolBox\\routes\\bizrulevis.js:261:30\n' +
' at Layer.handle [as handle_request] (C:\\NGToolbox\\NGToolBox\\node_modules\\express\\lib\\router\\layer.js:95:5)\n' +
' at next (C:\\NGToolbox\\NGToolBox\\node_modules\\express\\lib\\router\\route.js:137:13)\n' +
' at Route.dispatch (C:\\NGToolbox\\NGToolBox\\node_modules\\express\\lib\\router\\route.js:112:3)\n' +
' at Layer.handle [as handle_request] (C:\\NGToolbox\\NGToolBox\\node_modules\\express\\lib\\router\\layer.js:95:5)\n' +
' at C:\\NGToolbox\\NGToolBox\\node_modules\\express\\lib\\router\\index.js:281:22',
_streamObserverPromise: Promise { <pending> },
_p: null,
_query: "MERGE (b:TRIGGER {name:'DELETEME.1'}) MERGE (c:FIELD{name:'4001'}) MERGE (b)<-[:ACTIVATES]-(c)",
_parameters: {},
_connectionHolder: ConnectionHolder {
_mode: 'WRITE',
_database: '',
_bookmark: Bookmark { _values: [] },
_connectionProvider: DirectConnectionProvider {
_id: 3,
_config: [Object],
_log: [NoOpLogger],
_userAgent: 'neo4j-javascript/0.0.0-dev',
_authToken: [Object],
_connectionPool: [Pool],
_openConnections: [Object],
_address: [ServerAddress]
},
_referenceCount: 1,
_connectionPromise: Promise { <pending> }
}
}
finally
When I go into Neo4j the db is still empty.
I have confirmed that it is active, that I can use that same cypher to create a node, that the connection is being established in the security log and it is not failing due to un/pw. Everything looks perfect, except for the whole nothing created issue.
Thank you for any advice.
edit: added the driver call and the driver close from outside the snippets scope.
This code should work for you (assuming neouser, neopass, rname, and trigger are defined):
const neo4j = require('neo4j-driver');
async function doIt() {
const driver = neo4j.driver(
"bolt://localhost",
neo4j.auth.basic(neouser, neopass)
);
const session = driver.session();
try {
await session.writeTransaction(async txc => {
var result = await txc.run(
`MERGE (b:TRIGGER {name: $rname})
MERGE (c:FIELD {name: $trigger})
MERGE (b)<-[:ACTIVATES]-(c)
RETURN b, c`,
{rname: rname, trigger: trigger}
)
result.records.map(record => {
console.log(`b: ${record.get('b')}, c: ${record.get('c')}`);
})
})
} finally {
console.log("finally");
await session.close();
await driver.close();
}
}
doIt();
This code is aware of the asynchronous nature of the processing, uses the result after the transaction has completed, and only then closes the session and driver. It also uses a writeTransaction to write to the DB.
You should study the driver's github documentation to get an idea of all the ways the driver can be used.
If you are writing some API, try to use an OGM
https://www.npmjs.com/package/neo4j-node-ogm
class SomeModel extends from Model {} //see documentation
and to persist
const a = new SomeModel()
await a.save()
Tell me if that helps you.

"Cannot find global value 'Promise'" Error in Mocha test

The following has to do with a full stack Node.js problem. It involves Express, Mongoose, and Mocha.
I have a controller module with a function that processes an HTTP call. It basically takes Request and Response objects as its arguments. Within it, it pulls Form data out of the Request object and stores data in multiple MongoDB instances. In order to accomplish multiple data stores we use a call to Promise.all. This is done in an async function. Something like the following
async function saveData(data1 : Data1Interface, data2 : Data2Interface,
res: Response)
{
try
{
//Call 3 save methods each returning promised. Wait fLoginInfoModelor them all to be resolved.
let [data1Handle, data2Handle] = await Promise.all([saveData1(data1),
saveData2(data2)]);
//if we get here all of the promises resolved.
//This data2Handle should be equal to the JSON {"id" : <UUID>}
res.json(data2Handle);
res.status(200);
}
catch(err)
{
console.log("Error saving registration data” + err);
res.json( {
"message" : "Error saving registration data " + err
});
res.status(500);
}
}
Within saveData1 and saveData2 I am doing something like:
function saveData1(data : DataInterface) : Promise<any>
{
let promise = new Promise(function(resolve : bluebird.resolve, reject : bluebird.reject)
{
Data1Model.create(data, function(err,
data){
….
.
.
This works fine! We are doing all of this in Typescript.
However I want to test this method using Mocha. This is where the problems start. For the sake of brevity I am only using one of the Mongoose Models in this example. If I try to run the following code as a mocha unit test I get the following error message. I am not sure what it wants as far as a Promise constructor?
TSError: ⨯ Unable to compile TypeScript
Cannot find global value 'Promise'. (2468)
server/controllers/registration.server.controller.ts (128,17): An async function or method in ES5/ES3 requires the 'Promise' constructor. Make sure you have a declaration for the 'Promise' constructor or include 'ES2015' in your --lib option. (2705)
server/controllers/registration.server.controller.ts (134,66): 'Promise' only refers to a type, but is being used as a value here. (2693)
Note that line 128 is the line that starts “async function saveData(data1 : Data1Interface, data2 : Data2Interface, res: Response)
“
The following two lines
“let [data1Handle, data2Handle] = await Promise.all([saveData1(data1),
saveData2(data2)]); “
and
“ let promise = new Promise(function(resolve : bluebird.resolve, reject : bluebird.reject)”
produce the “'Promise' only refers to a type, but is being used as a value here.” errors.
The Mocha unit test code looks something like the following.
import 'mocha';
import { expect } from 'chai';
import * as sinon from 'sinon';
var config = require('../config/config');
import * as sinonmongoose from 'sinon-mongoose';
import * as controller from './registration.server.controller';
import { Request, Response } from 'express';
import { Data1Interface, Data1Model} from '../models/data1.server.model';
import * as mathUtilities from '../utilities/math.utilities';
import mongoose = require("mongoose");
import * as bluebird from 'mongoose';
(mongoose as any).Promise = bluebird.Promise;
//NOTE: This currently does not work.
describe('Registration related tests', function () {
beforeEach(()=>{
});
afterEach(()=>{
//sinon.restore(authentication);
});
it('should return 200 OK valid outline', function (done) {
let dummyRequest: any = {
body: {
username: "awhelan",
password: "awhelan",
firstName : "Andrew",
lastName: "Whelan",
email: "awhelan#srcinc.com",
source: "none",
school: "Arizona",
consent: true,
dob: "1970-03-10",
gender:"Male",
interviewconsent: true,
recordingconsent: true
}
};
let id = mathUtilities.createId("Andrew", "Whelan", "awhelan#srcinc.com");
let retJson = "{id:" + id +"}";
let dummyResponse: any = {
json: function (data) {
expect(data).to.equal(retJson);
done();
return this;
},
sendStatus: function (code) {
expect(code).to.equal(200);
done();
return this;
}
};
let req: Request = dummyRequest as Request;
let res: Response = dummyResponse as Response;
let mock = sinon.mock(Data1Model).expects('create').yields(null, { nModified: 1 });
controller.register(req, res);
sinon.restore(Data1Model.create);
});
});
Note that the suggestion in ts An async function or method in ES5/ES3 requires the 'Promise' constructor” doesn’t help.
Any suggestions as to how I might move past these errors would be appreciated.
-Andrew
Fixed it by installing es6-shim typings.

Categories

Resources