How to organize conditionally created gremlin query (JavaScript) - javascript

I am a beginner in gremlin with TinkerPop and have this query in JavaScript. It works fine. Since this question is not related to the query but rather how to structure it, so I don't think a graph is be needed for this.
This query solves two questions:
What restaurant near me with a specific cuisine is the highest rated?
-- set cuisines, queryOrder, limit=1 and restaurantFilter
Which restaurants are the ten highest-rated restaurants near me?
-- set limit=10 and queryOrder
// usuals
let dc = new DriverRemoteConnection(`ws://localhost:8182/gremlin`, {});
const graph = new Graph();
const __ = gremlin.process.statics;
const p = gremlin.process.P;
const order = gremlin.process.order;
const g = graph.traversal().withRemote(dc);
// limit for objects returned.
const limit: number | undefined = undefined;
// order on reviews
const queryOrder = order.desc;
// restaurant filters
const restaurantFilter = {location: "x"}
//26, 32
const cuisines = [26];
function convertObjectToGremlinPropertyFilter(
data: Record<string, any>,
g: gremlin.process.GraphTraversal
) {
return Object.keys(data).forEach((key) => {
g.has(key, data[key]);
});
}
async function init() {
const result = g.V().hasLabel("restaurant");
convertObjectToGremlinPropertyFilter(restaurantFilter, result);
const reviewQuery = __.inE("review").has("value", p.gte(2));
if (order) {
reviewQuery.order().by("value", order.desc);
}
reviewQuery.valueMap().fold();
if (cuisines.length) {
console.log("Filtering cuisines");
result.where(
__.outE("serves")
.inV()
.hasLabel("cuisine")
.hasId(p.within(...cuisines))
);
}
result
.project("restaurants", "reviews")
.by(
__.project("info", "cuisines")
.by(__.valueMap(true))
.by(
__.outE("serves").unfold().inV().hasLabel("cuisine").valueMap().fold()
)
)
.by(reviewQuery);
if (limit) {
result.limit(limit);
}
const dataRaw = await result.toList();
await dc.close();
const data = JSON.stringify(normalizeData(dataRaw as any), null, 2);
fs.writeFileSync("./data.json", data);
}
For now, I've hardcoded the limit, queryOrder, and cuisines array. And creating the query on the condition applied to them.
As you can see, this query is kind of hard to maintain and interpret what it is doing. I've done the research and didn't find any resources related to structuring conditional queries. How can I structure these kinds of queries?
Original Query:
g.V()
.hasLabel("restaurant")
.has("location", "karachi")
// Check for cuisines
.where(
__.outE("serves")
.inV()
.hasLabel("cuisine")
.hasId(p.within(id1,id2))
)
// build the object
.project("info", "cuisines", "reviews")
.by(__.elementMap())
.by(__.outE("serves").inV().hasLabel("cuisine").elementMap().fold())
// Get reviews which are greater than 1 and order by highest review
.by(
__.inE("review")
.has("value", p.gte(1))
.order()
.by("value", queryOrder)
.valueMap(true)
.fold()
)
.limit(10)
.toList();
Thanks in advance for your answer!

Related

I want to get multiple types as query parameters by using .split(',') in WHERE IN()

I want to get multiple types as query parameters by using type.split(',') in WHERE IN().
I have service.js and DAO.js files. And I tried this like below.
service.js
const productTypeSort = async (name, type, productSort, page) => {
const sort = orderBy(productSort);
const categoryType = getCategoryType(name, type);
return await productsDao.productTypeSort(categoryType, sort, page);
};
const getCategoryType = (name, type) => {
const filter = type.toString().split(',')
const FilterType = {
category: `WHERE c.name = "${name}"`,
categoryType: `WHERE c.name = "${name}" AND t.name IN ("${filter}")`,
};
if (name && type) {
return FilterType.categoryType;
} else if (name) {
return FilterType.category;
}
return '';
};
DAO.js
const productTypeSort = async (categoryType, sort, page) => {
const products = await myDataSource.query(
`SELECT
c.name AS category,
t.name AS type,
pr.name,
pr.description,
price_origin,
pr.created_at,
count(*) OVER() AS totalCount
FROM products pr
JOIN category c ON c.id = pr.category_id
JOIN product_types t ON t.id = pr.type_id
${categoryType}
GROUP BY pr.id
${sort}
LIMIT ?, 9`,
[(page - 1) * 9]
);
return products;
};
and the query I sent is as follows.
?name=abc&type=aaa,bbb&sort=review&page=1
But this returns an empty data column.
{
"data": []
}
What am I doing wrong? Maybe the location of .split(',') is wrong? I need help.
Your problem is that your filter array is ["aaa,bbb"] and that ends up making a query string which looks like:
WHERE t.name IN ("aaa,bbb")
What you need is to map each value in filter to a single-quote enclosed string, and remove the double-quotes from the query:
filter = type.split(',').map(s => `'${s}'`)
// ...
categoryType: `WHERE c.name = "${name}" AND t.name IN (${filter})`,
This will give you something that looks like:
WHERE t.name IN ('aaa','bbb')
which should give you your desired result.
You should take note of what #Andreas referred to in their comment; you should preferably be using prepared statements rather than injecting user input directly into your query. If someone sent type="); DROP table products --
you would get a nasty surprise.

how to sort data by geohash in firestore v9 and how does querying geohash work

How do I sort data by geohash in firestore v9? V8 docs give this example:
const bounds = geofire.geohashQueryBounds(center, radiusInM);
const promises = [];
for (const b of bounds) {
const q = db.collection('cities')
.orderBy('geohash')
.startAt(b[0])
.endAt(b[1]);
promises.push(q.get());
}
// Collect all the query results together into a single list
Promise.all(promises).then((snapshots) => {
const matchingDocs = [];
for (const snap of snapshots) {
for (const doc of snap.docs) {
const lat = doc.get('lat');
const lng = doc.get('lng');
// We have to filter out a few false positives due to GeoHash
// accuracy, but most will match
const distanceInKm = geofire.distanceBetween([lat, lng], center);
const distanceInM = distanceInKm * 1000;
if (distanceInM <= radiusInM) {
matchingDocs.push(doc);
}
}
}
return matchingDocs;
I do not know how to translate it to V9, I have tried like so:
const { latitude, longitude } = location?.coords || {};
if (latitude && longitude) {
const radiusInM = 50 * 100000;
const center = [latitude, longitude];
const bounds = geofire.geohashQueryBounds(center, radiusInM);
for (const b of bounds) {
const q = query(
collection(firestore, "laboratories"),
orderBy("geoHash"),
startAt(b[0]),
endAt(b[1])
);
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
console.log(doc.id, " => ", doc.data());
});
}
}
But it does not return any results, how does those queries work, because in the bounds results when I set radius to 50 * 100 I get 4 arrays with hashes, I assume those are points on a map
0: (2) ['u3j', 'u3k']
1: (2) ['u3n', 'u3p']
2: (2) ['u3m', 'u3n']
3: (2) ['u3q', 'u3r']
however when I increase the search radius to 50 * 100000 I get only
0: (2) ['h', '~']
I do not understand where do I make mistake, one of those locations is intentionally very close to where I am. And assuming this search was successful, would it return just locations in area or would those be sorted? Perhaps should I use some additional library to for that? Would you recommend one, please? And should I store latitude and longitude together with geohashes or this would be enough?
Posting Frank's comments for visibility.
Your translation to the v9 syntax looks fine, but you have a typo in the field name here: orderBy("geoHash"), should be replaced with orderBy("geohash"), with a lowercase h in hash.
The geohash field is a string value, not a specific data type. You may want to check the article about geohashes or watch a video about it.
To reiterate, the issue in your code here is that you mistyped the field name in your second/v9 query, as you spelled geoHash differently than in the first/v8 query. Even if you remove the startAt/endAt clauses, you'll get no results, as no document has a field with that misspelled name.

Looping through a JSON array and push the values into an array variable using node.js

I'm trying to create a chatbot in Dialogflow using node.js. The training phrases will come from a JSON file.
example JSON:
{
"Training_Phrases": ["How can I help you?", "How can I assist you today?"]
}
The Training phrases is being added on the intent created but the two phrases are merged in a single line instead of being two separate phrases.
Here is the current code that I have. The inputPhrases obj is the JSON file:
async function createIntent(inputPhrases) {
const displayName = ('Intent: ' + intentName);
//create training phrases
const trainingPhrases_Parts = [inputPhrases];
const trainingPhrases = [];
trainingPhrases_Parts.forEach(Phrases_Part => {
const part = {
text: Phrases_Part,
};
// Here we create a new training phrase for each provided part.
const trainingPhrase_con = {
parts: [part],
};
console.log(part);
trainingPhrases.push(trainingPhrase_con);
});
//CONSTRUCT THE INTENT
const intent = {
displayName: displayName,
trainingPhrases: trainingPhrases,
messages: [message],
};
const emptyArray = [];
Training_Phrased.forEach((text)=>{emptyArray.push(text)})
Also from your current code, what you're trying to achieve is not to clear but at this point you are pushing the whole object into the array and not the property of the object "parts" which actually stores the array.
trainingPhrases.push(trainingPhrase_con);
This should be trainingPhrases.push(trainingPhrase_con.parts); If you're are trying to store the parts in the trainingPhrases array.

Get cypress database query output objects in to variables

I have a cypress test which has been set up with mysql node module. When I run bellow mentioned test Its giving output as follows.
const executeQuery = (query) => {
cy.task('DBQuery', query).then(function (recordset) {
var rec = recordset
cy.log(rec)
})
}
Query:
select *
from Users
where email = 'sheeranlymited#lymitedtest.com'
OUTPUT: log [Object{23}]
Query:
select firstname
from Users
where email = 'sheeranlymited#lymitedtest.com'
OUTPUT: log [{firstname: Edward}]
instead of cy.log(rec) I want to get the output of 23 columns to assign in to different variables based on the column name.
Appreciate if someone can help me to resolve this...
You can use Object.values in js to retrieve values from your object
Let's say you need to extract the value of the 3rd column, so your code will look like,
cy.task('DBQuery', query).then(function (recordset) {
var rec = recordset
const results = Object.values(rec[0])
// results[index of the column] will output the results
cy.log(results[3])
})
We can do a small modification to make your task easier,
cy.task('DBQuery', query).then(function (recordset) {
var rec = recordset
const Values = Object.values(rec[0]);
const keys = Object.keys(rec[0]);
let result = {};
let index = 0;
keys.forEach(key => {
result[keys[index]] = Values[index];
i++
})
//result.firstName will give you your results
cy.log(result.firstName);
})
In this way, we are generating key-value pairs having the key as the column name. So you can use the column name to find the value.
Hope this helps.
cheers.

Writing a query parser in javascript

I'm trying to write a parser that supports the following type of query clauses
from: A person
at: a specific company
location: The person's location
So a sample query would be like -
from:Alpha at:Procter And Gamble location:US
How do i write this generic parser in javascript. Also, I was considering including AND operators inside queries like
from:Alpha AND at:Procter And Gamble AND location:US
However, this would conflict with the criteria value in any of the fields (Procter And Gamble)
Use a character like ";" instead of AND and then call theses functions:
var query = 'from:Alpha;at:Procter And Gamble;location:US';
var result = query.split(';').map(v => v.split(':'));
console.log(result);
And then you'll have an array of pairs, which array[0] = prop name and array[1] = prop value
var query = 'from:Alpha;at:Procter And Gamble;location:US';
var result = query.split(';').map(v => v.split(':'));
console.log(result);
Asuming your query will always look like this from: at: location:
You can do this:
const regex = /from:\s*(.*?)\s*at:\s*(.*?)\s*location:\s*(.*)\s*/
const queryToObj = query => {
const [,from,at,location] = regex.exec(query)
return {from,at,location}
}
console.log(queryToObj("from:Alpha at Betaat: Procter And Gamble location: US"))
However, adding a terminator allow you to mix order and lowering some keywords:
const regex = /(\w+):\s*(.*?)\s*;/g
const queryToObj = query => {
const obj = {}
let temp
while(temp = regex.exec(query)){
let [,key,value] = temp
obj[key] = value
}
return obj
}
console.log(queryToObj("from:Alpha at Beta;at:Procter And Gamble;location:US;"))
console.log(queryToObj("at:Procter And Gamble;location:US;from:Alpha at Beta;"))
console.log(queryToObj("from:Alpha at Beta;"))

Categories

Resources