Project fields from nested arrays of objects in mongoDB - mongodb

I have documents like this:
{
"_id" : ObjectId("5d302a6da9b1d6ae926c16a3"),
"location" : "car",
"playlist" : [
{
"playlist_number" : "1",
"number_of_songs" : "100",
"drive" : [
{
"drive_number" : "13",
"drive_name" : "Ooms",
"songs" : [
"59 Volvo",
"Yield Not To Temptation - Single Version",
"Johnny Angel",
"The Madison Time",
"Little Brown Jug",
"Only In My Dreams"
]
},
{
"drive_number" : "Z3",
"drive_name" : "codes",
"songs" : [
"59 Volvo",
"Perfect",
"Hands clap",
"I Ran",
"Falling down",
"Sounds of middle earth"
]
}
]
}
]
}
There can be many other location as well that may contain variable number of playlist objects (which in turn can contain n number of playlist_number, you get the idea).
When I search for a song by name for eg: 59 Volvo I should get back location, playlist_number, drive_number and drive_name. I tried with nested $elemMatch but it gives the whole document.

Related

MongoDB text search string equality?

I'm new to MongoDB and was looking through the docs a few nights ago and saw something that I haven't been able to locate since...
There was an option, I believe it was related to $text search, to treat an array of strings as if they were the same word. The syntax looked something like this:
["cheez", "cheese"],
["donut", "doughnut"],
["chips", "fries", "crisps"],
So a search for "chips" would return all documents indexed with "fries" or "crisps" even if they did not also have "chips".
Please tell me I wasn't dreaming!
YOU ARE NOT DREAMING
mongodb fuzzy text search
The following query searches the title field for the phrase naw yark. It uses the fuzzy default options where:
maxEdits allows up to two character variation of each term in the
given phrase to match the query to a document.
maxExpansions considers up to fifty similar terms for each term in
naw yark to find matches.
prefixLength is disabled.
The query also includes a $limit stage to limit the output to 10 results and a $project stage to:
Exclude all fields except title
Add a field named score
db.movies.aggregate([
{
$search: {
"text": {
"path": "title",
"query": "naw yark",
"fuzzy": {}
}
}
},
{
$limit: 10
},
{
$project: {
"_id": 0,
"title": 1,
score: { $meta: "searchScore" }
}
}
])
The above query returns the following results:
{ "title" : "New York, New York", "score" : 4.392756462097168 }
{ "title" : "New York", "score" : 4.050914287567139 }
{ "title" : "New York Stories", "score" : 3.4838104248046875 }
{ "title" : "New York Minute", "score" : 3.4838104248046875 }
{ "title" : "Synecdoche, New York", "score" : 3.4838104248046875 }
{ "title" : "New York Doll", "score" : 3.4838104248046875 }
{ "title" : "Little New York", "score" : 3.4838104248046875 }
{ "title" : "Escape from New York", "score" : 3.0559897422790527 }
{ "title" : "King of New York", "score" : 3.0559897422790527 }
{ "title" : "Naked in New York", "score" : 3.0559897422790527 }
also synonyms:
mongodb synonyms text search

MongoDB: Is it possible to index(unique) subarrays from documents in an isolated way?

I recently encountered an issue, and I'd like to solve it. If anyone would give any suggestion I'll be grateful.
I have documents that represent "users" and each document has a subarray that is responsible to save some codes, they can be many for each user. The matter is, each user cannot have duplicate codes in its specific array, but at the same time, in this case, each document should be isolated, for example, being possible to have two or more identical codes but since they are from different documents(users).
In short, the subarray("codes") cannot have individually duplicated codes(code), but that shouldn't interfere with other documents
I could do that in the application part, but I think doing that guarantee directly on DB, it's safer.
Is it possible to create indexes for this specific situation?
Example of two documents representing their respective users:
{ // Document of user 1
"_id" : "1", //user 1 and its codes
"codes" : [
{
"code" : "1111",
"description" : "code 1",
},
{
"code" : "2222",
"description" : "code 2",
},
{
"code" : "3333",
"description" : "code 3",
}
]
},
{ // Document of user 2
"_id" : "2", //user 2 and its codes
"codes" : [
{
"code" : "1111",
"description" : "code 1",
},
{
"code" : "4444",
"description" : "code 2",
},
{
"code" : "2222",
"description" : "code 3",
}
]
}
Thank you!
Use https://docs.mongodb.com/manual/reference/operator/update/addToSet/ to maintain uniqueness of code subdocuments. You will need to ensure that you always specify code fields in the same order (e.g. code, description).

How can i find the index of an array element that is a dictionary based on one of the keys of the dictionary using mongo query?

"_id" : ObjectId("5de64d7802a3414fbf374e75"),
"sectionDTO" : {
"sectionData" : [
{
"type" : "PRODUCT",
"title" : "<strong>What is Proburst BCAA supreme?</strong>",
"description" : "<p>Proburst® BCAA Supreme is a power-packed BCAA formula, designed to keep you fit and active. A combination of three essential amino acids, namely Leucine, Isoleucine and Valine, commonly known as “Branched Chain Amino Acids or BCAAs”, along with L-Glutamine.</p>"
},
{
"type" : "PRODUCT",
"title" : "<strong>Why Proburst BCAA supreme?</strong>",
"description" : "<p>Proburst® BCAA Supreme is not a regular BCAA formula. It is packed with 7g of BCAAs and 2.5g of L-Glutamine, along with key ingredients like Grape seed Extract and Black Pepper Extract to keep you active all day long.</p>"
},
{
"type" : "PRODUCT_PACKAGE",
"title" : "<strong>Select your Product:</strong>",
"description" : "",
"itemsPerRow" : "2",
"mItemsPerRow" : "1",
"comboPackageDetails" : [
{
"productName" : "Proburst BCAA Powder (250gms)",
"packageCode" : "bcaa-mango-flavoured-powder",
"productImage" : "https://assets.lybrate.com/q_auto,f_auto/rio/proburst/5/1.jpg"
},
{
"productName" : "Proburst BCAA Powder (400gms)",
"packageCode" : "bcaa-mango-flavoured-powder-v1",
"productImage" : "https://assets.lybrate.com/q_auto,f_auto/rio/proburst/6/1.jpg"
}
]
},
{
"type" : "PRODUCT",
"title" : "<strong>Key benefits of Proburst BCAA Supreme</strong>",
"description" : "<p>1. Reduces fatigue <br> 2. Keeps you active <br> 3. Helps you meet your ideal requirement of essential amino acids <br>4. Maintains electrolyte balance in the body</p>"
},
{
"type" : "PRODUCT",
"title" : "<strong>What are the key ingredients of BCAA supreme?</strong>",
"description" : "<p>1. Each serving of Proburst® BCAA Supreme has 7g of BCAAs in the ratio of 2:1:1 and 2.5g of L-Glutamine to keep you active by providing a dose of essential amino acids. <br> <br>2. Proburst® BCAA Supreme contains a combination of Citrulline Malate and Taurine for improved flow of blood in the vessels. <br> <br>3. Proburst® BCAA Supreme contains Electrolytes that will help in maintaining your body’s fluid levels. <br> <br>4. Contains a combination of powerful antioxidant herbs, Grape Seed (Vitis Vinifera) extract and Black Pepper (Piper Nigrum) extract to boost your immunity and improve absorption of nutrients in the body.</p>"
},
{
"type" : "PRODUCT",
"title" : "<strong>How to consume BCAA supreme?</strong>",
"description" : "<p>Take 1 scoop (about 13.3g) of Proburst BCAA Supreme in 250-300ml of water.</p>"
}
]
}
This is how my collection looks now i want to find the index of the array where the "title" is equal to a particular text for a given "_id".
Is there a way to do it using just mongodb?
Till now i have tried using the $indexofArray function but did not find any luck.
You need to use $unwind operator specifying includeArrayIndex parameter.
db.collection.aggregate([
{
$unwind: {
path: "$sectionDTO.sectionData",
includeArrayIndex: "index"
}
},
{
$match: {
"sectionDTO.sectionData.title": "<strong>Why Proburst BCAA supreme?</strong>"
}
}
])
MongoPlayground
[
{
"_id": ObjectId("5de64d7802a3414fbf374e75"),
"index": 1,
"sectionDTO": {
"sectionData": {
"description": "\u003cp\u003eProburst® BCAA Supreme is not a regular BCAA formula. It is packed with 7g of BCAAs and 2.5g of L-Glutamine, along with key ingredients like Grape seed Extract and Black Pepper Extract to keep you active all day long.\u003c/p\u003e",
"title": "\u003cstrong\u003eWhy Proburst BCAA supreme?\u003c/strong\u003e",
"type": "PRODUCT"
}
}
}
]
Since the <array expression> argument for the operator $indexOfArray in the following syntax:
{ $indexOfArray: [ <array expression>, <search expression>, <start>, <end> ] }
can be any valid expression as long as it resolves to an array, use the expression
"$sectionDTO.sectionData.title"
which returns an array of just titles which you can then search and return the matched index as:
var query = "<strong>Select your Product:</strong>";
db.collection.aggregate([
{ "$addFields": {
"indexOfTitle": {
"$indexOfArray": [
"$sectionDTO.sectionData.title",
query
]
}
} }
]);

How to combine Documents in aggregation pipeline with MongoDB Java driver 3.6?

I am using an aggregation pipeline with the MongoDB Java driver version 3.6. If I have documents that look something like:
doc1 --
{
"CAR": {
"VIN": "ASDF1234",
"YEAR": "2018",
"MAKE": "Honda",
"MODEL": "Accord"
},
"FEATURES": [
{
"AUDIO": "MP3",
"TIRES": "All Season",
"BRAKES": "ABS"
}
]
}
doc2 --
{
"CAR": {
"VIN": "ASDF1234",
"AVAILABILITY": "In Stock"
}
}
And if I submit a query like:
collection.aggregate(
Arrays.asList(
Aggregates.match(
and(
in("CAR.VIN", vinList),
or(
eq("CAR.MAKE", carMake),
eq("CAR.AVAILABILITY", carAvailability),
)
)
)
)
)
Let us assume that there are exactly two different records for which the "CAR.VIN" criteria match for every VIN, and I am going to get two results. Rather than deal with two results each time, I would like to merge the documents so that the result looks like this:
{
"CAR": {
"VIN": "ASDF1234",
"YEAR": "2018",
"MAKE": "Honda",
"MODEL": "Accord",
"AVAILABILITY": "In Stock"
},
"FEATURES": [
{
"AUDIO": "MP3",
"TIRES": "All Season",
"BRAKES": "ABS"
}
]
}
The example where I have two and only two results trivializes my need for this. Imagine that vinList is a list of 10000 values, and it might return 2 x 10000 documents. When I return an AggregateIterable to the client that is calling my code, I do not want to impose the requirement that they have to group or collate the results in any way, but that they will receive one document for each result that has all of the information that they will want to parse, cleanly and easily.
Of course, people will suggest that the data is simply combined into one document with all of the data in the MongoDB collection. For reasons that I cannot control, there are two separate documents corresponding to each VIN in the same collection, and that is something that I am unable to change. There is a value in our system that makes this more reasonable than it might seem, so please don't focus on this apparent problem with the data.
I am trying, with not much luck, to utilize the Aggretes.group() operation to merge the fields in my aggregation pipeline. Accumulators.push seems to be the closest operation to what I need, but I do not want to complicate the document structure with extra arrays, etc. Is there a straightforward approach that I am not seeing?
you can try $mergeObjects added in mongo v3.6
db.cc.aggregate(
[
{
$group: {
_id : "$CAR.VIN",
CAR : {$mergeObjects : "$CAR"},
FEATURES : {$mergeObjects : {$arrayElemAt : ["$FEATURES", 0 ]}}
}
}
]
).pretty()
result
{
"_id" : "ASDF1234",
"CAR" : {
"VIN" : "ASDF1234",
"YEAR" : "2018",
"MAKE" : "Honda",
"MODEL" : "Accord",
"AVAILABILITY" : "In Stock"
},
"FEATURES" : {
"AUDIO" : "MP3",
"TIRES" : "All Season",
"BRAKES" : "ABS"
}
}
>
to get features as array
db.cc.aggregate(
[
{
$group: {
_id : "$CAR.VIN",
CAR : {$mergeObjects : "$CAR"},
FEATURES : {$push : {$arrayElemAt : ["$FEATURES", 0 ]}}
}
}
]
).pretty()
result
{
"_id" : "ASDF1234",
"CAR" : {
"VIN" : "ASDF1234",
"YEAR" : "2018",
"MAKE" : "Honda",
"MODEL" : "Accord",
"AVAILABILITY" : "In Stock"
},
"FEATURES" : [
{
"AUDIO" : "MP3",
"TIRES" : "All Season",
"BRAKES" : "ABS"
},
null
]
}
>

MongoDB: text search on nested view

I have a document in MongoDB 3.2 with the following structure:
"_id" : ObjectId("5759815b94db5928bea3c3a5"),
"source" : "pons1",
"libraries" : [
{
"archive" : “deko1”,
"last_access" : ISODate("2016-06-09T14:45:04.644+0000"),
"books" : [
{
"title": "American Gods",
"author": "Neil Gaiman"
},
{
"title": "A Little Life",
"author": "Hanya Yanagihara"
}
]
},
{
"archive" : “deko90”,
"last_access" : ISODate("2016-06-10T12:45:03.624+0000"),
"books" : [
{
"title": "Sociology of News",
"author": "Michael Schudson"
},
{
"title": "City of God",
"author": "Augustine of Hippo"
}
]
}
]
There is an array (“books”) inside of another array (“libraries”).
Since the book titles are indexed as "text," I want to be able to conduct a free text search, and return only the relevant array elements.
For instance, if I search for the term “Gods,” I would like to see the following result:
"_id" : ObjectId("5759815b94db5928bea3c3a5"),
"source" : "pons1",
"libraries" : [
{
"archive" : “deko1”,
"last_access" : ISODate("2016-06-09T14:45:04.644+0000"),
"books" : [
{
"title": "American Gods",
"author": "Neil Gaiman"
}
]
},
{
"archive" : “deko90”,
"last_access" : ISODate("2016-06-10T12:45:03.624+0000"),
"books" : [
{
"title": "City of God",
"author": "Augustine of Hippo"
}
]
}
]
In MongoDB 3.2, you can filter the elements of an array using “$filter” (https://docs.mongodb.com/manual/reference/operator/aggregation/filter/).
The problem is that you cannot use text search ($text) as a condition for $filter.
$text can only be used in the first stage of the aggregation pipeline ($match) (https://docs.mongodb.com/manual/tutorial/text-search-in-aggregation/).
There is one obvious workaround: give up the power of MongoDB’s text search and maybe work with regex.
That does not seem a good option for me. I’d rather not lose the diacritic insensitivity and other interesting functionalities of MongoDB’s text search.
Is there a way of reconciling $filter and $text in the same MongoDB query?