I have the following JSON structure and I want to extract all the information about a particular team with a given id including the division it belongs to.
{
"teams":[
{
"divisionName":"5th Grade - Green",
"divisionTeams":[
{
"id":3222,
"name":"Columbia Ravens 5th",
"coach":"John Miller"
},
{
"id":3220,
"name":"HC Elite OMalley 5th",
"coach":"Eddie OMalley"
}
]
},
{
"divisionName":"5th Grade - White",
"divisionTeams":[
{
"id":3225,
"name":"CBSA Hoyas 5th Grade",
"coach":"Terrance Taylor"
},
{
"id":3276,
"name":"HC Elite 4th Tookes",
"coach":"Anthony Tookes"
},
]
}
]
}
I tried using the following lodash code, but it came up as undefined.
var team=_.chain(data.teams)
.flatten("divisionTeams")
.find({"id":3222 })
.value();
console.log(team);
Any help is much appreciated.
You can do everything you need with find() and some(). There's no need to flatten the arrays.
_.find(teams, function(item) {
return _.some(item.divisionTeams, { id: 3222 });
});
Related
I have a collection "Owners" and I want to return a list of "Owner" matching a filter (any filter), plus the count of "Pet" from the "Pets" collection for that owner, except I don't want the dead pets. (made up example)
I need the returned documents to look exactly like an "Owner" document with the addition of the "petCount" field because I'm using Java Pojos with the Mongo Java driver.
I'm using AWS DocumentDB that does not support $lookup with filters yet. If it did I would use this and I'd be done:
db.Owners.aggregate( [
{ $match: {_id: UUID("b13e733d-2686-4266-a686-d3dae6501887")} },
{ $lookup: { from: 'Pets', as: 'pets', 'let': { ownerId: '$_id' }, pipeline: [ { $match: { $expr: { $ne: ['$state', 'DEAD'] } } } ] } },
{ $addFields: { petCount: { $size: '$pets' } } },
{ $project: { pets: 0 } }
]).pretty()
But since it doesn't this is what I got so far:
db.Owners.aggregate( [
{ $match: {_id: { $in: [ UUID("cbb921f6-50f8-4b0c-833f-934998e5fbff") ] } } },
{ $lookup: { from: 'Pets', localField: '_id', foreignField: 'ownerId', as: 'pets' } },
{ $unwind: { path: '$pets', preserveNullAndEmptyArrays: true } },
{ $match: { 'pets.state': { $ne: 'DEAD' } } },
{ "$group": {
"_id": "$_id",
"doc": { "$first": "$$ROOT" },
"pets": { "$push": "$pets" }
}
},
{ $addFields: { "doc.petCount": { $size: '$pets' } } },
{ $replaceRoot: { "newRoot": "$doc" } },
{ $project: { pets: 0 } }
]).pretty()
This works perfectly, except if an Owner only has "DEAD" pets, then the owner doesn't get returned because all the "document copies" got filtered out by the $match. I'd need the parent document to be returned with petCount = 0 when ALL of them are "DEAD". I cannot figure out how to do this.
Any ideas?
These are the supported operations for DocDB 4.0 https://docs.amazonaws.cn/en_us/documentdb/latest/developerguide/mongo-apis.html
EDIT: update to use $filter as $reduce not supported by aws document DB
You can use $filter to keep only not DEAD pets in the lookup array, then count the size of the remaining array.
Here is the Mongo playground for your reference.
$reduce version
You can use $reduce in your aggregation pipeline to to a conditional sum for the state.
Here is Mongo playground for your reference.
As of January 2022, Amazon DocumentDB added support for $reduce, the solution posted above should work for you.
Reference.
There is an application with search input that gives an opportunity to search for contacts by their information stored in database.
For example, I can type 0972133122 Alan and my search engine must return all contacts whose firstname is Alan & whose numbers match 0972133122 string.
Of course, I can just type Alan 0972, for instance, and there must be returned all possible contacts matching this pattern. The query order may be different, so that I can type 0972 Alan Smith, and if there are 2 contacts with Alan names and whose phone numbers start with 0972, then additional Smith clarification should return the only 1 contact.
I suggest built in phone applications for Android make use of this search algorithm:
So that my goal is to achieve similar result, but I do know how to do this. Here my code:
GraphQL query
query contacts {
contacts(input: {
contactQuery: "Alan Smith"
}) {
name {
firstName
lastName
}
}
}
NodeJS query to MongoDB
const conditions = {};
const expr = contactQuery
.split(' ')
.map((contact) => new RegExp(`${contact}`, 'i'))
conditions.$or = [
{ 'firstName': { $in: expr } },
{ 'lastName': { $in: expr } },
{ 'university': { $in: expr } },
{ emails: { $elemMatch: { email: { $in: expr } } } },
{ phones: { $elemMatch: { phone: { $in: expr } } } },
{ socials: { $elemMatch: { id: { $in: expr } } } },
]
const contacts = await this.contacts
.find(conditions, undefined)
.exec()
This works partly, but I receive unwanted documents from MongoDB:
{
contacts: [
{
firstName: "Alan",
lastName: "Smith",
university: "KNTU",
...
},
{
firstName: "Alan",
lastName: "Alderson", // should not be returned
university: "ZNU",
...
},
...
]
}
But I need to get one contact that has strictly Alan firstname and Smith lastname. If it's impossible to do with MongoDB, -- please, provide me an example of SQL query. Any suggestions & solutions will be accepted!
Please, let me know if my question still is not clear.
Firstly, you need to separate out the numbers and words from the search text and then you can create a possible combination of it for an example:
FirstName: Alan, LastName: Smith
FirstName: Smith, LastName: Alan
Using regex you can do this easily and then you can use logical operators of mongodb to create your query like this
Approach 1
db.collection.find({
$or: [
{
$and: [
{
firstName: {
$regex: "Alan",
$options: "i"
}
},
{
lastName: {
$regex: "Smith",
$options: "i"
}
}
]
},
{
$and: [
{
firstName: {
$regex: "Smith",
$options: "i"
}
},
{
lastName: {
$regex: "Alan",
$options: "i"
}
}
]
}
]
})
Here is the link to the playground for you to look at it in action Mongo Playground
Approach 2
Another way is where you concat all the searchable keys into one field and then use regex to filter it out like this
db.collection.aggregate([
{
$addFields: {
text: {
$concat: [
"$firstName",
" ",
"$lastName",
" ",
"$university",
" ",
"$phones"
]
}
}
},
{
$match: {
text: {
$regex: "(?=.*?(0972))(?=.*?(Alan))(?=.*?(Smith))",
$options: "i"
}
}
},
{
$project: {
text: 0
}
}
])
Code to build the query:
let text = "0972 Alan Smith";
let parts = text.split(" ");
let query = parts.map(part => "(?=.*?("+part+"))").join("");
console.log(query);
But you need to check the performance implication of this approach or you can create a view and then query to view to make your query more cleaner
Here is the link to the playground for you to look at it in action Mongo Playground
I'm trying to fetch data from a single collection type of my Strapi backend into a Vue.js project using Apollo. It works well with a single alias, but I'm having troubles making it work with multiple aliases.
I'm getting my data from a collection type of "campaigns" which has a boolean field of "archive". I want to create an array of "campaigns" that contains all of the campaigns that haven't been archived (archive = false) as well as an array of "archive" that contains all of the archived ones (archive = true).
This is my code:
import gql from "graphql-tag";
export default {
name: "Campaigns",
data() {
return {
campaigns: [],
archive: []
};
},
apollo: {
campaigns: gql`
query getCampaigns {
campaigns: campaigns(where: { archive: "false" }, sort: "order:DESC") {
name
url
}
archive: campaigns(where: { archive: "true" }, sort: "order:DESC") {
name
url
}
}
`
}
The query returns an array of "campaigns", but the array of "archive" is still empty.
I've tried switching things up (put the archive alias first, switched the boolean values to make sure I can generally access the data of the archived campaigns etc.). The problem apparently lies with the "archive"-alias.
When I use the same query with Strapi's GraphQL playground I get the desired result:
{
campaigns: campaigns(where: { archive: "false" }, sort: "order:DESC") {
name
}
archive: campaigns(where: { archive: "true" }, sort: "order:DESC") {
name
}
}
... returns ...
{
"data": {
"campaigns": [
{
"name": "2020"
},
{
"name": "2019"
},
{
"name": "2018"
},
{
"name": "2017"
}
],
"archive": [
{
"name": "2016"
},
{
"name": "2015"
}
]
}
}
How can I make the query work in Vue.js with Apollo?
I think I've found a solution. Technically speaking I guess these are separate queries (which sort of defeats the purpose of aliases if I'm correct) but it does what I want:
apollo: {
campaigns: {
query: gql`
query {
campaigns: campaigns(
where: { archive: "false" }
sort: "order:desc"
) {
name
url
}
}
`
},
archive: {
query: gql`
query {
archive: campaigns(where: { archive: "true" }, sort: "order:desc") {
name
url
}
}
`
}
}
Apparently under some circumstance the initialization "apollo: { XYZ:" and the alias "query { XYZ:" have to match. I've seen in the docs that they don't necessarily have to match, but I don't fully understand when and why.
I guess I can't really tell what the initial parameter does.
You're using campaigns as the key for your entire query, so you need to initialize your data like this:
data() {
return {
campaigns: {
campaigns: [],
archive: [],
},
};
},
Then you can access each list through the key (i.e. campaigns.campaigns and campaigns.archive).
I believe the best way to do this is to use the update property: https://apollo.vuejs.org/guide/apollo/queries.html#name-matching
apollo: {
campaigns: {
query: gql`
query {
campaigns: campaigns(
where: { archive: "false" }
sort: "order:desc"
) {
name
url
}
}
`
},
archive: {
update: data => data.campaigns,
query: gql`
query {
campaigns(where: { archive: "true" }, sort: "order:desc") {
name
url
}
}
`
}
}
My requirement is to generate a line number for every new line generated in the json message. The input message is having array inside array, i.e, parent and child array.
Input message
[
{
id:"1",
Details:[
{
Name:"RAM",
LastName:"Manohar",
DOB:"20-10-1990",
Report:[
{
DateOfJoin:"03-03-2019",
Dept:"HR",
BillCode:"acx-12s",
EffectiveDate:"03-03-2019"
},
{
DateOfJoin:"03-04-2019",
Dept:"HR",
BillCode:"abc-12s",
EffectiveDate:"03-04-2019"
},
{
Name:"Alex",
LastName:"Ham",
DOB:"20-11-1980",
Report:[
{
DateOfJoin:"03-03-2019",
Dept:"HR",
BillCode:"acx-12s",
EffectiveDate:"03-03-2019"
},
{
DateOfJoin:"03-04-2019",
Dept:"HR",
BillCode:"abc-12s",
EffectiveDate:"03-04-2019"
}
]
}
]
},
{
id:"2",
Details:[
{
Name:"Kiran",
LastName:"Kurella",
DOB:"20-10-1980",
Report:[
{
DateOfJoin:"03-03-2019",
Dept:"DC",
BillCode:"acx-12s",
EffectiveDate:"03-03-2019"
},
{
DateOfJoin:"03-04-2019",
Dept:"DC",
BillCode:"abc-12s",
EffectiveDate:"03-04-2019"
},
{
Name:"Sunil",
LastName:"Kumar",
DOB:"20-11-1980",
Report:[
{
DateOfJoin:"03-01-2019",
Dept:"DC",
BillCode:"acx-12s",
EffectiveDate:"03-03-2019"
},
{
DateOfJoin:"03-04-2019",
Dept:"DC",
BillCode:"abc-12s",
EffectiveDate:"03-04-2019"
}
]
}
]
}
]
}
]
}
]
expected output:
[{LineNumber:1,
Dept:"HR",
Name: "Ram"},
{LineNumber:2,
Dept:"HR",
Name: "Alex"},
{LineNumber:3,
Dept:"HR",
Name: "Kiran"},
{LineNumber:4,
Dept:"HR",
Name: "Sunil"}]
Linenumber needs to be generated sequentially and irrespective of parent array or sub array. any help on this will be very appreciated. I have the logic in which i can generate the number using java function but in that case i need to set the variable value (flow variable) inside data weave which can be used in the java function to call recursively.
Use:
payload map {
count: $$
}
provided that I have following statement in sequelize
return models.VehicleModel
.findAll({
include: [
{
attributes: ['id'],
model: models.Unit
},
{
model: models.myPattern,
where: {
VehicleModelId: { $in: data.VehicleModelIds }
},
include: [
{
model: models.Calendar,
include: [
{
model: models.Restrictions,
include: [
{
model: models.RestrictionType
}],
where: {
//THIS IS THE LINE I NEED TO CHANGE SOMEHOW
UnitId: { $in: [models.Unit.id] }
},
required: false
}
]
}
]
}
],
where: {
id: { $in: data.VehicleModelIds }
}
})
This gives me NULL in the query like so:
[myPattern.Calendar.Restrictions].UnitId IN (NULL) AND ....
I want to be able to get the following instead:
[myPattern.Calendar.Restrictions].UnitId IN (Units.id) AND ....
This is because they are not mandatory to be there but should of course be linked to the proper unit corresponding to the vehicle model.
The myPattern is linked to the Calendar who is in turn linked to the Restrictions and so to the Units.
This all using id column.
Any help would be appreciated!
Found it
So for people looking for the "how to":
//...
where: {
UnitId: { $in: [models.sequelize.literal('Units.id')] },
}
//...and so on
This will output in the query: [myPattern.Calendar.Restrictions].UnitId IN (Units.id)