How can I loop a sequelize response - javascript

If I make a request to a Postgres database with Sequelize, I get the next result:
[
Tags {
dataValues: { idtag: 18 },
_previousDataValues: { idtag: 18 },
_changed: {},
_modelOptions: {
...
},
_options: {
...
},
isNewRecord: false
},
Tags {
dataValues: { idtag: 19 },
_previousDataValues: { idtag: 19 },
_changed: {},
_modelOptions: {
...
},
_options: {
...
},
isNewRecord: false
},
Tags {
dataValues: { idtag: 20 },
_previousDataValues: { idtag: 20 },
_changed: {},
_modelOptions: {
...
},
_options: {
...
},
isNewRecord: false
}
]
I can't find the way to loop this to can get the 'idtag' values.
I tried to parse this to JSON with JSON.stringify() but it gives me a string. Also, I tried to loop this like it was an array but it didn't works too.

Let's assume that your output is stored to an array. Now using following piece of code you can collect idTag values.
const idTagVals = [];
array.forEach(element => {
idTagVals.push(element.dataValues.idtag);
});
console.log(idTagVals); //[ 18, 19, 20 ]

According to sequelize docs
TagsModel.findAll({where: {} ,plain:true});
Will return Array of plain objects.

And finilly I have the answer. First I converted the result with JSON.stringify() and then with JSON.parse().
let tags = await models.Tags.findAll({ attributes: ['idtag'], where: { iduser: iduser } });
let string = JSON.stringify(tags);
tags = JSON.parse(string);
And finally, to loop:
tags.forEach(async (tag) => {
...
});

Related

Javascript Neo4j Driver Json parse express api

This may be a non-issue however I can't help but feel like there is a better way to do what I am trying to achieve.
I am writing an API in Express with my data stored in a neo4j database.
I am using the official neo4j-driver to interface with neo4j which is running locally. When I run a query eg :
session
.run(`MATCH (foo:FamilyMember)-[:HAS_SISTER]->(sister:FamilyMember)
WHERE foo.firstName = 'bar'
RETURN sister.firstName AS Name, sister.lastName AS Surname
`)
.then(res => {
console.log(res);
})
Neo4j returns a response object with a lot of information about the request:
{
records: [
Record {
keys: [Array],
length: 2,
_fields: [Array],
_fieldLookup: [Object]
},
Record {
keys: [Array],
length: 2,
_fields: [Array],
_fieldLookup: [Object]
},
Record {
keys: [Array],
length: 2,
_fields: [Array],
_fieldLookup: [Object]
},
Record {
keys: [Array],
length: 2,
_fields: [Array],
_fieldLookup: [Object]
}
],
summary: ResultSummary {
query: {
text: 'MATCH (foo:FamilyMember)-[:HAS_SISTER]->(sister:FamilyMember)\n' +
" WHERE foo.firstName = 'bar'\n" +
' RETURN sister.firstName AS Name, sister.lastName AS Surname\n' +
' ',
parameters: {}
},
queryType: 'r',
counters: QueryStatistics { _stats: [Object], _systemUpdates: 0 },
updateStatistics: QueryStatistics { _stats: [Object], _systemUpdates: 0 },
plan: false,
profile: false,
notifications: [],
server: ServerInfo {
address: 'localhost:7687',
version: 'Neo4j/4.2.1',
protocolVersion: 4.2
},
resultConsumedAfter: Integer { low: 0, high: 0 },
resultAvailableAfter: Integer { low: 4, high: 0 },
database: { name: 'neo4j' }
}
}
This is a pain when really I what I want is either the actual response data as an array of objects or an error message if something fails.
I wrote this parse method to generate an array of Javascript object's with the data returned from the query:
function parseNeo4jResponseJSON(res) {
return results = res.records.reduce( (array, currentRecord) => {
const record = currentRecord.keys.reduce( (obj, key, index) => {
obj[key] = currentRecord._fields[index]
return obj
}, {})
array.push(record);
return array;
},[])
};
This works and now when I console log the query response through my parser I get it in the format I want eg:
[
{ Name: 'foo', Surname: 'bar' },
{ Name: 'foo2', Surname: 'bar2' },
...
]
Is this approach going to cause me problems down the line? Is there a better way to get a javascript object from the response? I am pretty new to neo4j. Apologies if the answer is obvious.
Based on the existing examples, what about:
session
.readTransaction((tx) =>
tx.run(`MATCH (foo:FamilyMember)-[:HAS_SISTER]->(sister:FamilyMember)
WHERE foo.firstName = 'bar'
RETURN sister.firstName AS Name, sister.lastName AS Surname`)
)
.then(results => results.records.map((record) => {
return {
Name: record.get('Name'),
Surname: record.get('Surname')
}
})
You could keep session.run, but the session.{read,write}Transaction variants are usually recommended because they work in every environment (where session.run may sometimes fail in a cluster environment).
Please also make sure to use a dictionary of parameters (2nd argument of tx.run) instead of using string interpolation, if your query needs to be parameterized. If the value of foo.firstName comes from a variable (let's say someFirstName), the tx.run would become:
tx.run("MATCH (foo:FamilyMember)-[:HAS_SISTER]->(sister:FamilyMember)
WHERE foo.firstName = $firstName
RETURN sister.firstName AS Name, sister.lastName AS Surname",
{firstName: someFirstName})

Query access variable outside query based on condition in NodeJS and MongoDB

I have a schema like below:
[
{
"_id": 1,
"showResult": true,
"subject": "History",
},
{
"_id": 2,
"showResult": false,
"subject": "Math",
}
]
and an object in JS like below:
result = {
"History": 22,
"Math": 18
}
I am using aggregate to process query, in between i need to find score based on subject field in the document if showResult field is true i.e to access result variable inside query as map result[$subject]
My query:
db.collection.aggregate([
{
"$project": {
_id: 1,
"score":{$cond: { if: { $eq: [ "$showResult", true ] }, then: subjectObj[$subject], else: null }}
}
}
])
can this be done in MongoDB, i want result like below:
{
_id: 1,
score: 22
}
I think query is little costly than JS code, but i am adding the query if it will help you as per your question,
$match showResult is true
$project to show required fields, $reduce to iterate loop of result after converting from object to array using $objectToArray, check condition if subject match then return matching score
let result = {
"History": 22,
"Math": 18
};
db.collection.aggregate([
{ $match: { showResult: true } },
{
$project: {
_id: 1,
score: {
$reduce: {
input: { $objectToArray: result },
initialValue: 0,
in: {
$cond: [{ $eq: ["$$this.k", "$subject"] }, "$$this.v", "$$value"]
}
}
}
}
}
])
Playground

Nested query on ElasticSearch

I have an elastic search index which is storing documents in the following way:
{
categorisedTags:
{ urlTags: { L: [] },
commodityTags: { L: [Array] },
tags: { L: [] } },
newOptions: [],
created_at: 'Mon, 07 Oct 2019 12:55:34 GMT',
name: 'Template ',
}
I need to query the index by 'commodityTags', so given a string, it should return all documents where the string is included in the commodityTags array.
I have tried with:
service.queryTags = async (index, values) => {
const { hits } = await esClient.search({
index,
type: '_doc',
body: {
query: {
term: {
'categorisedTags.commodityTags': 'oil'
}
},
},
});
return hits.hits.map(({ _source }) => _source);
};
But no luck, always returns 0 hits. How can I do this kind of nested queries on ES ?
Nested query can be created like below
Query
"query": {
"nested": {
"path": "categorisedTags",
"query": {
"bool": {
"must": [
{
"term": {
"categorisedTags.commodityTags": {
"value": "oil"
}
}
}
]
}
},
"inner_hits": {}
}
}

Using find and aggregate on mongoDB/Mongoose to fetch data

I'm working on a node.js project to display some data using charts and tables on the front end.
I have the two following queries on my route:
atendimentos.find({})
.then(atendimentos => {
final = atendimentos.filter(atendimentos => atendimentos.status === 'F')
testeea = atendimentos.filter(atendimentos => atendimentos.status === 'EA')
res.render('home', {user: req.user, fin: final.length, ea: testeea.length});
//Funciona
console.log(final.length)
})
.catch(err => console.error(err));
atendimentos.aggregate([
{ $project:
{ _id: "$month",
year: {$year: "$date" },
month: { $month: "$date"},
amount: 1
}
},
{ $group:
{ _id: { year: "$year", month: ("$month")},
sum: { $sum: 1}
}
}]).exec(function(error, items){
if(error){return next(error);}
console.log(items);
});
EDIT 1:
So, the input data... I guess that I don't have any because I'm actually fetching everything from the database through my queries. The data that I expect, are the documents/object with the status F or EA which I'm rendering on my chart.
The database has around 8.5k documents, the F one returns 8041 documents and the EA returns 351, it is a simple array with the number that is returned using .length on my route. Those numbers are rendered on the chart.
Now, related to the aggregation part, I'm trying to make a table using the collection. I intend to show the number of support calls (atendimentos) per month. It's actually logging the correct data like this:
[ { _id: { year: 2018, month: 6 }, sum: 4005 },
{ _id: { year: 2018, month: 7 }, sum: 43 },
{ _id: { year: 2018, month: 5 }, sum: 3996 },
{ _id: { year: 2018, month: 4 }, sum: 434 } ]
And I want to use this data to render the table on my view.
END OF EDIT 1
EDIT 2
router.get('/home', isAuthenticated, async (req, res, next) => {
let final;
let testeea;
atendimentos.find({})
.then(atendimentos => {
final = atendimentos.filter(atendimentos => atendimentos.status === 'F')
testeea = atendimentos.filter(atendimentos => atendimentos.status === 'EA')
res.render('home', {user: req.user, fin: final.length, ea: testeea.length});
//Funciona
console.log(final.length)
})
.catch(err => console.error(err));
So, here's the route, the other part is just the aggregation query that I've tried to do and closing brackets. As you can see, I get the data and use Array.filter to filter the results fetched, using status = F or = EA.
It returns me the length of the array, so it counts the number of status with each letter. This number is rendered in the chart, because I'm sending it to the front end as fin: final.length and ea: testeea.length. No formatted data or something like that in here. It's okay this way.
Related to the aggregation part where it returns the calls per month, I want to use just the number of calls, month and year. In this part I expected the data like: [ { _id: { year: 2018, month: 6 }, sum: 4005 }
I wish I could fetch the data the same way as I've fetched the fin and ea, using .length to count and put it into the view.
END OF EDIT 2
Both are returning exactly what I need, the problem is that I can't just put the aggregation query before the find query and add items: items to the render method. I would like to know how do I do these queries to display the same that that I'm fetching on these two queries. Thanks in advance!
MongoDB Server 3.2 and below
You need to run two aggregate queries and merge the objects in the results. This can be done in a multiple ways but can show you the Promise way and the async/await approach.
1. Using Promises
router.get('/home', isAuthenticated, (req, res, next) => {
const counts = atendimentos.aggregate([
{ '$group': {
'_id': null,
'fin': {
'$sum': {
'$cond': [ { '$eq': [ '$status', 'F' ] }, 1, 0 ]
}
},
'ea': {
'$sum': {
'$cond': [ { '$eq': [ '$status', 'EA' ] }, 1, 0 ]
}
}
} }
]).exec();
const monthly = atendimentos.aggregate([
{ '$group': {
'_id': {
'year': { '$year': '$date' },
'month': { '$month': '$date' }
},
'sum': { '$sum': 1 }
} },
{ '$group': {
'_id': null,
'back': { '$push': '$$ROOT' }
} },
]).exec();
Promise.all([ counts, monthly ]).then(([ counts, monthly ]) => {
const statusData = counts[0];
const monthlyData = monthly[0];
const data = {...statusData, ...monthlyData, user: req.user};
console.log(JSON.stringify(data, null, 4));
res.render('home', data);
}).catch(err => next(err));
});
2. Using async/await
router.get('/home', isAuthenticated, async (req, res, next) => {
try {
const counts = await atendimentos.aggregate([
{ '$group': {
'_id': null,
'fin': {
'$sum': {
'$cond': [ { '$eq': [ '$status', 'F' ] }, 1, 0 ]
}
},
'ea': {
'$sum': {
'$cond': [ { '$eq': [ '$status', 'EA' ] }, 1, 0 ]
}
}
} }
]).exec();
const monthly = await atendimentos.aggregate([
{ '$group': {
'_id': {
'year': { '$year': '$date' },
'month': { '$month': '$date' }
},
'sum': { '$sum': 1 }
} },
{ '$group': {
'_id': null,
'back': { '$push': '$$ROOT' }
} },
]).exec();
const statusData = counts[0];
const monthlyData = monthly[0];
const data = {...statusData, ...monthlyData, user: req.user};
console.log(JSON.stringify(data, null, 4));
res.render('home', data);
} catch (err) {
next(err);
}
});
MongoDB Server 3.4.4 and above
The aggregation pipeline can also handle filtering, you just need to use the $facet pipeline step which is capable of processing multiple aggregation pipelines within a single stage on the same set of input documents. Each sub-pipeline has its own field in the output document where its results are stored as an array of documents.
Consider running the following pipeline:
atendimentos.aggregate([
{ '$facet': {
'counts': [
{ '$group': {
'_id': null,
'fin': {
'$sum': {
'$cond': [ { '$eq': [ '$status', 'F' ] }, 1, 0 ]
}
},
'ea': {
'$sum': {
'$cond': [ { '$eq': [ '$status', 'EA' ] }, 1, 0 ]
}
}
} }
],
'monthly': [
{ '$group': {
'_id': {
'year': { '$year': '$date' },
'month': { '$month': '$date' }
},
'sum': { '$sum': 1 }
} },
{ '$group': {
'_id': null,
'items': { '$push': '$$ROOT' }
} },
]
} },
{ '$replaceRoot': {
'newRoot': {
'$mergeObjects': {
'$concatArrays': ['$counts', '$monthly']
}
}
} }
]).exec((err, results) => {
const data = results[0];
console.log(data);
res.render('home', { user: req.user, ...data });
})

Loopback findOrCreate creates new records even if there is an existing record

As per my understanding of the loopback documentation Persistedmodel.findOrCreate should find a model according to the query and return it, or create a new entry in the database if the model does not exist.
What I have noticed in my case is that it creates a new entry irrespective of whether there is an existing entry.
Not sure what I am missing. Here is my code:
teams-joined.json
{
"name": "teamsJoined",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"teamID": {
"type": "string",
"required": true
},
"playerID":{
"type":"string",
"required":true
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": {}
}
teams-joined.js
let queryThatWorks = {where:{
and: [
{teamID: teamID}
]
}
};
let query = {where:{
and: [
{teamID: teamID},
{playerID: req.currentUser.id},
],
}
};
let joinTeamsData = {
teamID: teamID,
playerID: req.currentUser.id,
};
console.log(query.where,'query');
teamsJoined.findOrCreate(query, joinTeamsData,
function(err, data, created) {
console.log(data,created);
});
When I cal the API multiple times, this is what I get
{ and:
[ { teamID: 'bf36e0-93a5-11e8-a8f4-9d86f4dd79ee' },
{ playerID: '5b20887bb6563419505c4590' } ] } 'query'
{ teamID: 'bf36e0-93a5-11e8-a8f4-9d86f4dd79ee',
playerID: '5b20887bb6563419505c4590',
id: 5b61798534fa410d2b1d900a } 'data'
true 'created'
{ and:
[ { teamID: 'bf36e0-93a5-11e8-a8f4-9d86f4dd79ee' },
{ playerID: '5b20887bb6563419505c4590' } ] } 'query'
{ teamID: 'bf36e0-93a5-11e8-a8f4-9d86f4dd79ee',
playerID: '5b20887bb6563419505c4590',
id: 5b61798634fa410d2b1d900b } 'data'
true 'created'
I expected it to return false for created and just return the existing data.
This works fine when I use the 'queryThatWorks' query from my code sample.
You don't need to use and operator in your where clause. See findOrCreate.
{ where: { teamID: teamID, playerID: req.currentUser.id } }
You can include your data into your filter like that:
{ where: joinTeamsData }
To return specific fields in your return statement, you can use the fields option in your query.
Finally, try this:
let data = {
teamID: teamID,
playerID: req.currentUser.id,
};
let filter = { where: data };
teamsJoined.findOrCreate(filter, data,
function(err, instance, created) {
console.log(instance, created);
}
);

Categories