Query Mongo Collection of nested document of same type - mongodb

I have a not so unique requirement but i am just finding the right terminology to search, since i only keep getting results on how to query list/ array fields or nested elements.
Here is my class (document type)...
public class Item {
private String identity = null;
private String name = null;
private String type = null;
private List<Item> grouping = null;
}
Thus, some entity instances of this type could get complex as shown below...
{
"identity":"ITEM-1",
"name":"Pack-1",
"type":"Bundle",
"grouping":[
{
"identity":"ITEM-2",
"name":"Book",
"type":"Atomic Unit"
},
{
"identity":"ITEM-3",
"name":"Stationary",
"type":"Bundle",
"grouping":[
{
"identity":"ITEM-4",
"name":"Pen",
"type":"Atomic Unit"
},
{
"identity":"ITEM-5",
"name":"Paper",
"type":"Atomic Unit"
},
{
"identity":"ITEM-6",
"name":"Paraphernalia",
"type":"Bundle",
"grouping":[
{
"identity":"ITEM-7",
"name":"Eraser",
"type":"Atomic Unit"
},
{
"identity":"ITEM-8",
"name":"Ruler",
"type":"Atomic Unit"
},
{
"identity":"ITEM-9",
"name":"Compass",
"type":"Atomic Unit"
}
]
}
]
}
]
}
Now my requirement is to be able to search for Book or Pen or Compass and must be able to fetch the record ITEM-1. How do I achieve this in Mongo Query. I am using Spring Data Mongo Repository approach on my data abstraction service layer.
Thanks in advance for all the help.

my requirement is to be able to search for Book or Pen or Compass and
must be able to fetch the record ITEM-1.
You can try this query from mongo shell, and it fetches the document:
var SEARCH_ITEMS = [ "Book", "Pen", "Compass" ]
var ATOMIC = "Atomic Unit"
db.collection.find( {
$or: [
{ "type": ATOMIC, name: { $in: SEARCH_ITEMS } },
{ "grouping.type": ATOMIC, "grouping.name": { $in: SEARCH_ITEMS } },
{ "grouping.grouping.type": ATOMIC, "grouping.grouping.name": { $in: SEARCH_ITEMS } },
{ "grouping.grouping.grouping.type": ATOMIC, "grouping.grouping.grouping.name": { $in: SEARCH_ITEMS } }
]
} )

Related

Return an array element of an aggregation in an MongoDB Atlas (4.2) trigger function

So I am currently testing with returning an array element of a an aggregation in an MongoDB Atlas (4.2) trigger function:
exports = function(changeEvent) {
const collection = context.services.get(<clusterName>).db(<dbName>).collection(<collectionName>);
var aggArr = collection.aggregate([
{
$match: { "docType": "record" }
},
..,
{
$group: {
"_id": null,
"avgPrice": {
$avg: "$myAvgPriceFd"
}
}
}
]);
return aggArr;
};
Which outputs:
> result:
[
{
"_id": null,
"avgPrice": {
"$numberDouble": "18.08770081782988165"
}
}
]
> result (JavaScript):
EJSON.parse('[{"_id":null,"avgPrice":{"$numberDouble":"18.08770081782988165"}}]')
As you can see this is returned as one object in an array (I then intend to use the avgPrice value to update a field in a document in the same collection). I have tried to extract the object from the array with aggArr[0] or aggArr(0) - both resulting in:
> result:
{
"$undefined": true
}
> result (JavaScript):
EJSON.parse('{"$undefined":true}')
or by using aggArr[0].avgPrice as per this solution which fails with:
> error:
TypeError: Cannot access member 'avgPrice' of undefined
> trace:
TypeError: Cannot access member 'avgPrice' of undefined
at exports (function.js:81:10(163))
at function_wrapper.js:5:30(18)
at <eval>:13:8(6)
at <eval>:2:15(6)
Any pointers are most welcome because this one has me stumped for now!
I had the same problem, and figured it out. You have to append the .toArray() function to the aggregation call, where you have.
collection.aggregate(pipeline_steps).toArray()
Here's an example:
const user_collection = context.services
.get("mongodb-atlas")
.db("Development")
.collection("users");
const search_params = [
{
"$search": {
"index": 'search_users',
"text": {
"query": value,
"path": [
"email", "first_name", "last_name"
],
"fuzzy":{
"prefixLength": 1,
"maxEdits": 2
}
}
}
}
];
const search_results = await user_collection.aggregate(search_params).toArray();
const results = search_results
return results[0]
Here's the documentation showing how to convert the aggregation to an array.

How to query an object in an array embedded in mongodb?

I am currently working on an application that takes control of Projects, which have Meetings and that these meetings have Participants.
I want to consult a Participant by his nomina field.
Structure for a project document object:
{
"id":"5c1b0616a0441f27f022bfdc",
"name":"Project Test",
"area":"Area",
"date":"2019-01-01",
"meetings":[
{
"id":"5c1b073d445707834699ce97",
"objetive":"Objetive",
"fecha":"2019-01-01",
"participants":[
{
"nomina":1,
"name":"Person 1",
"role":"Rol1",
"area":"area1",
"signature":null
},
{
"nomina":2,
"name":"Person 2",
"role":"rol 2",
"area":"área 2",
"signature":null
}
]
}
]
}
Expected behavior
I want to consult a Participant by nomina field knowing the id of the Project and also knowing the id of the Meeting.
Expected output
Having:
id Project = 5c1b0616a0441f27f022bfdc
id Meeting = 5c1b073d445707834699ce97
nomina Participant = 1
It's expected that the query will return me:
{
"nomina":1,
"name":"Person 1",
"role":"Rol1",
"area":"area1",
"signature":null
}
For not so huge number of meetings in every document if you want to get the exact document stated, you can do this pipeline, it is straight forward:
db.collection.aggregate(
[
{
$match: {
id:"5c1b0616a0441f27f022bfdc"
}
}, {
$unwind: {
path : "$meetings"
}
},
{
$unwind: {
path : "$meetings.participants"
}
},
{
$match: {
"meetings.id":"5c1b073d445707834699ce97",
"meetings.participants.nomina":1
}
},
{
$replaceRoot: {
newRoot: "$meetings.participants"
}
}
]);
If you would have over thousands of elements in meetings then I'd suggest adding another match to meetings or grouping meetings and project IDs.
But if you just want to get the document containing what you want it is just a simple find query:
db.collection.find({id:"5c1b0616a0441f27f022bfdc","meetings.id":"5c1b073d445707834699ce97","meetings.participants.nomina":1 });

mongo repository query for array of objects

I am using MongoRepository query instead of MongoTemplate for querying list of objects.Below is the sample mongo json values.
{
"host":[
{
"user":"username",
"pass":"password"
},
{
"user":"username1",
"pass":"password"
}
]
}
{
"host":[
{
"user":"username",
"pass":"password123"
},
{
"user":"username123",
"pass":"password"
}
]
}
Need to get list of host Object based on username and password query.
Below is my query
#Query("{'host.user' : { $in : ?0 }, 'host.pass' : { $in : ?1 }}")
public List<Host> getListofHost(String username,String password);
But Actually it is listing all host since username and password is present in list of host,Is there a way to get Actual List of host getListofHost("username","password") and it should return only
"host":[
{
"user":"username",
"pass":"password"
},
{
"user":"username1",
"pass":"password"
}
]
Thanks in Advance

Tricky MongoDB search challenge

I have a tricky mongoDB problem that I have never encountered.
The Documents:
The documents in my collection have a search object containing named keys and array values. The keys are named after one of eight categorys and the corresponding value is an array containing items from that category.
{
_id: "bRtjhGNQ3eNqTiKWa",
/* */
search :{
usage: ["accounting"],
test: ["knowledgetest", "feedback"]
},
test: {
type:"list",
vals: [
{name:'knowledgetest', showName: 'Wissenstest'},
{name:'feedback', showName: '360 Feedback'},
]
},
usage: {
type:"list",
vals: [
{name:'accounting', showName: 'Accounting'},
]
}
},
{
_id: "7bgvegeKZNXkKzuXs",
/* */
search :{
usage: ["recruiting"],
test: ["intelligence", "feedback"]
},
test: {
type:"list",
vals: [
{name:'intelligence', showName: 'Intelligenztest'},
{name:'feedback', showName: '360 Feedback'},
]
},
usage: {
type:"list",
vals: [
{name:'recruiting', showName: 'Recruiting'},
]
}
},
The Query
The query is an object containing the same category - keys and array - values.
{
usage: ["accounting", "assessment"],
test : ["feedback"]
}
The desired outcome
If the query is empty, I want all documents.
If the query has one category and any number of items, I want all the documents that have all of the items in the specified category.
If the query has more then one category, I want all the documents that have all of the items in all of the specified categorys.
My tries
I tried all kinds of variations of:
XX.find({
'search': {
"$elemMatch": {
'tool': {
"$in" : ['feedback']
}
}
}
No success.
EDIT
Tried: 'search.test': {$all: (query.test ? query.test : [])} which gives me no results if I have nothing selected; the right documents when I am only looking inside the test category; and nothing when I additionally look inside the usage category.
This is at the heart of my app, thus I historically put up a bounty.
let tools = []
const search = {}
for (var q in query) {
if (query.hasOwnProperty(q)) {
if (query[q]) {
search['search.'+q] = {$all: query[q] }
}
}
}
if (Object.keys(query).length > 0) {
tools = ToolsCollection.find(search).fetch()
} else {
tools = ToolsCollection.find({}).fetch()
}
Works like a charm
What I already hinted at in the comment: your document structure does not support efficient and simple searching. I can only guess the reason, but I suspect that you stick to some relational ideas like "schemas" or "normalization" which just don't make sense for a document database.
Without digging deeper into the problem of modeling, I could imagine something like this for your case:
{
_id: "bRtjhGNQ3eNqTiKWa",
/* */
search :{
usage: ["accounting"],
test: ["knowledgetest", "feedback"]
},
test: {
"knowledgetest" : {
"showName": "Wissenstest"
},
"feedback" : {
"showName": "360 Feedback"
}
},
usage: {
"accounting" : {
"values" : [ "knowledgetest", "feedback" ],
"showName" : "Accounting"
}
}
},
{
_id: "7bgvegeKZNXkKzuXs",
/* */
search : {
usage: ["recruiting"],
test: ["intelligence", "feedback"]
},
test: {
"intelligence" : {
showName: 'Intelligenztest'
},
"feedback" : {
showName: '360 Feedback'
}
},
usage: {
"recruiting" : {
"values" : [ "intelligence", "feedback" ],
"showName" : "Recruiting"
}
}
}
Then, a search for "knowledgetest" and "feedback" in "accounting" would be a simple
{ "usage.accounting.values" : { $all : [ "knowledgetest", "feedback"] } }
which can easily be used multiple times in an and condition:
{
{ "usage.accounting.values" : { $all : [ "knowledgetest", "feedback"] } },
{ "usage.anothercategory.values" : { $all [ "knowledgetest", "assessment" ] } }
}
Even the zero-times-case matches your search requirements, because an and-filter with none of these criteria yields {} which is the find-everything filter expression.
Once more, to make it absolutely clear: when using mongo, forget everything you know as "best practice" from the relational world. What you need to consider is: what are your queries, and how can my document model support these queries in an ideal way.

Efficiency of indexed embedded array

I am currently evaluating the efficiency of different databases for a use case. In Mongodb, would like to store around 1 million objects with the following structure. Each object will have between 5 and 10 objects in the foo array.
{
name:"my name",
foos:[
{
foo:"...",
bar:"..."
},
{
foo:"...",
bar:"..."
},
{
foo:"...",
bar:"..."
}
]
}
I often need to search for objects which where the foos collection contains an object with a specific property, e.g.:
// mongo collection
[
{
name:'my name',
foos:[
{
foo:'one_foo',
bar:'a_bar'
},
{
foo:'two_foo',
bar:'b_bar'
}
]
},
{
name:'another name',
foos:[
{
foo:'another foo',
bar:'a_bar'
},
{
foo:'just another foo',
bar:'c_bar'
}
]
}
]
// search (pseudo code)
{ foos: {$elemMatch: {bar: 'c_bar'}} }
// returns
{
name:'another name',
foos:[
{
foo:'another foo',
bar:'a_bar'
},
{
foo:'just another foo',
bar:'c_bar'
}
]
}
Can this efficiently be done with mongo and how should the indexes be set?
I don't want you to evaluate performance for me, just an idea how mongo performs for my use case or how optimization could look like.
MongoDB has documentation explaining how to create indexes on embedded documents, through dot notation:
Dot Notation (Reaching into Objects)
> db.blogposts.findOne()
{ title : "My First Post", author: "Jane",
comments : [{ by: "Abe", text: "First" },
{ by : "Ada", text : "Good post" } ]
}
> db.blogposts.find( { "comments.by" : "Ada" } )
> db.blogposts.ensureIndex( { "comments.by" : 1 } );
As for the performance characteristic... just test it with your dataset.