Related
good day everyone, i am new here,
I have a response data looking like this
[
{
"Outlet": "Outlet1",
"Inventory": 12
},
{
"Outlet": "Outlet2",
"Inventory": 0
},
{
"Outlet": "Outlet3",
"Inventory": 3
},
{
"Outlet": "Outlet4",
"Inventory": 0
}
}
I need to verify if the outlet 1 inventory is exact 12, and every other data EXCEPT outlet1 inventory is 0. do i need to loop the test?
I’ve already tried:
pm.test("Inventory.OnHand Outlet1 == 12", () => {
let Outlet1Result = jsonData.find(a => a.Outlet === "Outlet1")
pm.expect(Outlet1Result.Outlet).to.eql("Outlet1")
pm.expect(Outlet1Result.Inventory).to.eql(12)
});
pm.test("Inventory.OnHand not Outlet1 == 0", () => {
if (jsonData.Outlet !== "Outlet1") {
jsonData.forEach(function() {
let result2 = jsonData.find(a => a.Outlet !== "Outlet1")
pm.expect(result2.Inventory).to.eql(0)
}) ;
}
});
I have tried using this 2 test, the first test worked just fine, but i think the second test is wrong because it’s passed, it should not be passed since outlet 3 inventory is 3, the text expect it to be 0
Other way would be :
pm.test("Validate inventory values", () => {
jsonData.forEach(function (item) {
if (item.Outlet !== "Outlet1") {
pm.expect(item.Inventory,
`Expected inventory value of ${item.Outlet} to be 0`).
to.
eql(0)
} else {
pm.expect(item.Inventory,
`Expected inventory value of ${item.Outlet} to be 12`).
to.
eql(12)
}
})
});
why can't you just compare the object as it is than individual fields ? as you are comparing exact values.
let expected = [
{
"Outlet": "Outlet1",
"Inventory": 12
},
{
"Outlet": "Outlet2",
"Inventory": 0
},
{
"Outlet": "Outlet3",
"Inventory": 3
},
{
"Outlet": "Outlet4",
"Inventory": 0
}
]
pm.expect(JSONdata).to.be.deep.eq(expected1, `Expected ${JSON.stringify(JSONdata,null,2)} to be equal to ${JSON.stringify(expected,null,2)}`)
I'm attempting to import data from Azure Application Insights into PowerBI. The issue is that, regardless of the timespan I set, I seem to only be pulling about a week's worth of data. Here's what the M query looks like:
let AnalyticsQuery =
let Source = Json.Document(Web.Contents("https://api.applicationinsights.io/v1/apps/<uuid>/query",
[Query=[#"query"="customEvents
| project customDimensions
",#"x-ms-app"="AAPBI",#"timespan"="P30D"],Timeout=#duration(0,0,60,0)])),
TypeMap = #table(
{ "AnalyticsTypes", "Type" },
{
{ "string", Text.Type },
{ "int", Int32.Type },
{ "long", Int64.Type },
{ "real", Double.Type },
{ "timespan", Duration.Type },
{ "datetime", DateTimeZone.Type },
{ "bool", Logical.Type },
{ "guid", Text.Type },
{ "dynamic", Text.Type }
}),
DataTable = Source[tables]{0},
Columns = Table.FromRecords(DataTable[columns]),
ColumnsWithType = Table.Join(Columns, {"type"}, TypeMap , {"AnalyticsTypes"}),
Rows = Table.FromRows(DataTable[rows], Columns[name]),
Table = Table.TransformColumnTypes(Rows, Table.ToList(ColumnsWithType, (c) => { c{0}, c{3}}))
in
Table
in
AnalyticsQuery
I was thinking this was a size issue, but I've already narrowed it down to a single column (albeit a wide one) and it's still not returning any more data.
Narrowing the returned dataset to two columns has increased the dataset to include a few weeks instead of less than a week, but I'm still looking for a bigger dataset. Here's the latest query:
let AnalyticsQuery =
let Source = Json.Document(Web.Contents("https://api.applicationinsights.io/v1/apps/<uuid>/query",
[Query=[#"query"="customEvents
| extend d=parse_json(customDimensions)
| project timestamp, d[""Properties""]
| order by timestamp desc
| where timestamp <= now() and d_Properties <> """"
",#"x-ms-app"="AAPBI"],Timeout=#duration(0,0,4,0)])),
TypeMap = #table(
{ "AnalyticsTypes", "Type" },
{
{ "string", Text.Type },
{ "int", Int32.Type },
{ "long", Int64.Type },
{ "real", Double.Type },
{ "timespan", Duration.Type },
{ "datetime", DateTimeZone.Type },
{ "bool", Logical.Type },
{ "guid", Text.Type },
{ "dynamic", Text.Type }
}),
DataTable = Source[tables]{0},
Columns = Table.FromRecords(DataTable[columns]),
ColumnsWithType = Table.Join(Columns, {"type"}, TypeMap , {"AnalyticsTypes"}),
Rows = Table.FromRows(DataTable[rows], Columns[name]),
Table = Table.TransformColumnTypes(Rows, Table.ToList(ColumnsWithType, (c) => { c{0}, c{3}}))
in
Table,
#"Sorted Rows" = Table.Sort(AnalyticsQuery,{{"timestamp", Order.Ascending}})
in
#"Sorted Rows"
You should look into either table buffering or directquery: see this discussion
I am new to AWS and dynamo. I working on my project with React.js front-end and AWS (Gateway API, Lambda, Dynamo) backend.
This is my app location:
https://www.alphaux.com
After I click "Get Hint", I receive server response. If I click on a keywords - these keywords will be added to the list of GET params for the request like:
topic=blah&keywords=blah1,blah2,blah3
Here are the details of my problem:
In my Lambda:
..
const docClient = new AWS.DynamoDB.DocumentClient({region: 'us-west-2'});
exports.handler = async (event) => { ..
from GET I am receiving the following params:
const topicName = event.queryParams.topic="React";
const keywords = event.queryParams.topic.keywords="blah1,blah2,blah3";
..some code here converts keywords string into array:
const keywordsArray = [blah1,blah2,blah3];
In dynamodb they exist in the following way:
[ { "S" : "javascript" }, { "S" : "programming" }, { "S" : "React" } ]
My Primary partition key is: id (Number)
I have tried different ways and approaches, used scan and query - nothing works. I am stuck..
I have tried the following approach:
const listToObjectMappings = () => {
let x = {};
/* keywords hardcoded for now: */
const keywords = ["javascript", "React"];
keywords.map(item => x[':' + item] = item)
return x
}
let mappings = listToObjectMappings()
let joined = Object.keys(mappings).join();
var params2 = {
TableName : "my-little-table",
FilterExpression: 'topic = :topic and #keywords IN (' + joined + ')',
ExpressionAttributeNames: {
'#keywords' : 'keywords'
},
ExpressionAttributeValues:{
":topic" : "React"
}
};
var params = {
TableName: "my-little-table",
FilterExpression: "#topic = :topic",
ExpressionAttributeNames: {
"#topic": "topic"
},
ExpressionAttributeValues: {
":topic": "React"
}
};
let result;
try {
/* scan DB */
result = await docClient.scan(params).promise();
}
catch(ex) {
result = ex;
}
When I use "scan", the result is always empty [] if I am trying to use "keywords".
It works only with bare minimum like:
const params = {
TableName : currentTable,
FilterExpression:'topic = :topic',
ExpressionAttributeValues:{
":topic" : requestedTopic
}
};
..which gives me all records based on requestedTopic.
If I am using "query" it complains that my key name ("id") is too short and has to be at least 3 chars long.
I am stuck and gracefully asking for your help!
Thank you!
If you're trying to match the topic and any one of the keywords then use something like this:
const params = {
TableName: 'mytable',
FilterExpression: '#tp = :tp AND (contains(#kw, :kw1) OR contains(#kw, :kw2))',
ExpressionAttributeNames: {
'#tp': 'topic',
'#kw': 'keywords',
},
ExpressionAttributeValues: {
':tp': 'React',
':kw1': 'react',
':kw2': 'react-router',
},
};
If you're trying to match the topic and all of the keywords then use something like this:
const params = {
TableName: 'mytable',
FilterExpression: '#tp = :tp AND contains(#kw, :kw1) AND contains(#kw, :kw2)',
ExpressionAttributeNames: {
'#tp': 'topic',
'#kw': 'keywords',
},
ExpressionAttributeValues: {
':tp': 'React',
':kw1': 'react',
':kw2': 'react-router',
},
};
I have a MongoDB collection of documents of the form
{
"id": 42,
"title": "candy can",
"description": "canada candy canteen",
"brand": "cannister candid",
"manufacturer": "candle canvas"
}
I need to implement auto-complete feature based on the input search term by matching in the fields except id. For example, if the input term is can, then I should return all matching words in the document as
{ hints: ["candy", "can", "canada", "canteen", ...]
I looked at this question but it didn't help. I also tried searching how to do regex search in multiple fields and extract matching tokens, or extracting matching tokens in a MongoDB text search but couldn't find any help.
tl;dr
There is no easy solution for what you want, since normal queries can't modify the fields they return. There is a solution (using the below mapReduce inline instead of doing an output to a collection), but except for very small databases, it is not possible to do this in realtime.
The problem
As written, a normal query can't really modify the fields it returns. But there are other problems. If you want to do a regex search in halfway decent time, you would have to index all fields, which would need a disproportional amount of RAM for that feature. If you wouldn't index all fields, a regex search would cause a collection scan, which means that every document would have to be loaded from disk, which would take too much time for autocompletion to be convenient. Furthermore, multiple simultaneous users requesting autocompletion would create considerable load on the backend.
The solution
The problem is quite similar to one I have already answered: We need to extract every word out of multiple fields, remove the stop words and save the remaining words together with a link to the respective document(s) the word was found in a collection. Now, for getting an autocompletion list, we simply query the indexed word list.
Step 1: Use a map/reduce job to extract the words
db.yourCollection.mapReduce(
// Map function
function() {
// We need to save this in a local var as per scoping problems
var document = this;
// You need to expand this according to your needs
var stopwords = ["the","this","and","or"];
for(var prop in document) {
// We are only interested in strings and explicitly not in _id
if(prop === "_id" || typeof document[prop] !== 'string') {
continue
}
(document[prop]).split(" ").forEach(
function(word){
// You might want to adjust this to your needs
var cleaned = word.replace(/[;,.]/g,"")
if(
// We neither want stopwords...
stopwords.indexOf(cleaned) > -1 ||
// ...nor string which would evaluate to numbers
!(isNaN(parseInt(cleaned))) ||
!(isNaN(parseFloat(cleaned)))
) {
return
}
emit(cleaned,document._id)
}
)
}
},
// Reduce function
function(k,v){
// Kind of ugly, but works.
// Improvements more than welcome!
var values = { 'documents': []};
v.forEach(
function(vs){
if(values.documents.indexOf(vs)>-1){
return
}
values.documents.push(vs)
}
)
return values
},
{
// We need this for two reasons...
finalize:
function(key,reducedValue){
// First, we ensure that each resulting document
// has the documents field in order to unify access
var finalValue = {documents:[]}
// Second, we ensure that each document is unique in said field
if(reducedValue.documents) {
// We filter the existing documents array
finalValue.documents = reducedValue.documents.filter(
function(item,pos,self){
// The default return value
var loc = -1;
for(var i=0;i<self.length;i++){
// We have to do it this way since indexOf only works with primitives
if(self[i].valueOf() === item.valueOf()){
// We have found the value of the current item...
loc = i;
//... so we are done for now
break
}
}
// If the location we found equals the position of item, they are equal
// If it isn't equal, we have a duplicate
return loc === pos;
}
);
} else {
finalValue.documents.push(reducedValue)
}
// We have sanitized our data, now we can return it
return finalValue
},
// Our result are written to a collection called "words"
out: "words"
}
)
Running this mapReduce against your example would result in db.words look like this:
{ "_id" : "can", "value" : { "documents" : [ ObjectId("553e435f20e6afc4b8aa0efb") ] } }
{ "_id" : "canada", "value" : { "documents" : [ ObjectId("553e435f20e6afc4b8aa0efb") ] } }
{ "_id" : "candid", "value" : { "documents" : [ ObjectId("553e435f20e6afc4b8aa0efb") ] } }
{ "_id" : "candle", "value" : { "documents" : [ ObjectId("553e435f20e6afc4b8aa0efb") ] } }
{ "_id" : "candy", "value" : { "documents" : [ ObjectId("553e435f20e6afc4b8aa0efb") ] } }
{ "_id" : "cannister", "value" : { "documents" : [ ObjectId("553e435f20e6afc4b8aa0efb") ] } }
{ "_id" : "canteen", "value" : { "documents" : [ ObjectId("553e435f20e6afc4b8aa0efb") ] } }
{ "_id" : "canvas", "value" : { "documents" : [ ObjectId("553e435f20e6afc4b8aa0efb") ] } }
Note that the individual words are the _id of the documents. The _id field is indexed automatically by MongoDB. Since indices are tried to be kept in RAM, we can do a few tricks to both speed up autocompletion and reduce the load put to the server.
Step 2: Query for autocompletion
For autocompletion, we only need the words, without the links to the documents.
Since the words are indexed, we use a covered query – a query answered only from the index, which usually resides in RAM.
To stick with your example, we would use the following query to get the candidates for autocompletion:
db.words.find({_id:/^can/},{_id:1})
which gives us the result
{ "_id" : "can" }
{ "_id" : "canada" }
{ "_id" : "candid" }
{ "_id" : "candle" }
{ "_id" : "candy" }
{ "_id" : "cannister" }
{ "_id" : "canteen" }
{ "_id" : "canvas" }
Using the .explain() method, we can verify that this query uses only the index.
{
"cursor" : "BtreeCursor _id_",
"isMultiKey" : false,
"n" : 8,
"nscannedObjects" : 0,
"nscanned" : 8,
"nscannedObjectsAllPlans" : 0,
"nscannedAllPlans" : 8,
"scanAndOrder" : false,
"indexOnly" : true,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"indexBounds" : {
"_id" : [
[
"can",
"cao"
],
[
/^can/,
/^can/
]
]
},
"server" : "32a63f87666f:27017",
"filterSet" : false
}
Note the indexOnly:true field.
Step 3: Query the actual document
Albeit we will have to do two queries to get the actual document, since we speed up the overall process, the user experience should be well enough.
Step 3.1: Get the document of the words collection
When the user selects a choice of the autocompletion, we have to query the complete document of words in order to find the documents where the word chosen for autocompletion originated from.
db.words.find({_id:"canteen"})
which would result in a document like this:
{ "_id" : "canteen", "value" : { "documents" : [ ObjectId("553e435f20e6afc4b8aa0efb") ] } }
Step 3.2: Get the actual document
With that document, we can now either show a page with search results or, like in this case, redirect to the actual document which you can get by:
db.yourCollection.find({_id:ObjectId("553e435f20e6afc4b8aa0efb")})
Notes
While this approach may seem complicated at first (well, the mapReduce is a bit), it is actual pretty easy conceptually. Basically, you are trading real time results (which you won't have anyway unless you spend a lot of RAM) for speed. Imho, that's a good deal. In order to make the rather costly mapReduce phase more efficient, implementing Incremental mapReduce could be an approach – improving my admittedly hacked mapReduce might well be another.
Last but not least, this way is a rather ugly hack altogether. You might want to dig into elasticsearch or lucene. Those products imho are much, much more suited for what you want.
Thanks to #Markus solution, I came up with something similar with aggregations instead. Knowing that map-reduce are flagged as deprecated for later versions.
const { MongoDBNamespace, Collection } = require('mongodb')
//.replace(/(\b(\w{1,3})\b(\W|$))/g,'').split(/\s+/).join(' ')
const routine = `function (text) {
const stopwords = ['the', 'this', 'and', 'or', 'id']
text = text.replace(new RegExp('\\b(' + stopwords.join('|') + ')\\b', 'g'), '')
text = text.replace(/[;,.]/g, ' ').trim()
return text.toLowerCase()
}`
// If the pipeline includes the $out operator, aggregate() returns an empty cursor.
const agg = [
{
$match: {
a: true,
d: false,
},
},
{
$project: {
title: 1,
desc: 1,
},
},
{
$replaceWith: {
_id: '$_id',
text: {
$concat: ['$title', ' ', '$desc'],
},
},
},
{
$addFields: {
cleaned: {
$function: {
body: routine,
args: ['$text'],
lang: 'js',
},
},
},
},
{
$replaceWith: {
_id: '$_id',
text: {
$trim: {
input: '$cleaned',
},
},
},
},
{
$project: {
words: {
$split: ['$text', ' '],
},
qt: {
$const: 1,
},
},
},
{
$unwind: {
path: '$words',
includeArrayIndex: 'id',
preserveNullAndEmptyArrays: true,
},
},
{
$group: {
_id: '$words',
docs: {
$addToSet: '$_id',
},
weight: {
$sum: '$qt',
},
},
},
{
$sort: {
weight: -1,
},
},
{
$limit: 100,
},
{
$out: {
db: 'listings_db',
coll: 'words',
},
},
]
// Closure for db instance only
/**
*
* #param { MongoDBNamespace } db
*/
module.exports = function (db) {
/** #type { Collection } */
let collection
/**
* Runs the aggregation pipeline
* #return {Promise}
*/
this.refreshKeywords = async function () {
collection = db.collection('listing')
// .toArray() to trigger the aggregation
// it returns an empty curson so it's fine
return await collection.aggregate(agg).toArray()
}
}
Please check for very minimal changes for your convenience.
My objective :I want to update multiple documents in a collection in a certain path with a condition that path matches a regex then search and replace with certain value in the path then finally save all those documents persistently in db.
example:
myCollection : [
{ doc1 : { summary : 'Summary 1 : one', value : 1 },
{ doc2 : { summary : 'Summaryyuist 2 : two', value : 1 },
{ doc3 : { summary : 'hello 3 : three', value : 3 },
];
now i want to replace all 'Summary' with 'hello' in path :'summary'
so the result after query should be :
myCollection : [
{ doc1 : { summary : 'hello 1 : one', value : 1 },
{ doc2 : { summary : 'helloyuist 2 : two', value : 1 },
{ doc3 : { summary : 'hello 3 : three', value : 3 },
];
I am just looking the query to be used above.
From here
http://mongoosejs.com/docs/api.html#query_Query-regex
I found no information how to implement. Specially what the 'Number' parameter does in regex method.?
Also from here :
http://mongoosejs.com/docs/api.html#query_Query-regex
Also i do not found the 'show code' link for monsoose regex. Can someone at least reply a link for mongoose regex code.
i find only how to find models, NOT how to update with replacement with a value that matches the regex in certain path of docs.
How to achieve my objective?
You could use mongoose Model.Update option i hope.
Model.update = function (query, doc, options, callback) { ... }
Example :
MyModel.update({ age: { $gt: 18 } }, { oldEnough: true }, fn);
MyModel.update({ name: 'Tobi' }, { ferret: true }, { multi: true }, function(err,numberAffected, raw) {
if (err) return handleError(err);
console.log('The number of updated documents was %d', numberAffected);
console.log('The raw response from Mongo was ', raw);
});
http://mongoosejs.com/docs/api.html#model_Model.update