Mongoose aggregation With GraphQL returning error - node.js

I'm working on a project and currently using Mongodb Time-Series and aggregation.
I connected my Apollo Graphql to retrieve the data but i'm stuck with an error that i can not solve no matter what.
Float cannot represent non numeric value:
On my db the numbers are saved as Double. If i try to run the query in MongoDB Aggregation with MongoDBCompass it's working perfectly but not on my Nodejs database.
I tried googling my problem first and looking ad docs but all the different solution i found do not solve my problem, any suggestions?
This is my Query:
module.exports = async (root, { limit }, { models }) => {
const keyx = await models.Weather.aggregate([
{
$group: {
_id: {
yearMonthDay: {
$dateToString: { format: "%Y-%m-%d", date: "$timestamp" },
},
},
temp: {
$push: { temp: "$temp" },
},
},
},
])
.exec();
return keyx;
};
This is my model:
const mongoose = require("mongoose");
const { Schema } = mongoose;
mongoose.pluralize(null);
const weather = new Schema({
timestamp: {
type: String,
trim: true,
},
temp: {
type: Number,
trim: true,
},
});
const Weather = mongoose.model("time_weather", weather);
module.exports = { Weather };
and this is my types:
const { gql } = require("apollo-server");
module.exports = gql`
type Weather {
timestamp: String
_id: ID
temp: Float
}
type Query {
weather(limit: Int): [Weather]
}
`;
and finally this is the response i'm getting from apollo studio:
{
"errors": [
{
"message": "Float cannot represent non numeric value: [{ temp: 11.7 }, { temp: 11.7 }, { temp: 12.01 }, { temp: 13.21 }, { temp: 13.21 }, { temp: 11.93 }, { temp: 13.21 }, { temp: 12.73 }, { temp: 14.21 }, { temp: 11.7 }, ... 14 more items]",
"locations": [
{
"line": 6,
"column": 5
}
],
"path": [
"weather",
0,
"temp"
],
}
},

Related

GraphQL with nested schema returning null

I am trying to return a mongodb document upon a graphql query but getting null value. No error is being shown. The mongodb query works fine with mongoshell or mongoose.
Here is the schema, typedef and resolver:
const unionSchema = new Schema(
{
geometry: mongoose.Schema.Types.MultiPolygon,
properties: {
Divi_name: String,
Dist_name: String,
Upaz_name: String,
Uni_namae: String,
},
},
{ collection: "unionbounds" }
);
const union = mongoose.model("Union", unionSchema);
const typeDefs = `
type Query {
data: Union
}
type Union {
properties: Props
}
type Props{
Dist_name: String,
}
`;
const resolvers = {
Query: {
data: () => {
union.findOne(
{
geometry: {
$geoIntersects: {
$geometry: {
type: "Point",
coordinates: [90, 22],
},
},
},
},
"properties"
);
},
},
};
Mongoshell query returns the document:
{
properties: {
Div_ID: '10',
Dist_ID: '04',
Upz_ID: '28',
Un_ID: '95',
Un_UID: '10042895',
Divi_name: 'Barisal',
Dist_name: 'Barguna',
Upaz_name: 'Barguna Sadar Upazila',
Uni_name: 'Naltona',
Area_SqKm: 45.7658667915
},
_id: 6001e54a51c6d49215322f94
}
My suspicion is that I am doing something wrong in the resolver function. I would appreciate any suggestion.
The problem was indeed the resolver function. Following code worked after returning the result from the callback function and using async .. await.
const resolvers = {
Query: {
data: async () => {
var value;
await union.findOne(
{
geometry: {
$geoIntersects: {
$geometry: {
type: "Point",
coordinates: [90, 22],
},
},
},
},
"properties",
function (err, result) {
value = result;
}
);
return value;
},
},
};

Get all data from subdocument via graphql

Model
const mongoose=require('mongoose');
const CustomerGeneralInformation = require('./CustomerGeneralInformation').schema;
const CustomerContact = require('./CustomerContact').schema;
const CustomerFinance=require('./CustomerFinancial').schema;
const CustomerPersonalData=require('./CustomerPersonalData').schema;
const CustomerIdentification=require('./CustomerIdentification').schema;
const Schema=mongoose.Schema;
const CustomerSchema=new Schema({
generalInformation: [CustomerGeneralInformation],
contactData: [CustomerContact],
financialData:[CustomerFinance],
personalData:[CustomerPersonalData],
identificationData:[CustomerIdentification]
});
module.exports=mongoose.model('Customer',CustomerSchema);
Graphql Schema
const RootQuery=new GraphQLObjectType({
name:'RootQueryType',
fields:{
customers:{
type: new GraphQLList(CustomerType),
resolve(parent,args){
return customer.find({});
}
}
}
});
GraphiQl
{
customers
{
id
generalInformation{
purposeOfBusiness
}
contactData{
phone
email
countryResidence
mailbox
houseNo
zip
city
}
financialData{
taxNo
countryTaxation
refBankIban
StringrefBankBic
refBankIban
}
}
}
Result
{
"data": {
"customers": [
{
"id": "5fa0f8ea4e028a2cf8d24c3f",
"generalInformation": {
"purposeOfBusiness": null
},
"contactData": {
"phone": null,
"email": null,
"countryResidence": null,
"mailbox": null,
"houseNo": null,
"zip": null,
"city": null
},
"financialData": {
"taxNo": null,
"countryTaxation": null,
"refBankIban": null,
"StringrefBankBic": null
}
}
]
}
}
SO I have created a customer model which have an id(autogenerated by mongodb) and 5 other fields of schema type.
I have used graphql for creating apis.
Everything is working fine, I have created mutations to add data, its all tested in mongo that data is being saved.
But if you check my graphql output I am unable to query subdocuments, it shows null. I want list of all subdocuments with data.
PlEASE HELP
I had tried this way and its working perfectly,
Firstly as you are passing the whole model in your new Customer model so instead of that I just have stored the ID as a reference.
const CustomerSchema = new mongoose.Schema(
{
generalInformation: {
type: mongoose.Schema.Types.ObjectId,
ref: "GeneralInfo"
},
contactData: { type: mongoose.Schema.Types.ObjectId, ref: "ContactData" },
financialData: {
type: mongoose.Schema.Types.ObjectId,
ref: "FinacialData"
},
personalData: { type: mongoose.Schema.Types.ObjectId, ref: "PersonalData" }
},
{
timestamps: true
}
);
After that, for CustomerType, I had done this
const CustomerType = new GraphQLObjectType({
name: "Customer",
fields: () => ({
id: { type: GraphQLID },
customerContact: {
type: CustomerContactType,
resolve(parent, args) {
return CustomerContact.findById(parent.contactData);
}
},
generalInformation: {
type: CustomerGeneralType,
resolve(parent, args) {
return CustomerGeneral.findById(parent.generalInformation);
}
},
financialData: {
type: CustomerFinanceType,
resolve(parent, args) {
return CustomerFinance.findById(parent.financialData);
}
},
personalData: {
type: CustomerPersonalType,
resolve(parent, args) {
return CustomerPersonal.findById(parent.personalData);
}
}
})
});
And them for RootQuery
customers: {
type: new GraphQLList(CustomerType),
resolve(parent, args) {
return Customar.find({});
}
}
and here is my Query:
{
customers{
id
generalInformation{
purposeOfBusiness
}
customerContact{
phone
email
countryResidence
}
financialData{
taxNo
}
}
}
And Output
{
"data": {
"customers": [
{
"id": "5fa3fe066f0fda0568e56456",
"generalInformation": {
"purposeOfBusiness": "Test Purpose"
},
"customerContact": {
"phone": "123456798",
"email": "test#demo.com",
"countryResidence": "Pak"
},
"financialData": {
"taxNo": "120033244"
}
}
]
}
}

Mongoose multiple optional 'and' conditions

I am trying to filter a collection with 3 optional 'and' conditions.
Here is my model:
const Company = mongoose.model(
'Company',
new Schema({
name: { type: String },
sectors: [{ type: Schema.Types.ObjectId, ref: 'Sector' }],
industries: [{ type: Schema.Types.ObjectId, ref: 'Industry' }],
countries: [{ type: Schema.Types.ObjectId, ref: 'Country' }],
})
And my component:
const getCompanies = (skip, limit, filter) =>
Company.find({
...filter.countries && { countries: filter.countries },
...filter.sectors && { sectors: filter.sectors },
...filter.industries && { industries: filter.industries },
})
.skip(skip)
.limit(limit)
.sort({ date: -1 })
.populate('countries')
.populate('sectors')
.populate('industries');
const getAll = async (req, res) => {
try {
const countries = req.query.country;
const sectors = req.query.sector;
const industries = req.query.industry;
const skip = parseInt(req.query.skip, 10);
const limit = parseInt(req.query.limit, 10);
const filter = {
...countries && { countries },
...sectors && { sectors },
...industries && { industries },
};
const result = await getCompanies(skip, limit, filter);
return res.status(200).json(result);
} catch (e) {
return res.status(500).send({ message: (e.message) });
}
};
This is working when the filter is empty, but when there is one or more items in the filter, I get an empty array.
If I hard code data in getCompanies like so:
Company.find({
countries: '5d5e913e20c01070fef5c77e',
sectors: '5d5e913e20c01070fef5c754',
industries: '5d5e913e20c01070fef5c7ad',
})
or :
Company.find({
countries: '5d5e913e20c01070fef5c77e'
})
I get the data I want.
I also tried to console.log the filter in getCompanies to make sure the data was correct, and I get this if all fields are requested:
{
countries: '5d5e913e20c01070fef5c77e',
sectors: '5d5e913e20c01070fef5c754',
industries: '5d5e913e20c01070fef5c7ad',
}
and this for just one:
{ countries: '5d5e913e20c01070fef5c77e' }
So it seems fine to me.
I also tried using '$and' like so:
Company.find({ $and: [
{ ...filter.countries && { countries: filter.countries } },
{ ...filter.sectors && { sectors: filter.sectors } },
{ ...filter.industries && {industries: filter.industries } },
],
})
or using '$in':
Company.find({
...filter.countries && { countries: { $in: filter.countries } },
...filter.sectors && { sectors: { $in: filter.sectors } },
...filter.industries && { industries: { $in: filter.industries } },
})
But no luck either.
Here is a sample URL:
GET /api/internal/member/get?skip=12&limit=6&country=5d5e913e20c01070fef5c77e&sector=&industry=
I have found some other threads with questions somewhat similar to mine, but they were to different to help me solve my case.
Looking forward to your helpful advice.
I finally got it to work. Turns out I was not clearing the data in the frontend at each change, which caused an issue with the skip/limit fields.
I also changed the find as advised by #whoami like so:
Company.find({ $and: [
{
...filter.countries && { countries: { $in: [mongoose.Types.ObjectId(filter.countries)] } },
...filter.sectors && { sectors: { $in: [mongoose.Types.ObjectId(filter.sectors)] } },
...filter.sdgs && { sdgs: { $in: [mongoose.Types.ObjectId(filter.sdgs)] } },
}
],
})

Mongodb update multiple documents with different values

I have been trying to use updatemany with mongoose. I want to update the values in database using an array of objects.
[
{
"variantId": "5e1760fbdfaf28038242d676",
"quantity": 5
},
{
"variantId": "5e17e67b73a34d53160c7252",
"quantity": 13
}
]
I want to use variantId as filter.
Model schema is:
let variantSchema = new mongoose.Schema({
variantName: String,
stocks: {
type: Number,
min: 0
},
regularPrice: {
type: Number,
required: true
},
salePrice: {
type: Number,
required: true
}
})
I want to filter the models using variantId and then decrease the stocks.
As you need to update multiple documents with multiple criteria then .updateMany() wouldn't work - it will work only if you need to update multiple documents with same value, Try this below query which will help you to get it done in one DB call :
const Mongoose = require("mongoose");
let variantSchema = new mongoose.Schema({
variantName: String,
stocks: {
type: Number,
min: 0
},
regularPrice: {
type: Number,
required: true
},
salePrice: {
type: Number,
required: true
}
})
const Variant = mongoose.model('variant', variantSchema, 'variant');
let input = [
{
"variantId": "5e1760fbdfaf28038242d676",
"quantity": 5
},
{
"variantId": "5e17e67b73a34d53160c7252",
"quantity": 13
}
]
let bulkArr = [];
for (const i of input) {
bulkArr.push({
updateOne: {
"filter": { "_id": Mongoose.Types.ObjectId(i.variantId) },
"update": { $inc: { "stocks": - i.quantity } }
}
})
}
Variant.bulkWrite(bulkArr)
Ref : MongoDB-bulkWrite
I don't think this can be done with a single Model.updateMany query. You will need to loop the array and use Model.update instead.
for (const { variantId, quantity } of objects) {
Model.update({ _id: variantId }, { $inc: { stocks: -quantity } });
}
To run this in a transaction (https://mongoosejs.com/docs/transactions.html), the code should look something like this (however I have not tried or tested this):
mongoose.startSession().then(async session => {
session.startTransaction();
for (const { variantId, quantity } of objects) {
await Model.update({ _id: variantId }, { $inc: { stocks: -quantity } }, { session });
}
await session.commitTransaction();
});

How to insert multiple JSON document in elastic search

Input Data
[{
"_index": "abc",
"_type": "_doc",
"_id": "QAE",
"_score": 6.514091,
"_source": {
"category": "fruits",
"action": "eating",
"metainfo": {
"hash": "nzUZ1ONm0e167p"
},
"createddate": "2019-10-03T12:37:45.297Z"
}},
{
"_index": "abc",
"_type": "_doc",
"_id": "PQR",
"_score": 6.514091,
"_source": {
"category": "Vegetables",
"action": "eating",
"metainfo": {
"hash": "nzUZ1ONm0e167p"
},
"createddate": "2019-10-03T12:37:45.297Z"
}
}-----------------
----------------]
I have around 30,000 records as input data. How to insert this data in a single query. I tried by
var elasticsearch = require('elasticsearch');
var client = new elasticsearch.Client({
host: '********',
log: 'trace'
});
client.index({
index: "abc",
body: ****input data*****
}).then((res) => {
console.log(res);
}, (err) => {
console.log("err", err);
});
In this code, send input data in the body. but it returns an error. Please suggest to me.
This seems like what are you looking for:
'use strict'
require('array.prototype.flatmap').shim()
const { Client } = require('#elastic/elasticsearch')
const client = new Client({
node: 'http://localhost:9200'
})
async function run () {
await client.indices.create({
index: 'tweets',
body: {
mappings: {
properties: {
id: { type: 'integer' },
text: { type: 'text' },
user: { type: 'keyword' },
time: { type: 'date' }
}
}
}
}, { ignore: [400] })
const dataset = [{
id: 1,
text: 'If I fall, don\'t bring me back.',
user: 'jon',
date: new Date()
}, {
id: 2,
text: 'Winter is coming',
user: 'ned',
date: new Date()
}, {
id: 3,
text: 'A Lannister always pays his debts.',
user: 'tyrion',
date: new Date()
}, {
id: 4,
text: 'I am the blood of the dragon.',
user: 'daenerys',
date: new Date()
}, {
id: 5, // change this value to a string to see the bulk response with errors
text: 'A girl is Arya Stark of Winterfell. And I\'m going home.',
user: 'arya',
date: new Date()
}]
// The major part is below:
const body = dataset.flatMap(doc => [{ index: { _index: 'tweets' } }, doc])
const { body: bulkResponse } = await client.bulk({ refresh: true, body })
//
if (bulkResponse.errors) {
const erroredDocuments = []
// The items array has the same order of the dataset we just indexed.
// The presence of the `error` key indicates that the operation
// that we did for the document has failed.
bulkResponse.items.forEach((action, i) => {
const operation = Object.keys(action)[0]
if (action[operation].error) {
erroredDocuments.push({
// If the status is 429 it means that you can retry the document,
// otherwise it's very likely a mapping error, and you should
// fix the document before to try it again.
status: action[operation].status,
error: action[operation].error,
operation: body[i * 2],
document: body[i * 2 + 1]
})
}
})
console.log(erroredDocuments)
}
const { body: count } = await client.count({ index: 'tweets' })
console.log(count)
}
run().catch(console.log)
Reference link: https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/bulk_examples.html

Resources