How to filter embedded array in mongo document with morphia - arrays

Given my Profile data looks like below, I want to find the profile for combination of userName and productId
and only return the profile with the respective contract for this product.
{
"firstName": "John",
"lastName": "Doe",
"userName": "john.doe#gmail.com",
"language": "NL",
"timeZone": "Europe/Amsterdam",
"contracts": [
{
"contractId": "DEMO1-CONTRACT",
"productId": "ticket-api",
"startDate": ISODate('2016-06-29T09:06:42.391Z'),
"roles": [
{
"name": "Manager",
"permissions": [
{
"activity": "ticket",
"permission": "createTicket"
},
{
"activity": "ticket",
"permission": "updateTicket"
},
{
"activity": "ticket",
"permission": "closeTicket"
}
]
}
]
},
{
"contractId": "DEMO2-CONTRACT",
"productId": "comment-api",
"startDate": ISODate('2016-06-29T10:27:45.899Z'),
"roles": [
{
"name": "Manager",
"permissions": [
{
"activity": "comment",
"permission": "createComment"
},
{
"activity": "comment",
"permission": "updateComment"
},
{
"activity": "comment",
"permission": "deleteComment"
}
]
}
]
}
]
}
I managed to find the solution how to do this from the command line. But I don't seem to find a way how to accomplish this with Morphia (latest version).
db.Profile.aggregate([
{ $match: {"userName": "john.doe#gmail.com"}},
{ $project: {
contracts: {$filter: {
input: '$contracts',
as: 'contract',
cond: {$eq: ['$$contract.productId', "ticket-api"]}
}}
}}
])
This is what I have so far. Any help is most appreciated
Query<Profile> matchQuery = getDatastore().createQuery(Profile.class).field(Profile._userName).equal(userName);
getDatastore()
.createAggregation(Profile.class)
.match(matchQuery)
.project(Projection.expression(??))
Note... meanwhile I found another solution which does not use an aggregation pipeline.
public Optional<Profile> findByUserNameAndContractQuery(String userName, String productId) {
DBObject contractQuery = BasicDBObjectBuilder.start(Contract._productId, productId).get();
Query<Profile> query =
getDatastore()
.createQuery(Profile.class)
.field(Profile._userName).equal(userName)
.filter(Profile._contracts + " elem", contractQuery)
.retrievedFields(true, Profile._contracts + ".$");
return Optional.ofNullable(query.get());
}

I finally found the best way (under assumption I only want to return max. 1 element from array) to filter embedded array.
db.Profile.aggregate([
{ $match: {"userName": "john.doe#gmail.com"}},
{ $unwind: "$contracts"},
{ $match: {"contracts.productId": "comment-api"}}
])

To match according to your first design you could try the projection settings with morphia aggregation pipeline.
Query<Profile> matchQuery = getDatastore().createQuery(Profile.class).field(Profile._userName).equal(userName);
getDatastore()
.createAggregation(Profile.class)
.match(matchQuery)
.project(Projection.expression("$filter", new BasicDBObject()
.append("input", "$contracts")
.append("as", "contract")
.append("cond", new BasicDBObject()
.append("$eq", Arrays.asList('$$contract.productId', "ticket-api")));
Also see the example written by the morphia crew around line 88 at https://github.com/mongodb/morphia/blob/master/morphia/src/test/java/org/mongodb/morphia/aggregation/AggregationTest.java.

Related

How to filter JSON data based on another JSON data in typescript

I have 2 JSON Data 1. Payers 2. Rules. I need to filter Payers JSON data based on PayerId from Rules JSON data.
{
"Payers": [
{
"payerId": "12345",
"name": "Test Payer1"
},
{
"payerId": "23456",
"name": "Test Payer2",
},
{
"payerId": "34567",
"name": "Test Payer3"
}}
Rules JSON file
{
"Rules": [
{
"actions": {
"canCopyRule": true
},
"RuleId": 123,
"description": "Test Rule",
"isDisabled": false,
"Criteria": [
{
"autoSecondaryCriteriaId": 8888,
"criteriaType": { "code": "primaryPayer", "value": "Primary Payer" },
"payerId": ["12345", "34567"]
}
]
}
}]}
I need to filter Payers JSON data based on Rules JSON data if PayerID matches
I need output like below
{
"Payers": [
{
"payerId": "12345",
"name": "Test Payer1"
},
{
"payerId": "34567",
"name": "Test Payer3"
}
}
How to filter?
You can use Array.filter like that (based on your data structure):
const filteredPayers = payersObj.Payers.filter((p) => rulesObj.Rules[0].Criteria[0].payerId.includes(p.payerId));
I can't figure out why your Rules json looks like this, I guess you have multiple rules. If so, you will need to iterate over each rule and invoke includes. Same for Criteria.
Code will check each rule and each critirias
and will return payers if payerId found in any of the given rules of any criteria
const payers = {
"Payers": [
{
"payerId": "12345",
"name": "Test Payer1"
},
{
"payerId": "23456",
"name": "Test Payer2",
},
{
"payerId": "34567",
"name": "Test Payer3"
}]}
const rules = {
"Rules": [
{
"actions": {
"canCopyRule": true
},
"RuleId": 123,
"description": "Test Rule",
"isDisabled": false,
"Criteria": [
{
"autoSecondaryCriteriaId": 8888,
"criteriaType": { "code": "primaryPayer", "value": "Primary Payer" },
"payerId": ["12345", "34567"]
}
]
}
]
}
const data = payers.Payers.filter(payer => rules.Rules.findIndex(rule => rule.Criteria.findIndex(criteria => criteria.payerId.includes(payer.payerId)) != -1) !== -1)
console.log(data)

Is it possible to get key value pairs from snowflake api instead rowType?

I'm working with an API from snowflake and to deal with the json data, I would need to receive data as key-value paired instead of rowType.
I've been searching for results but haven't found any
e.g. A table user with name and email attributes
Name
Email
Kelly
kelly#email.com
Fisher
fisher#email.com
I would request this body:
{
"statement": "SELECT * FROM user",
"timeout": 60,
"database": "DEV",
"schema": "PLACE",
"warehouse": "WH",
"role": "DEV_READER",
"bindings": {
"1": {
"type": "FIXED",
"value": "123"
}
}
}
The results would come like:
{
"resultSetMetaData": {
...
"rowType": [
{ "name": "Name",
...},
{ "name": "Email",
...}
],
},
"data": [
[
"Kelly",
"kelly#email.com"
],
[
"Fisher",
"fisher#email.com"
]
]
}
And the results needed would be:
{
"resultSetMetaData": {
...
"data": [
[
"Name":"Kelly",
"Email":"kelly#email.com"
],
[
"Name":"Fisher",
"Email":"fisher#email.com"
]
]
}
Thank you for any inputs
The output is not valid JSON, but the return can arrive in a slightly different format:
{
"resultSetMetaData": {
...
"data":
[
{
"Name": "Kelly",
"Email": "kelly#email.com"
},
{
"Name": "Fisher",
"Email": "fisher#email.com"
}
]
}
}
To get the API to send it that way, you can change the SQL from select * to:
select object_construct(*) as KVP from "USER";
You can also specify the names of the keys using:
select object_construct('NAME', "NAME", 'EMAIL', EMAIL) from "USER";
The object_construct function takes an arbitrary number of parameters, as long as they're even, so:
object_construct('KEY1', VALUE1, 'KEY2', VALUE2, <'KEY_N'>, <VALUE_N>)

How to update array inside MongoDB document

Can someone help me with a solution to update an array object inside the MongoDB document, I've tried a couple of methods but still it's to updating, here is my document that I want to update the array in the document.
{
"title": "Products",
"description": "test",
"image": "bdd8510d75f6e83ad308d5f306afccef_image.jpg",
"_created_at": "2021-06-07T20:51:08.316Z",
"ratingCount": 0,
"ratingTotal": 0,
"placeListSave": [
{
"objectId": "g70brr45pfi",
"name": "Kale",
"email": "null",
"strBrandLogo": "84de8865e3223d1ca61386355895aa04_image.jpg",
"storeNumber": "56",
"phone": "0815342119",
"createdAt": "2021-06-10T10:19:53.384Z",
"image": "ad1fb7602c2188223fd891a52373cb9d_image.jpg"
},
{
"objectId": "0qokn33p773",
"name": "Apple",
"email": null,
"strBrandLogo": null,
"storeNumber": "01",
"phone": "011 393 8600",
"createdAt": "2021-06-11T03:11:17.342Z",
"image": "8cfcbf2bcb5e3b4ea8ade44d3825bb52_image.jpg"
}
]
}
So I only want to update the apple object and change the data, I've tried the following code but doesn't seem to work.
`
var db = client.db("test");
try {
db.collection("ShoppingCentres").updateOne({
"title": req.body.product,
"placeListSave.objectId": req.body.id,
}, {
$set: {
"placeListSave.$.email": req.body.email,
"placeListSave.$.storeNumber": req.body.storeNumber,
"placeListSave.$.phone": req.body.phone,
"placeListSave.name": req.body.name,
},
});
res.json("client");
} catch (e) {
console.log("verify", e);
}
});`
arrayFilters seems suitable here:
db.collection.update({
"title": "Products",
"placeListSave.objectId": "0qokn33p773",
},
{
$set: {
"placeListSave.$[x].email": "test#some.email",
"placeListSave.$[x].storeNumber": "test",
"placeListSave.$[x].phone": "test",
"placeListSave.$[x].name": "test"
}
},
{
arrayFilters: [
{
"x.objectId": "0qokn33p773"
}
]
})
explained:
Add array filter called "x" with the objectId for the element that you need to update and use this filter in the $set stage to update the necessary elements.
Hint: To speed up the update you will need to add index on title field or compound index on title+placeListSave.objectId
playground

(Mongodb) Getting an error (BadValue) $in needs an array , when using $map aggregate function

I am trying to convert array of strings to array of ObjectId using map function, map will return me array of ObjectIds that I am using in $match to check if _id matches with any of objectids present in array using $in operator.
Database configuration is
db={
"students": [
{
"_id": ObjectId("5c1a79f7c98da061141475b7"),
"firstName": "Ibrahim",
"kelasID": ObjectId("5c429f9906f2a805bc6cd494"),
"lastName": "Ali",
"schoolID": ObjectId("5c1a735fc98da061141475a1"),
"year": 2018,
"__v": 0,
"addedOn": ISODate("2018-12-25T04:27:47.909Z"),
"checkIn": false,
"checkInStatus": 1,
"contactNo1": "012225656",
"father": "Ali",
"fatherID": "8852245",
"idType": 0,
"lastModified": ISODate("2018-12-25T04:27:47.909Z"),
"mother": "",
"motherID": ""
},
{
"_id": ObjectId("5c3bfea37774fb0b55000cb5"),
"idType": 0,
"checkIn": false,
"checkInStatus": 1,
"year": 2019,
"schoolID": ObjectId("5c1a735fc98da061141475a1"),
"kelasID": ObjectId("5c1a7534c98da061141475a3"),
"firstName": "Umar",
"lastName": "Bin Al-Khattab",
"contactNo1": "601222",
"status": 1,
"addedOn": ISODate("2019-01-14T03:14:43.597Z"),
"lastModified": ISODate("2019-01-14T03:14:43.597Z"),
"__v": 0
},
{
"_id": ObjectId("5c1a7c69c98da061141475bb"),
"idType": 0,
"checkIn": false,
"checkInStatus": 1,
"year": 2018,
"schoolID": ObjectId("5c1a735fc98da061141475a1"),
"kelasID": ObjectId("5c1a7540c98da061141475a5"),
"firstName": "Abdul Rahman",
"lastName": "Affan",
"father": "Affan",
"fatherID": "54321",
"contactNo1": "602288",
"status": 1,
"addedOn": ISODate("2018-12-25T04:30:16.130Z"),
"lastModified": ISODate("2018-12-25T04:30:16.130Z"),
"__v": 0
}
]
}
and the query is
db.students.aggregate([
{
"$match": {
"_id": {
$in: {
"stud": {
"$map": {
"input": [
"5c1a79f7c98da061141475b7",
"5c3bfea37774fb0b55000cb5",
"5c1a7c69c98da061141475bb",
"5c3bfea37774fb0b55000cb4",
"5c1a7d32c98da061141475be",
"5c3bfea37774fb0b55000cb7"
],
"in": {
"$toObjectId": "$$this"
}
}
}
}
}
}
}
])
You can also check this out here- https://mongoplayground.net/p/8tQFHxtrttW
The above doesn't work but if I change the query like below it works.
db.students.aggregate([
{
"$match": {
"_id": {
$in: [
ObjectId("5c1a79f7c98da061141475b7"),
ObjectId("5c3bfea37774fb0b55000cb5")
]
}
}
}
])
Link to above query- https://mongoplayground.net/p/cjq77KIAcPE
Error i'm getting - query failed: (BadValue) $in needs an array
Can anybody help with the reason to the error , as according to docs map does return an array , so what am I doing wrong?
Not sure why it doesn't work it might have to do with $in operator and $in aggregation. When you use an array that is defined by a variable you can use $in operator but when you are using aggregation result ( in this case $map ) you have to use $in aggregation. But to use $in aggregation you need to work with $expr for $match.
You can try the below code which works fine
db.students.aggregate([
{
$addFields: {
studentIds: {
$map: {
input: [
"5c1a79f7c98da061141475b7",
"5c3bfea37774fb0b55000cb5",
"5c1a7c69c98da061141475bb",
"5c3bfea37774fb0b55000cb4",
"5c1a7d32c98da061141475be",
"5c3bfea37774fb0b55000cb7",
],
in: {
$toObjectId: "$$this",
},
},
},
},
},
{
$match: {
$expr: {
$in: ["$_id", "$studentIds"],
},
},
},
]);
or if you want to combine the two of the stages you can do it like this
db.students.aggregate([
{
$match: {
$expr: {
$in: [
"$_id",
{
$map: {
input: [
"5c1a79f7c98da061141475b7",
"5c3bfea37774fb0b55000cb5",
"5c1a7c69c98da061141475bb",
"5c3bfea37774fb0b55000cb4",
"5c1a7d32c98da061141475be",
"5c3bfea37774fb0b55000cb7",
],
in: {
$toObjectId: "$$this",
},
},
},
],
},
},
},
]);

How to move MongoDB document fields to an array of objects?

Given a collection of documents similar to the following document
{
"_id": {
"$oid": "60582f08bf1d636f4b762ebc"
}
"experience": [{
"title": "Senior Customer Success Account Manager",
"company": "Microsoft",
"tenure": 8
}, {
"title": "Senior Service Delivery Manager",
"company": "Microsoft",
"tenure": 34
}],
"company3": "Oracle",
"tenure3": 10,
"title3": "Senior Customer Success Manager - EMEA |Oracle Marketing Cloud"
}
How would I write an updateMany or other shell command to move company3, tenure3 and title3 inside the experience array as a new object {company: <company3 value>, title: <title3 value>, tenure: <tenure3 value>} ?
Seems like you're looking for this aggregation update:
db.collection.update({},
[
{
$set: {
experience: {
$concatArrays: [
"$experience",
[
{
company: "$company3",
title: "$title3",
tenure: "$tenure3"
}
]
]
}
}
},
{
$unset: "company3"
},
{
$unset: "tenure3"
},
{
$unset: "title3"
}
],
{
multi: true
})
Playground: https://mongoplayground.net/p/xoEveE0rdBN

Resources