LEFT JOIN in Azure CosmosDB SQL API - azure

I have used JOIN in Azure Cosmos DB collection using SQL API to query documents.
I have two contact documents, one is with property Address and another one is without address.
I need to get the address list of all the persons(including the persons who is not having any address). I have used the below query to do this.
But it gives the person list who is having address.
Is there any way to do LEFT JOIN?
Query:
SELECT base.FirstName, base.LastName, Address.City FROM ContactPerson
base JOIN Address IN base.Address
Sample Document:
[
{
"FirstName": "Saravana",
"LastName": "Kumar",
"Address": [
{
"City": "aaaa"
},
{
"City": "bbbb"
}
]
},
{
"FirstName": "Jayanth",
"LastName": "T"
}
]
Expected output:
[
{
"FirstName": "Saravana",
"LastName": "Kumar",
"City": "aaa"
},
{
"FirstName": "Saravana",
"LastName": "Kumar",
"City": "bbbb"
},
{
"FirstName": "Jayanth",
"LastName": "T"
}
]
Actual Output:
[
{
"FirstName": "Saravana",
"LastName": "Kumar",
"City": "bbbb"
},
{
"FirstName": "Saravana",
"LastName": "Kumar",
"City": "bbbb"
}
]

As i know, left join is not supported by cosmos db so far,you could vote up this thread.
As workaround,i suggest you using 2 different sql in stored procedure, then merge the results in it.
1.SELECT base.FirstName, base.LastName FROM ContactPerson base where NOT IS_DEFINED(base.Address)
2.SELECT base.FirstName, base.LastName, Address.City FROM ContactPerson base JOIN Address IN base.Address
SP:
function sample() {
var collection = getContext().getCollection();
var array = [];
var isAccepted = collection.queryDocuments(
collection.getSelfLink(),
'SELECT base.FirstName, base.LastName, Address.City FROM ContactPerson base JOIN Address IN base.Address',
function (err, feed, options) {
if (err) throw err;
if (!feed || !feed.length) {
var response = getContext().getResponse();
response.setBody('no docs found');
}
else {
array.push(feed);
}
});
var isAccepted1 = collection.queryDocuments(
collection.getSelfLink(),
'SELECT base.FirstName, base.LastName FROM ContactPerson base where NOT IS_DEFINED(base.Address)',
function (err, feed1, options) {
console.log(222)
if (err) throw err;
if (!feed1|| !feed1.length) {
var response = getContext().getResponse();
response.setBody('no docs found');
}
else {
array.push(feed1);
}
});
var response = getContext().getResponse();
response.setBody(array);
if (!isAccepted) throw new Error('The query was not accepted by the server.');
}
You could adjust the format of output as you want.

You can simulate LEFT JOIN with the EXISTS sentence. Eg:
SELECT VALUE c
FROM c
WHERE (
--Like a "Left Join SomeCollection"
(NOT IS_DEFINED(c.SomeCollection) OR c.SomeCollection = null)
OR EXISTS (
SELECT null
FROM s IN c.SomeCollection
WHERE s.PropertyName = 'SomeValue'
)
)
--AND/OR Some other c Node conditions

then don't join. you can directly access the Address field. ex:
SELECT base.FirstName, base.LastName, Address.City FROM ContactPerson base
it will just display null or empty string if it has no property

Related

Azure Cosmos DB SQL API - #Query

I have a Azure Cosmos DB Container 'RouteZipcodes' container. It will have json documents with each route and zipcodes that each route is serving. Below is the sample data stored in cosmos DB
{
"id": "2347z3e8-850d-364a-92d2-4fcae2fa5642",
"routeName": "THORN1",
"zips": [
{ "zipCode": "80373" },
{ "zipCode": "80371" },
{ "zipCode": "80020" },
{ "zipCode": "80370" },
{ "zipCode": "80021" },
{ "zipCode": "80040" },
{ "zipCode": "80602" },
{ "zipCode": "80372" }
],
"status": "A"
}
When I execute below query from portal:
SELECT r.routeName as routeName
FROM routeZipcodes r
JOIN rz in r.zips
where rz.zipCode = "80602"
Below is result:
[ { "routeName": "THORN1" } ]
When I am trying to run same query from Spring boot app, and am not getting any result. Below is my respository
#Repository
public interface RouteZipcodesRepository extends CosmosRepository<RouteZipcodes, String> {
Optional<RouteZipcodes> findById(String id);
Optional<List<RouteZipcodes>> findByHubName(String hubName);
Optional<RouteZipcodes> findByRouteName(String routeName);
// #Query("SELECT r.routeName FROM routeZipcodes r JOIN rz in r.zips where rz.zipCode = \"#zipcode\"")
// String findByZipCode (#Param("zipcode") String zipcode);
#Query("SELECT r.routeName as routeName FROM routeZipcodes r JOIN rz in r.zips where rz.zipCode = \"#zipcode\"")
List<IRouteNameByZipcode> findByZipCode (#Param("zipcode") String zipcode);
}
other methods are working as expected. Only query written with #query is not giving results when the same was working in portal.

MongoDB $and query only works on Strings

I want to filter out data based on search criteria in mongoDb.
Here is the query:
exports.getParkingListByCriteria = async (req, res) => {
const cityQuery = req.body.city;
const stateQuery = req.body.state;
const countryQuery = req.body.country;
const zipQuery = req.body.zipCode;
try {
const filter = await Parking.find({
$and: [
{
"location.city": { $regex: new RegExp(cityQuery, ($options = "i")) },
"location.state": { $regex: new RegExp(stateQuery, ($options = "i"))},
"location.country": { $regex: new RegExp(countryQuery, ($options = "i"))},
"location.zipCode": zipQuery,
},
],
});
res.status(200).send(filter);
} catch (error) {
return res.status(500).json({ error: error.message });
}
};
City, state and country are stored as a String and zipCode is stored as a Number in mongoose model.
city, state and country filter was just working fine. It was giving me an intended results but then I added a zipCode query and now city, state and countries filter is also giving an empty array.
request I am sending from postman:
{
"city": "edmund",
"state": "lousinia",
"country":"australia",
"zipCode": 49755
}
All are stored in a one collection like shown below:
{"_id": {"$oid": "62cc46e920782c4be0673d50"},
"merchantId": {"$oid": 62c950ebc96c2b690028be8b"},
"contactInfo": {"name": "Ronda Green", "phoneNumber": 9104933588},
"about": "Laborum non minim ad",
"location":
{"address": "349 scott avenue",
"city": "edmund",
"state": "louisiana",
"zipCode": 49755,
"country": "australia"},
"price": 18,
"parkingType": "parkingLot",
"parkingInfo": [{"parkingName": "College Place","_id":"$oid":"62cc46e920782c4be0673d51"},"default": []}],
"totalSpots": [168],
"parkingSpotType": ["Motorbike","Large"],
"coordinates":
{"lng": 1.522645,
"lat": 125.939061},
"status": "active",
"isFeePaid": false,
"availability": [],
"specialEvents": [],
"__v": 0}
Before the introduction of zipQuery to the code every individual request was working perfectly fine (Like if I pass a query { "city": "edmund" } it would give me above result) but after the zipQuery it just got messy.
The result of console.log(zipQuery) is 49755

Retrieve a specific level level from a JSON object in Node.js

I have a two level JSON
{"Policy": {
"Channel": "online",
"Credit Score": "20000",
"Car": [{
"Age": "28",
"AnnualMiles": "15000",
"CarAge": "3",
"Young Driver": "1"
}
]
}}
i am trying to change the json structure and only retrieve the root policy object from the JSON
let data = JSON.parse(json);
console.log(data)
policy=data.Policy
console.log(policy)
The output that i am getting is the entire JSON basically, the result that i want is just the root level values :
{
"Channel": "online",
"Credit Score": "20000"
}
How do i only retrieve the root level in nodejs?
var json = {"Policy": {
"Channel": "online",
"Credit Score": "20000",
"Car": [{
"Age": "28",
"AnnualMiles": "15000",
"CarAge": "3",
"Young Driver": "1"
}
]
}}
//Filter function for objects
Object.filter = (obj, predicate) =>
Object.keys(obj)
.filter( key => predicate(obj[key]) )
.reduce( (res, key) => (res[key] = obj[key], res), {} );
//Exclude objects
let result = Object.filter(json.Policy, x => typeof x != "object")
console.log(result)
Output:
{
Channel: "online",
Credit Score: "20000"
}

Azure Cosmos DB add related entities to the query result

In my cosmos DB collection I have entities of such type:
// Suppliers:
[{
"id": "some_unique_str",
"products": [
"id_of_product1",
"id_of_product2"
]
}]
// Products:
[{
"id": "id_of_product1",
"name": "product name"
},
//...
]
I need to write a query to get such result:
[{
"id": "some_unique_str",
"products": [
{
"id": "id_of_product1",
"name": "product 1 name"
},
{
"id": "id_of_product2",
"name": "product 2 name"
}
]
}]
In other words: I'm trying to achieve OData expand functionality.
Is it possible?
You can't achieve that with direct sql in cosmos db sql api. Your needs can be implemented with foreign key in relational database,not no-sql database.
In cosmos db sql api,you could achieve that with stored procedure.I tried to write some code for your reference:
function sample(prefix) {
var collection = getContext().getCollection();
var isAccepted = collection.queryDocuments(
collection.getSelfLink(),
'SELECT c.id,c.products FROM c where is_defined(c.products)',
function (err, feed, options) {
if (err) throw err;
if (!feed || !feed.length) {
var response = getContext().getResponse();
response.setBody('no docs found');
}
else {
var response = getContext().getResponse();
var returnArray = [];
for (var i=0;i<feed.length;i++){
var map = {};
map['id'] = feed[i].id;
mergeData(map,feed[i].products);
returnArray.push(map);
}
response.setBody(returnArray);
}
});
if (!isAccepted) throw new Error('The query was not accepted by the server.');
function mergeData(map,idArray){
var sqlQuery = {
"query": 'SELECT c.id,c.name FROM c where not is_defined(c.products) and '+
' array_contains( #idArray,c.id,true) ',
"parameters": [
{"name": "#idArray", "value": idArray}
]
}
var isAccepted = collection.queryDocuments(
collection.getSelfLink(),
sqlQuery,
function (err, feed, options) {
if (err) throw err;
if (!feed || !feed.length) {
map['products'] = [];
}
else {
map['products'] = feed;
}
});
}
}
Output with your sample data is:

find object inside JSON using nested ID

i have a mongo collection like this
{"stores": [{"name": "foo",
"songs": [ {"id": "", "name": "", "artist": "", "category": "", "tags": []} ],
"songsSchedule": [
{
"song_id": "",
"date": ,
"user": "",
"help": ,
"partners": [{"user": ""}],
"likes":
}
]
}]}
and i want to get the songs name and artist from the songsSchedule song_id, i've tried this but it's not working
var query = { _id: fn.generateID(req.params.store_id), songsSchedule: { $exists: true } };
var select = { songsSchedule:1 };
var array = [];
client("stores", function(err, collection) {
if (err)
return res.json(fn.status("30"));
collection.findOne(query, select, function(err, store) {
if (err || !store)
return res.json(fn.status("31"));
for (var i in store.songsSchedule) {
var song = store.songsSchedule[i];
array.push(song.song_id);
}
collection.find({ _id: fn.generateID(req.params.store_id), "songs._id": { $in: array } }, function(err, songs) {
res.json(songs);
});
});
});
and i dont really know if it's the best way of doing it
I'm not entirely clear what you mean by "get the songs name and artist from the songsSchedule song_id" but it looks like that query will be messy.
If it were me, I'd consider splitting out songs and songSchedule into their own collections for easier querying.
from your document example, the "songs" field contains documents that do not contain an "_id" field.
"songs": [ {"name": "", "artist": "", "category": "", "tags": []} ]
But, your find() query is querying on the "songs._id" field.
Also, I'm not too familiar with the json() method, but does it handle cursors?
Regards,
Kay

Resources