I am new to MongoDB and I am stuck trying to get unique subdocuments in an array.
A document in my collection looks like this:
{
"PubDate": "1/01/01 00:00",
"Title": "Identification of DNA-Dependent Protein Kinase Catalytic Subunit (DNA-PKcs) as a Novel Target of Bisphenol A",
"Datums": [
{
"evidence_id": "3515620_6",
"evidence": [
"\n\nTo examine the interaction between DNA-PKcs and Ku70/Ku80 more directly, we performed immunoprecipitation (IP) using FLAG-Ku70 or FLAG-Ku80 recombinants, which were expressed in 293T cells after IR-irradiation (Fig. 4B\n ) or UV-irradiation (Fig. 4C\n ). After IR-irradiation, co-precipitation of DNA-PKcs with Ku80 increased compared with that in the non-irradiated controls (Fig. 4B\n lanes 7 and 8)."
],
"map": {
"change": [
{
"Text": "increased"
}
],
"subject": [
{
"Entity": {
"strings": [
"dna-pkcs"
],
"uniprotSym": "P78527"
}
}
],
"treatment": [
{
"Entity": {
"strings": [
"dna-pkcs"
],
"uniprotSym": "P78527"
}
}
],
"assay": [
{
"Text": "copptby"
}
]
}
},
{
"evidence_id": "3515620_6",
"evidence": [
"\n\nTo examine the interaction between DNA-PKcs and Ku70/Ku80 more directly, we performed immunoprecipitation (IP) using FLAG-Ku70 or FLAG-Ku80 recombinants, which were expressed in 293T cells after IR-irradiation (Fig. 4B\n ) or UV-irradiation (Fig. 4C\n ). After IR-irradiation, co-precipitation of DNA-PKcs with Ku80 increased compared with that in the non-irradiated controls (Fig. 4B\n lanes 7 and 8)."
],
"map": {
"change": [
{
"Text": "increased"
}
],
"subject": [
{
"Entity": {
"strings": [
"dna-pkcs"
],
"uniprotSym": "P78527"
}
}
],
"treatment": [
{
"Entity": {
"strings": [
"dna-pkcs"
],
"uniprotSym": "P78527"
}
}
],
"assay": [
{
"Text": "copptby"
}
]
}
},
{
"evidence_id": "3515620_6",
"evidence": [
"\n\nTo examine the interaction between DNA-PKcs and Ku70/Ku80 more directly, we performed immunoprecipitation (IP) using FLAG-Ku70 or FLAG-Ku80 recombinants, which were expressed in 293T cells after IR-irradiation (Fig. 4B\n ) or UV-irradiation (Fig. 4C\n ). After IR-irradiation, co-precipitation of DNA-PKcs with Ku80 increased compared with that in the non-irradiated controls (Fig. 4B\n lanes 7 and 8)."
],
"map": {
"change": [
{
"Text": "increased"
}
],
"subject": [
{
"Entity": {
"strings": [
"dna-pkcs"
],
"uniprotSym": "P78527"
}
}
],
"treatment": [
{
"Entity": {
"strings": [
"dna-pkcs"
],
"uniprotSym": "P78527"
}
}
],
"assay": [
{
"Text": "copptby"
}
]
}
}
],
"Volume": "7",
"FullJournalName": "PLoS ONE",
"Authors": "Ito Y, Ito T, Karasawa S, Enomoto T, Nashimoto A, Hase Y, Sakamoto S, Mimori T, Matsumoto Y, Yamaguchi Y, Handa H",
"Issue": "12",
"Pages": "e50481",
"PMCID": "3515620"
}
In the above example, the "Datums" field has only one subdocument, but usually, the "Datums" field will have around 20-30 subdocuments. I want my MongoDB query to output documents (that satisfy certain criteria), where the "Datums" field will have unique subdocuments in its array. To do that I am using the following MongoDB query:
db.My_Datums.aggregate(
[
{ "$match": {
"Datums":
{
"$elemMatch":
{
"map.treatment.Entity.uniprotSym": { "$in": ["P33981", "P78527"] },
"map.assay.Text": "copptby"
}
}
}},
{ "$project": { "PMCID":1, "Title":1, "PubDate":1, "Volume":1, "Issue":1, "Pages":1, "FullJournalName":1, "Authors":1, "Datums.map.assay.Text":1, "Datums.map.change.Text":1, "Datums.map.subject.Entity.strings":1, "Datums.map.treatment.Entity.uniprotSym":1, "Datums.evidence_id":1, "_id":0 }},
{ "$unwind": "$Datums" },
{ "$match": { "Datums.map.treatment.Entity.uniprotSym": { "$in": ["P33981", "P78527"] }, "Datums.map.assay.Text": "copptby" }},
{ "$group": { "_id": "$PMCID", "Datums": { "$addToSet": "$Datums" }}}
]
#{ allowDiskUse: 1 }
)
But on running the above command, I am getting the below output:
{u'Datums': [{u'evidence_id': u'3515620_6',
u'map': {u'assay': [{u'Text': u'copptby'}],
u'change': [{u'Text': u'increased'}],
u'subject': [{u'Entity': {u'strings': u'dna-pkcs'}}],
u'treatment': [{u'Entity': {u'uniprotSym': u'P78527'}}]}},
{u'evidence_id': u'3515620_6',
u'map': {u'assay': [{u'Text': u'copptby'}],
u'change': [{u'Text': u'increased'}],
u'subject': [{u'Entity': {u'strings': u'dna-pkcs'}}],
u'treatment': [{u'Entity': {u'uniprotSym': u'P78527'}}]}},
{u'evidence_id': u'3515620_6',
u'map': {u'assay': [{u'Text': u'copptby'}],
u'change': [{u'Text': u'increased'}],
u'subject': [{u'Entity': {u'strings': u'dna-pkcs'}}],
u'treatment': [{u'Entity': {u'uniprotSym': u'P78527'}}]}}],
u'_id': u'3515620'}
What I am not understanding is why addToSet adding duplicate subdocuments to "Datums". Is there any way I can filter out the duplicates? What am I doing wrong in my query? I have searched a lot and read up a lot, but couldnt find any solution. Any MongoDB guru out there who could help this noob?? I will be eternally grateful to you!
Thanks in advance!
Related
I am using complex nested mongo DB document. I have tried to use aggregate with replaceroot but using replaceroot I am able to go for only one sub-level. In my ocument I am having 3 sub levels. I tried to get all document and loop information in python but I ended up using 5nested for loops.
[
{
"_id": "A-38",
"resourcePaths": [
{
"resourcePathName": "some1",
"method": "POST",
"scenarioDetails": [
{
"scenarioId": "4-ABCD",
"scenarioName": "scenaio1",
"propertiesDatatype": {},
"requestData": [
{
"sql": "Select * from tbl",
"insUpdFlag": "Y",
},
{
"digit": "",
"insUpdFlag": "N",
}
],
"updatedBy": "db_insert",
},
{
"scenarioId": "4-QZWBJ3",
"scenarioName": "Scen2",
"requestData": [
{
"sql": "",
"insUpdFlag": "N",
}
],
"updated": "01/31/2023 09:16:41"
}
]
}
],
}
]
I am trying get the scenario details which is having "insUpdFlag" as Y like this
{
"scenarioId": "4-ABCD",
"scenarioName": "scenaio1",
"propertiesDatatype": {},
"requestData": [
{
"sql": "Select * from tbl",
"insUpdFlag": "Y",
},
{
"digit": "",
"insUpdFlag": "N",
}
],
"updatedBy": "db_insert",
}
I tried but giving me entire document. I need some help here. TYIA
db.collection.find({
"resourcePaths.scenarioDetails.requestData.insUpdFlag": "Y"
},
The problem I am facing is that I want to develop an autocomplete search bar using Mean Stack like the one in this site, but when I type, for example, 'ag' it's not returning the right location that should be 'Aguascalientes'.
I have two different search indexes set up and a different query for each.
First Index:
{
"mappings": {
"dynamic": false,
"fields": {
"name": {
"foldDiacritics": false,
"maxGrams": 7,
"minGrams": 3,
"tokenization": "edgeGram",
"type": "autocomplete"
},
"searchName": {
"foldDiacritics": false,
"maxGrams": 7,
"minGrams": 3,
"tokenization": "edgeGram",
"type": "autocomplete"
}
}
}
}
First Query:
[
{
$search: {
index: "autocomplete2",
compound: {
must: [
{
text: {
query: search,
path: "searchName",
fuzzy: {
maxEdits: 2,
},
},
},
],
},
},
},
{
$limit: 10,
},
]
The first ones are not returning any document at all. But the second example is:
{
"mappings": {
"dynamic": false,
"fields": {
"name": {
"analyzer": "lucene.standard",
"type": "string"
},
"searchName": {
"analyzer": "lucene.standard",
"type": "string"
}
}
}
}
Query:
[
{
$search: {
index: 'default',
compound: {
must: [
{
text: {
query: search,
path: 'name',
fuzzy: {
maxEdits: 1,
},
},
},
{
text: {
query: search,
path: 'searchName',
fuzzy: {
maxEdits: 1,
},
},
},
],
},
},
},
{
$limit: 5,
},
]
The second example is only returning documents if the search term 'aguascalient' but is not returning any document if the search term is shorter like the site. Maybe it has something to do with the fuzzy edits but if I set it up to greater than 2 I get an error.
Also the order is not right, it returns first the CITY and second the STATE but I need the STATE first because the search term is more similar than the city. Let me explain, search field for STATE is only 'Aguascalientes' but search field cities is 'Aguascalientes Aguascalientes' so I don't know why is not working properly. Maybe in that case I should give weights accordingly but I'm not sure if it's the right approach to solve this.
My data structure:
{
"_id": "638d0ffc34ad076c6bd12cb6",
"depth": 2,
"label": "CITY",
"location_id": "V1-C-247",
"name": "Aguascalientes",
"parent": "Aguascalientes",
"fullName": "Aguascalientes, Aguascalientes",
"parentId": "V1-B-61",
"searchName": "Aguascalientes Aguascalientes",
}
{
"_id": "638d0ffc34ad076c6bd12cb6",
"depth": 1,
"label": "STATE",
"location_id": "V1-C-248",
"name": "Aguascalientes",
"parent": null,
"fullName": "Aguascalientes",
"parentId": null,
"searchName": "Aguascalientes",
}
For the first index + query setup:
First, you are indexing the name field but are not searching on it. I will remove it from the code snippets for readability, but you can add it back to your index definition if you find you need to search on it.
There are two problems with the this index + query setup if you want to return results with a query for "ag". You have searchName defined as a field mapping of type autocomplete, but you also need to use the autocomplete operator in your query:
[
{
$search: {
index: "autocomplete2",
compound: {
must: [
{
autocomplete: {
query: search,
path: "searchName",
},
},
],
},
},
},
{
$limit: 10,
},
]
Second, in your index definition field mapping for searchName, you have minGram set to 3 and maxGram set to 7. Based on the documentation for the autocomplete field mapping, this means that your data will be tokenized into sequences of character lengths between 3 to 7, using the selected tokenization strategy. Since you have selected edgeGram, the tokens generated by the text "Aguascalientes" will be tokenized starting from the left edge, resulting in tokens "agu", "agua", "aguas", "aguasc", "aguasca". Since the search term "ag" does not match any of the tokens, nothing is returned. So, you must change the minGram to 2 to get the token "ag":
{
"mappings": {
"dynamic": false,
"fields": {
"searchName": {
"foldDiacritics": false,
"maxGrams": 7,
"minGrams": 2,
"tokenization": "edgeGram",
"type": "autocomplete"
}
}
}
}
Finally, if you want the document with an exact match to return over a partial match, ie. "Aguascalientes" should return before "Aguascalientes Aguascalientes", you need to implement exact matching. Here is a MongoDB blog post outlining a few options.
One option that I tried: In the index, use a keyword analyzer on the "searchName" field typed as a string data type. In the query, use the text operator nested in a should clause so that exact matches will return higher than other results.
Index:
{
"mappings": {
"dynamic": false,
"fields": {
"searchName": [
{
"foldDiacritics": false,
"maxGrams": 7,
"type": "autocomplete"
},
{
"analyzer": "lucene.keyword",
"searchAnalyzer": "lucene.keyword",
"type": "string"
}
]
}
}
}
Query:
[
{
$search: {
compound: {
must: [
{
autocomplete: {
query: search,
path: "searchName"
}
}
],
should:[
{
text: {
query: search,
path: "searchName"
}
}
],
},
},
},
]
I got a collection of 10000 ca. docs, where each doc has the following format:
{
"_id": {
"$oid": "631edc6e207c89b932a70a26"
},
"name": "Ethereum",
"auditInfoList": [
{
"coinId": "1027",
"auditor": "Fairyproof",
"auditStatus": 2,
"reportUrl": "https://www.fairyproof.com/report/Covalent"
}
],
"circulatingSupply": 122335921.0615,
"cmcRank": 2,
"dateAdded": "2015-08-07T00:00:00.000Z",
"id": 1027,
"isActive": 1,
"isAudited": true,
"lastUpdated": 1662969360,
"marketPairCount": 6085,
"quotes": [
{
"name": "USD",
"price": 1737.1982544180462,
"volume24h": 14326453277.535921,
"marketCap": 212521748520.66168,
"percentChange1h": 0.62330307,
"percentChange24h": -1.08847937,
"percentChange7d": 10.96517745,
"lastUpdated": 1662966780,
"percentChange30d": -13.49374496,
"percentChange60d": 58.25153862,
"percentChange90d": 42.27475921,
"fullyDilluttedMarketCap": 212521748520.66,
"marketCapByTotalSupply": 212521748520.66168,
"dominance": 20.0725,
"turnover": 0.0674117,
"ytdPriceChangePercentage": -53.9168
}
],
"selfReportedCirculatingSupply": 0,
"slug": "ethereum",
"symbol": "ETH",
"tags": [
"mineable",
"pow",
"smart-contracts",
"ethereum-ecosystem",
"coinbase-ventures-portfolio",
"three-arrows-capital-portfolio",
"polychain-capital-portfolio",
"binance-labs-portfolio",
"blockchain-capital-portfolio",
"boostvc-portfolio",
"cms-holdings-portfolio",
"dcg-portfolio",
"dragonfly-capital-portfolio",
"electric-capital-portfolio",
"fabric-ventures-portfolio",
"framework-ventures-portfolio",
"hashkey-capital-portfolio",
"kenetic-capital-portfolio",
"huobi-capital-portfolio",
"alameda-research-portfolio",
"a16z-portfolio",
"1confirmation-portfolio",
"winklevoss-capital-portfolio",
"usv-portfolio",
"placeholder-ventures-portfolio",
"pantera-capital-portfolio",
"multicoin-capital-portfolio",
"paradigm-portfolio",
"injective-ecosystem"
],
"totalSupply": 122335921.0615
}
Im pulling updated version of it and, to aviod duplicates, im doing the following by using 'update_one'
for doc in new_doc_list:
CRYPTO_TEMPORARY_LIST.update_one(
{ "name" : doc['name']},
{ "$set": {
"lastUpdated": doc['lastUpdated']
}
},
upsert=True)
The problem is it's too slow.
I'm trying to figure out how to improve speed by using update_many but can't figure out how to set it up.
I Basically want to update every document x name. Completely change the doc and not the "lastUpdated" field would b even better.
Thanks guys <3
I came across this page discussing different hash functions that mcrouter can use, but could not find an example of how a hash function can be configured if you do not want to use the default ch3. In my case, i would like to use the "wch3" with a balanced (50/50) weight between two nodes in a pool. How can i manually change the default to configure wch3?
Thanks in advance.
Here is an example that can help you:
{
"pools": {
"cold": {
"servers": [
"memc_1:11211",
"memc_2:11211"
]
},
"warm": {
"servers": [
"memc_11:11211",
"memc_12:11211"
]
}
},
"route": {
"type": "OperationSelectorRoute",
"operation_policies": {
"get": {
"type": "WarmUpRoute",
"cold": "PoolRoute|cold",
"warm": "PoolRoute|warm",
"exptime": 20
}
},
"default_policy": {
"type": "AllSyncRoute",
"children": [{
"type": "PoolRoute",
"pool": "cold",
"hash": {
"hash_func": "WeightedCh3",
"weights": [
1,
1
]
}
},
{
"type": "PoolRoute",
"pool": "warm",
"hash": {
"hash_func": "WeightedCh3",
"weights": [
1,
1
]
}
}
]
}
}
}
You can adjust the weight in the range [0.0, 1.0].
I have tried two different ways to do the same query with Doctrine's MongoDB-ODM.
Can you figure out that why the two, in my opinion similar queries both return different result? Snippet 1 doesn't return anything where Snippet 2 returns correct database entries. Both of the queries seem similar in the log file - except #1 does not have skip & limit lines.
Snippet 1
$dateDayAgo = new \DateTime('1 day ago');
$recentLogins = $this->get('user_activity_tracker')->findBy(array(
'targetUser' => $userAccount->getId(),
'code' => array('$in' => array('login.attempt','login.ok')),
'ts' => array('$gte', $dateDayAgo)
))->sort(['ts' => 1]);
Symfony's log entries from the Snippet 1:
[2012-08-13 09:14:33] doctrine.INFO: MongoDB query: { "find": true, "query": { "targetUser": ObjectId("4fa377e06803fa7303000002"), "code": { "$in": [ "login.attempt", "login.ok" ] }, "ts": [ "$gte", new Date("Sun, 12 Aug 2012 09:14:33 +0000") ] }, "fields": [ ], "db": "eventio_com", "collection": "ActivityEvent" } [] []
[2012-08-13 09:14:33] doctrine.INFO: MongoDB query: { "sort": true, "sortFields": { "ts": 1 }, "query": { "targetUser": ObjectId("4fa377e06803fa7303000002"), "code": { "$in": [ "login.attempt", "login.ok" ] }, "ts": [ "$gte", new Date("Sun, 12 Aug 2012 09:14:33 +0000") ] }, "fields": [ ] } [] []
Snippet 2
$recentLoginsQuery = $this->get('user_activity_tracker')->createQueryBuilder()
->field('targetUser')->equals($userAccount->getId())
->field('code')->in(array('login.attempt','login.ok'))
->field('ts')->gte($dateDayAgo)
->sort('ts','asc')
->getQuery();
$recentLogins = $recentLoginsQuery->execute();
Log entries for Snippet 2:
[2012-08-13 09:17:30] doctrine.INFO: MongoDB query: { "find": true, "query": { "targetUser": ObjectId("4fa377e06803fa7303000002"), "code": { "$in": [ "login.attempt", "login.ok" ] }, "ts": { "$gte": new Date("Sun, 12 Aug 2012 09:17:30 +0000") } }, "fields": [ ], "db": "eventio_com", "collection": "ActivityEvent" } [] []
[2012-08-13 09:17:30] doctrine.INFO: MongoDB query: { "limit": true, "limitNum": null, "query": { "targetUser": ObjectId("4fa377e06803fa7303000002"), "code": { "$in": [ "login.attempt", "login.ok" ] }, "ts": { "$gte": new Date("Sun, 12 Aug 2012 09:17:30 +0000") } }, "fields": [ ] } [] []
[2012-08-13 09:17:30] doctrine.INFO: MongoDB query: { "skip": true, "skipNum": null, "query": { "targetUser": ObjectId("4fa377e06803fa7303000002"), "code": { "$in": [ "login.attempt", "login.ok" ] }, "ts": { "$gte": new Date("Sun, 12 Aug 2012 09:17:30 +0000") } }, "fields": [ ] } [] []
[2012-08-13 09:17:30] doctrine.INFO: MongoDB query: { "sort": true, "sortFields": { "ts": 1 }, "query": { "targetUser": ObjectId("4fa377e06803fa7303000002"), "code": { "$in": [ "login.attempt", "login.ok" ] }, "ts": { "$gte": new Date("Sun, 12 Aug 2012 09:17:30 +0000") } }, "fields": [ ] } [] []
My 'user_activity_tracker' service works just as a proxy to the underlying Doctrine repository / document manager. Both snippets get a LoggableCursor back after query.
The extra log output with the query builder method is due to Query::prepareCursor(), which always sets additional cursor options. The repository findBy() method, which utilizes DocumentPersister::loadAll(), only sets options if a non-null value is provided. That explains the difference in log output, but is unrelated to a difference of result sets.
The logged query for each example are identical, apart from a small drift in the ts criteria. If the count() values of both cursors differ, and the results are different after unwrapping the cursor with iterator_to_array(), I would suggest attempting to reproduce this in a failing test case and submit a pull request against the mongodb-odm repository.